diff options
author | Justus Winter <4winter@informatik.uni-hamburg.de> | 2014-05-29 02:04:24 +0200 |
---|---|---|
committer | Justus Winter <4winter@informatik.uni-hamburg.de> | 2014-05-29 02:04:24 +0200 |
commit | e3e9a8509830690c1c7b68d8d62454d6408e1fcf (patch) | |
tree | af524411b48593afc2e5b01e852d78d591cd6460 /debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch | |
parent | 51a3b0c9ab8515774faedaf8d34b04472d3989c8 (diff) |
add patch series
Diffstat (limited to 'debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch')
-rw-r--r-- | debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch b/debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch new file mode 100644 index 00000000..45988e5b --- /dev/null +++ b/debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch @@ -0,0 +1,402 @@ +From 9976531d49ce20fed01ffb25fe4d8d1c347f1c10 Mon Sep 17 00:00:00 2001 +From: Justus Winter <4winter@informatik.uni-hamburg.de> +Date: Thu, 29 May 2014 02:03:03 +0200 +Subject: [PATCH 14/14] libdiskfs: use a hash table for the name cache + +* libdiskfs/name-cache.c: XXX. +--- + libdiskfs/name-cache.c | 292 +++++++++++++++++++++++++++++++++---------------- + 1 file changed, 199 insertions(+), 93 deletions(-) + +diff --git a/libdiskfs/name-cache.c b/libdiskfs/name-cache.c +index 25b5d0d..0e64e69 100644 +--- a/libdiskfs/name-cache.c ++++ b/libdiskfs/name-cache.c +@@ -1,6 +1,6 @@ + /* Directory name lookup caching + +- Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. ++ Copyright (C) 1996, 1997, 1998, 2014 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG, & Miles Bader. + + This file is part of the GNU Hurd. +@@ -21,90 +21,201 @@ + + #include "priv.h" + #include <string.h> +-#include <cacheq.h> + +-/* Maximum number of names to cache at once */ +-#define MAXCACHE 200 ++/* The name-cache is implemented using a hash table. + +-/* Maximum length of file name we bother caching */ +-#define CACHE_NAME_LEN 100 ++ Some design choices: + +-/* Cache entry */ +-struct lookup_cache ++ XXX ++ */ ++ ++/* Maximum number of names to cache. Must be a power of two. */ ++#define CACHE_SIZE 512 ++#define CACHE_MASK (CACHE_SIZE - 1) ++ ++#define BUCKET_SIZE 2 ++ ++/* Cache bucket with BUCKET_SIZE entries. */ ++struct cache_bucket + { +- struct cacheq_hdr hdr; ++ /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. If ++ NULL, the entry is unused. */ ++ char *name[BUCKET_SIZE]; + +- /* Used to indentify nodes to the fs dependent code. 0 for NODE_CACHE_ID +- means a `negative' entry -- recording that there's definitely no node with +- this name. */ +- ino64_t dir_cache_id, node_cache_id; ++ /* The key. */ ++ unsigned long key[BUCKET_SIZE]; + +- /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. Entries +- with names too long to fit in this buffer aren't cached at all. */ +- char name[CACHE_NAME_LEN]; ++ /* Used to indentify nodes to the fs dependent code. */ ++ ino64_t dir_cache_id[BUCKET_SIZE]; + +- /* Strlen of NAME. If this is zero, it's an unused entry. */ +- size_t name_len; ++ /* 0 for NODE_CACHE_ID means a `negative' entry -- recording that ++ there's definitely no node with this name. */ ++ ino64_t node_cache_id[BUCKET_SIZE]; + }; + +-/* The contents of the cache in no particular order */ +-static struct cacheq lookup_cache = { sizeof (struct lookup_cache) }; ++static struct cache_bucket name_cache[CACHE_SIZE]; + +-static pthread_spinlock_t cache_lock = PTHREAD_SPINLOCK_INITIALIZER; ++static pthread_rwlock_t cache_lock = PTHREAD_RWLOCK_INITIALIZER; + +-/* If there's an entry for NAME, of length NAME_LEN, in directory DIR in the +- cache, return its entry, otherwise 0. CACHE_LOCK must be held. */ +-static struct lookup_cache * +-find_cache (struct node *dir, const char *name, size_t name_len) ++static inline void ++add_entry (struct cache_bucket *b, int i, ++ const char *name, unsigned long key, ++ ino64_t dir_cache_id, ino64_t node_cache_id) + { +- struct lookup_cache *c; +- int i; ++ if (b->name[i]) ++ free (b->name[i]); + +- /* Search the list. All unused entries are contiguous at the end of the +- list, so we can stop searching when we see the first one. */ +- for (i = 0, c = lookup_cache.mru; +- c && c->name_len; +- c = c->hdr.next, i++) +- if (c->name_len == name_len +- && c->dir_cache_id == dir->cache_id +- && c->name[0] == name[0] && strcmp (c->name, name) == 0) +- return c; ++ b->name[i] = strdup (name); ++ if (b->name[i] == NULL) ++ return; + +- return 0; ++ b->key[i] = key; ++ b->dir_cache_id[i] = dir_cache_id; ++ b->node_cache_id[i] = node_cache_id; ++} ++ ++static inline void ++remove_entry (struct cache_bucket *b, int i) ++{ ++ if (b->name[i]) ++ free (b->name[i]); ++ b->name[i] = NULL; ++} ++ ++static inline int ++valid_entry (struct cache_bucket *b, int i) ++{ ++ return b->name[i] != NULL; + } + +-/* Node NP has just been found in DIR with NAME. If NP is null, that +- means that this name has been confirmed as absent in the directory. */ +-void +-diskfs_enter_lookup_cache (struct node *dir, struct node *np, const char *name) ++/* This is the Murmur3 hash algorithm. */ ++ ++#define FORCE_INLINE inline __attribute__((always_inline)) ++ ++inline uint32_t rotl32 ( uint32_t x, int8_t r ) + { +- struct lookup_cache *c; +- size_t name_len = strlen (name); ++ return (x << r) | (x >> (32 - r)); ++} + +- if (name_len > CACHE_NAME_LEN - 1) +- return; ++#define ROTL32(x,y) rotl32(x,y) ++ ++/* Block read - if your platform needs to do endian-swapping or can ++ only handle aligned reads, do the conversion here. */ ++ ++FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) ++{ ++ return p[i]; ++} ++ ++/* Finalization mix - force all bits of a hash block to avalanche. */ ++ ++FORCE_INLINE uint32_t fmix32 ( uint32_t h ) ++{ ++ h ^= h >> 16; ++ h *= 0x85ebca6b; ++ h ^= h >> 13; ++ h *= 0xc2b2ae35; ++ h ^= h >> 16; ++ ++ return h; ++} ++ ++/* The Murmur3 hash function. */ ++void MurmurHash3_x86_32 ( const void * key, int len, ++ uint32_t seed, void * out ) ++{ ++ const uint8_t * data = (const uint8_t*)key; ++ const int nblocks = len / 4; ++ ++ uint32_t h1 = seed; ++ ++ const uint32_t c1 = 0xcc9e2d51; ++ const uint32_t c2 = 0x1b873593; ++ ++ /* body */ + +- pthread_spin_lock (&cache_lock); ++ const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + +- if (lookup_cache.length == 0) +- /* There should always be an lru_cache; this being zero means that the +- cache hasn't been initialized yet. Do so. */ +- cacheq_set_length (&lookup_cache, MAXCACHE); ++ for(int i = -nblocks; i; i++) ++ { ++ uint32_t k1 = getblock32(blocks,i); + +- /* See if there's an old entry for NAME in DIR. If not, replace the least +- recently used entry. */ +- c = find_cache (dir, name, name_len) ?: lookup_cache.lru; ++ k1 *= c1; ++ k1 = ROTL32(k1,15); ++ k1 *= c2; + +- /* Fill C with the new entry. */ +- c->dir_cache_id = dir->cache_id; +- c->node_cache_id = np ? np->cache_id : 0; +- strcpy (c->name, name); +- c->name_len = name_len; ++ h1 ^= k1; ++ h1 = ROTL32(h1,13); ++ h1 = h1*5+0xe6546b64; ++ } + +- /* Now C becomes the MRU entry! */ +- cacheq_make_mru (&lookup_cache, c); ++ /* tail */ + +- pthread_spin_unlock (&cache_lock); ++ const uint8_t * tail = (const uint8_t*)(data + nblocks*4); ++ ++ uint32_t k1 = 0; ++ ++ switch(len & 3) ++ { ++ case 3: k1 ^= tail[2] << 16; ++ case 2: k1 ^= tail[1] << 8; ++ case 1: k1 ^= tail[0]; ++ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; ++ }; ++ ++ /* finalization */ ++ ++ h1 ^= len; ++ ++ h1 = fmix32(h1); ++ ++ *(uint32_t*)out = h1; ++} ++ ++static inline int ++lookup (ino64_t dir_cache_id, const char *name, unsigned long key, ++ struct cache_bucket **bucket, int *index) ++{ ++ int i; ++ struct cache_bucket *b = *bucket = &name_cache[key & CACHE_MASK]; ++ ++ for (i = 0; i < BUCKET_SIZE; i++) ++ if (valid_entry (b, i) ++ && b->key[i] == key ++ && b->dir_cache_id[i] == dir_cache_id ++ && strcmp (b->name[i], name) == 0) ++ { ++ *index = i; ++ return 1; ++ } ++ ++ *index = 0; ++ return 0; ++} ++ ++/* Hash the directory cache_id and the name. */ ++static inline unsigned long ++compute_key (ino64_t dir_cache_id, const char *name) ++{ ++ unsigned long hash; ++ MurmurHash3_x86_32 (&dir_cache_id, sizeof dir_cache_id, 0, &hash); ++ MurmurHash3_x86_32 (name, strlen (name), hash, &hash); ++ return hash; ++} ++ ++/* Node NP has just been found in DIR with NAME. If NP is null, that ++ means that this name has been confirmed as absent in the directory. */ ++void ++diskfs_enter_lookup_cache (struct node *dir, struct node *np, const char *name) ++{ ++ unsigned long key = compute_key (dir->cache_id, name); ++ struct cache_bucket *bucket; ++ int i; ++ ++ pthread_rwlock_wrlock (&cache_lock); ++ lookup (dir->cache_id, name, key, &bucket, &i); ++ add_entry (bucket, i, name, key, dir->cache_id, np ? np->cache_id : 0); ++ pthread_rwlock_unlock (&cache_lock); + } + + /* Purge all references in the cache to NP as a node inside +@@ -112,27 +223,21 @@ diskfs_enter_lookup_cache (struct node *dir, struct node *np, const char *name) + void + diskfs_purge_lookup_cache (struct node *dp, struct node *np) + { +- struct lookup_cache *c, *next; ++ int i; ++ struct cache_bucket *b; + +- pthread_spin_lock (&cache_lock); +- for (c = lookup_cache.mru; c; c = next) +- { +- /* Save C->hdr.next, since we may move C from this position. */ +- next = c->hdr.next; ++ pthread_rwlock_wrlock (&cache_lock); + +- if (c->name_len +- && c->dir_cache_id == dp->cache_id +- && c->node_cache_id == np->cache_id) +- { +- c->name_len = 0; +- cacheq_make_lru (&lookup_cache, c); /* Use C as the next free +- entry. */ +- } +- } +- pthread_spin_unlock (&cache_lock); ++ for (b = &name_cache[0]; b < &name_cache[CACHE_SIZE]; b++) ++ for (i = 0; i < BUCKET_SIZE; i++) ++ if (valid_entry (b, i) ++ && b->dir_cache_id[i] == dp->cache_id ++ && b->node_cache_id[i] == np->cache_id) ++ remove_entry (b, i); ++ ++ pthread_rwlock_unlock (&cache_lock); + } + +- + /* Scan the cache looking for NAME inside DIR. If we don't know + anything entry at all, then return 0. If the entry is confirmed to + not exist, then return -1. Otherwise, return NP for the entry, with +@@ -140,27 +245,25 @@ diskfs_purge_lookup_cache (struct node *dp, struct node *np) + struct node * + diskfs_check_lookup_cache (struct node *dir, const char *name) + { +- struct lookup_cache *c; ++ unsigned long key = compute_key (dir->cache_id, name); ++ struct cache_bucket *bucket; ++ int i, found; + +- pthread_spin_lock (&cache_lock); +- +- c = find_cache (dir, name, strlen (name)); +- if (c) ++ pthread_rwlock_rdlock (&cache_lock); ++ found = lookup (dir->cache_id, name, key, &bucket, &i); ++ if (found) + { +- int id = c->node_cache_id; +- +- cacheq_make_mru (&lookup_cache, c); /* Record C as recently used. */ ++ ino64_t id = bucket->node_cache_id[i]; ++ pthread_rwlock_unlock (&cache_lock); + + if (id == 0) + /* A negative cache entry. */ + { +- pthread_spin_unlock (&cache_lock); + return (struct node *)-1; + } + else if (id == dir->cache_id) + /* The cached node is the same as DIR. */ + { +- pthread_spin_unlock (&cache_lock); + diskfs_nref (dir); + return dir; + } +@@ -170,8 +273,6 @@ diskfs_check_lookup_cache (struct node *dir, const char *name) + struct node *np; + error_t err; + +- pthread_spin_unlock (&cache_lock); +- + if (name[0] == '.' && name[1] == '.' && name[2] == '\0') + { + pthread_mutex_unlock (&dir->lock); +@@ -181,14 +282,19 @@ diskfs_check_lookup_cache (struct node *dir, const char *name) + /* In the window where DP was unlocked, we might + have lost. So check the cache again, and see + if it's still there; if so, then we win. */ +- c = find_cache (dir, "..", 2); +- if (!c || c->node_cache_id != id) ++ pthread_rwlock_rdlock (&cache_lock); ++ found = lookup (dir->cache_id, "..", key, &bucket, &i); ++ if (! found ++ || ! bucket->node_cache_id[i] != id) + { ++ pthread_rwlock_unlock (&cache_lock); ++ + /* Lose */ + pthread_mutex_unlock (&np->lock); + diskfs_nrele (np); + return 0; + } ++ pthread_rwlock_unlock (&cache_lock); + } + else + err = diskfs_cached_lookup (id, &np); +@@ -196,7 +302,7 @@ diskfs_check_lookup_cache (struct node *dir, const char *name) + } + } + +- pthread_spin_unlock (&cache_lock); ++ pthread_rwlock_unlock (&cache_lock); + + return 0; + } +-- +2.0.0.rc2 + |