summaryrefslogtreecommitdiff
path: root/debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch
diff options
context:
space:
mode:
authorJustus Winter <4winter@informatik.uni-hamburg.de>2014-05-29 02:04:24 +0200
committerJustus Winter <4winter@informatik.uni-hamburg.de>2014-05-29 02:04:24 +0200
commite3e9a8509830690c1c7b68d8d62454d6408e1fcf (patch)
treeaf524411b48593afc2e5b01e852d78d591cd6460 /debian/patches/0014-libdiskfs-use-a-hash-table-for-the-name-cache.patch
parent51a3b0c9ab8515774faedaf8d34b04472d3989c8 (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.patch402
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
+