--- linux-chopstix/include/linux/dcookies.h.orig 2009-03-05 08:45:29.000000000 -0500 +++ linux-chopstix/include/linux/dcookies.h 2009-03-12 13:43:53.000000000 -0400 @@ -45,6 +45,27 @@ int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, unsigned long * cookie); + +/** + * dcookie_swap - switch to the next dcookie epoch + * + * Deactivate the current dcookie hash table and activate + * the next one + * + * Returns 0 on success + */ + +int dcookie_swap(void); + +/** + * dcookie_garbage_collect - clear the hash table next in line + * + * Clear the hash table to be activated in the next epoch. + * + * Returns 0 on success + */ + +int dcookie_garbage_colect(void); #else static inline struct dcookie_user * dcookie_register(void) --- linux-chopstix/fs/dcookies.c.orig 2009-03-05 08:46:09.000000000 -0500 +++ linux-chopstix/fs/dcookies.c 2009-04-06 11:37:35.000000000 -0400 @@ -23,6 +23,7 @@ #include #include #include +#include #include /* The dcookies are allocated from a kmem_cache and @@ -37,15 +38,21 @@ static LIST_HEAD(dcookie_users); static DEFINE_MUTEX(dcookie_mutex); +spinlock_t dcookie_hash_write_lock = SPIN_LOCK_UNLOCKED; static struct kmem_cache *dcookie_cache __read_mostly; -static struct list_head *dcookie_hashtable __read_mostly; +static struct list_head *dcookie_hashtable[3] __read_mostly; static size_t hash_size __read_mostly; +unsigned int current_hash = 1, old_hash = 0; static inline int is_live(void) { return !(list_empty(&dcookie_users)); } +static inline int is_shared(void) +{ + return !(list_empty(&dcookie_users)) && !(list_empty(dcookie_users.next)); +} /* The dentry is locked, its address will do for the cookie */ static inline unsigned long dcookie_value(struct dcookie_struct * dcs) @@ -67,7 +74,7 @@ struct list_head * pos; struct list_head * list; - list = dcookie_hashtable + dcookie_hash(dcookie); + list = dcookie_hashtable[current_hash] + dcookie_hash(dcookie); list_for_each(pos, list) { dcs = list_entry(pos, struct dcookie_struct, hash_list); @@ -77,13 +84,26 @@ } } + if (!found) { + list = dcookie_hashtable[old_hash] + dcookie_hash(dcookie); + + list_for_each(pos, list) { + dcs = list_entry(pos, struct dcookie_struct, hash_list); + if (dcookie_value(dcs) == dcookie) { + found = dcs; + break; + } + } + + } + return found; } static void hash_dcookie(struct dcookie_struct * dcs) { - struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs)); + struct list_head * list = dcookie_hashtable[current_hash] + dcookie_hash(dcookie_value(dcs)); list_add(&dcs->hash_list, list); } @@ -91,7 +111,7 @@ static struct dcookie_struct * alloc_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt) { - struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_KERNEL); + struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_ATOMIC); if (!dcs) return NULL; @@ -114,7 +134,7 @@ int err = 0; struct dcookie_struct * dcs; - mutex_lock(&dcookie_mutex); + spin_lock(&dcookie_hash_write_lock); if (!is_live()) { err = -EINVAL; @@ -134,7 +154,7 @@ *cookie = dcookie_value(dcs); out: - mutex_unlock(&dcookie_mutex); + spin_unlock(&dcookie_hash_write_lock); return err; } @@ -210,9 +230,18 @@ if (!dcookie_cache) goto out; - dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!dcookie_hashtable) + dcookie_hashtable[0] = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!dcookie_hashtable[0]) goto out_kmem; + + dcookie_hashtable[1] = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!dcookie_hashtable[1]) + goto out_kmem; + + dcookie_hashtable[2] = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!dcookie_hashtable[2]) + goto out_kmem; + err = 0; @@ -235,7 +264,24 @@ hash_size = 1UL << hash_bits; /* And initialize the newly allocated array */ - d = dcookie_hashtable; + + d = dcookie_hashtable[0]; + i = hash_size; + do { + INIT_LIST_HEAD(d); + d++; + i--; + } while (i); + + d = dcookie_hashtable[1]; + i = hash_size; + do { + INIT_LIST_HEAD(d); + d++; + i--; + } while (i); + + d = dcookie_hashtable[2]; i = hash_size; do { INIT_LIST_HEAD(d); @@ -259,6 +305,39 @@ kmem_cache_free(dcookie_cache, dcs); } +int dcookie_swap(void) { + if (is_shared()) + return -EAGAIN; + + old_hash=current_hash; + current_hash = (current_hash + 1) % 3; + return 0; +} + +/* Switch to the second hash */ +int dcookie_garbage_collect(void) { + struct list_head * list; + struct list_head * pos; + struct list_head * pos2; + struct dcookie_struct * dcs; + size_t i; + int next_hash=(current_hash + 1) % 3; + + if (is_shared()) + return -EAGAIN; + + /* XXX consider the consequence of dcookie allocation concurring with this cleanup */ + for (i = 0; i < hash_size; ++i) { + list = dcookie_hashtable[next_hash] + i; + list_for_each_safe(pos, pos2, list) { + dcs = list_entry(pos, struct dcookie_struct, hash_list); + list_del(&dcs->hash_list); + free_dcookie(dcs); + } + } + + return 0; +} static void dcookie_exit(void) { @@ -269,7 +348,21 @@ size_t i; for (i = 0; i < hash_size; ++i) { - list = dcookie_hashtable + i; + list = dcookie_hashtable[0] + i; + list_for_each_safe(pos, pos2, list) { + dcs = list_entry(pos, struct dcookie_struct, hash_list); + list_del(&dcs->hash_list); + free_dcookie(dcs); + } + + list = dcookie_hashtable[1] + i; + list_for_each_safe(pos, pos2, list) { + dcs = list_entry(pos, struct dcookie_struct, hash_list); + list_del(&dcs->hash_list); + free_dcookie(dcs); + } + + list = dcookie_hashtable[2] + i; list_for_each_safe(pos, pos2, list) { dcs = list_entry(pos, struct dcookie_struct, hash_list); list_del(&dcs->hash_list); @@ -277,7 +370,9 @@ } } - kfree(dcookie_hashtable); + kfree(dcookie_hashtable[0]); + kfree(dcookie_hashtable[1]); + kfree(dcookie_hashtable[2]); kmem_cache_destroy(dcookie_cache); } @@ -327,3 +422,5 @@ EXPORT_SYMBOL_GPL(dcookie_register); EXPORT_SYMBOL_GPL(dcookie_unregister); EXPORT_SYMBOL_GPL(get_dcookie); +EXPORT_SYMBOL_GPL(dcookie_garbage_collect); +EXPORT_SYMBOL_GPL(dcookie_swap);