diff -Nurb linux-2.6.27-580/fs/dcookies.c linux-2.6.27-590/fs/dcookies.c --- linux-2.6.27-580/fs/dcookies.c 2009-07-23 10:51:40.000000000 -0400 +++ linux-2.6.27-590/fs/dcookies.c 2009-11-30 17:26:58.000000000 -0500 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -38,15 +39,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) @@ -68,7 +75,18 @@ 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); + if (dcookie_value(dcs) == dcookie) { + found = dcs; + break; + } + } + + if (!found) { + list = dcookie_hashtable[old_hash] + dcookie_hash(dcookie); list_for_each(pos, list) { dcs = list_entry(pos, struct dcookie_struct, hash_list); @@ -77,6 +95,8 @@ break; } } + } + return found; } @@ -84,15 +104,14 @@ 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); } static struct dcookie_struct *alloc_dcookie(struct path *path) { - 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; @@ -112,7 +131,7 @@ int err = 0; struct dcookie_struct * dcs; - mutex_lock(&dcookie_mutex); + spin_lock(&dcookie_hash_write_lock); if (!is_live()) { err = -EINVAL; @@ -132,7 +151,7 @@ *cookie = dcookie_value(dcs); out: - mutex_unlock(&dcookie_mutex); + spin_unlock(&dcookie_hash_write_lock); return err; } @@ -204,7 +223,7 @@ static int dcookie_init(void) { struct list_head * d; - unsigned int i, hash_bits; + unsigned int i, j, hash_bits; int err = -ENOMEM; dcookie_cache = kmem_cache_create("dcookie_cache", @@ -214,9 +233,11 @@ if (!dcookie_cache) goto out; - dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!dcookie_hashtable) + for (i=0; i<3; i++) { + dcookie_hashtable[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!dcookie_hashtable[i]) goto out_kmem; + } err = 0; @@ -239,13 +260,16 @@ hash_size = 1UL << hash_bits; /* And initialize the newly allocated array */ - d = dcookie_hashtable; - i = hash_size; + + for (i=0; i<3; i++) { + d = dcookie_hashtable[i]; + j = hash_size; do { INIT_LIST_HEAD(d); d++; - i--; - } while (i); + j--; + } while (j); + } out: return err; @@ -262,25 +286,61 @@ kmem_cache_free(dcookie_cache, dcs); } +int dcookie_swap(void) { + if (is_shared()) + return -EAGAIN; -static void dcookie_exit(void) -{ + 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 + 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) +{ + struct list_head * list; + struct list_head * pos; + struct list_head * pos2; + struct dcookie_struct * dcs; + size_t j; + unsigned int i; + + for (i = 0; i < 3; ++i) { + for (j = 0; j < hash_size; ++j) { + list = dcookie_hashtable[i] + j; list_for_each_safe(pos, pos2, list) { dcs = list_entry(pos, struct dcookie_struct, hash_list); list_del(&dcs->hash_list); free_dcookie(dcs); } } + kfree(dcookie_hashtable[i]); + } - kfree(dcookie_hashtable); kmem_cache_destroy(dcookie_cache); } @@ -330,3 +390,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); diff -Nurb linux-2.6.27-580/include/linux/dcookies.h linux-2.6.27-590/include/linux/dcookies.h --- linux-2.6.27-580/include/linux/dcookies.h 2008-10-09 18:13:53.000000000 -0400 +++ linux-2.6.27-590/include/linux/dcookies.h 2009-11-30 16:42:59.000000000 -0500 @@ -46,6 +46,27 @@ */ int get_dcookie(struct path *path, 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_collect(void); + #else static inline struct dcookie_user * dcookie_register(void)