diff options
author | Nick Piggin <npiggin@suse.de> | 2010-01-29 15:38:24 -0800 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2010-04-27 17:32:37 +0200 |
commit | ebfa7fa2c03891bd25a07fefc7218c273071a659 (patch) | |
tree | cb470c3622fc94ec26d2ab273e855d2c096bb550 | |
parent | 68d0cacc5da18248a173c2fd83f8870f6a242729 (diff) | |
download | lwn-ebfa7fa2c03891bd25a07fefc7218c273071a659.tar.gz lwn-ebfa7fa2c03891bd25a07fefc7218c273071a659.zip |
dcache-chain-hashlock
We can turn the dcache hash locking from a global dcache_hash_lock into
per-bucket locking.
XXX: should probably use a bit lock in the first bit of the hash pointers
to avoid any space bloating (non-atomic unlock means no extra atomics either)
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | fs/dcache.c | 207 | ||||
-rw-r--r-- | include/linux/dcache.h | 20 |
2 files changed, 121 insertions, 106 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 2ad61b0d01c5..d91e0da25209 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -39,7 +39,7 @@ * Usage: * dcache_inode_lock protects: * - the inode alias lists, d_inode - * dcache_hash_lock protects: + * dcache_hash_bucket->lock protects: * - the dcache hash table * dcache_lru_lock protects: * - the dcache lru lists and counters @@ -54,19 +54,17 @@ * dcache_inode_lock * dentry->d_lock * dcache_lru_lock - * dcache_hash_lock + * dcache_hash_bucket->lock */ int sysctl_vfs_cache_pressure __read_mostly = 100; EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_inode_lock); -__cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_hash_lock); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lru_lock); __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock); EXPORT_SYMBOL(rename_lock); EXPORT_SYMBOL(dcache_inode_lock); -EXPORT_SYMBOL(dcache_hash_lock); static struct kmem_cache *dentry_cache __read_mostly; @@ -85,7 +83,12 @@ static struct kmem_cache *dentry_cache __read_mostly; static unsigned int d_hash_mask __read_mostly; static unsigned int d_hash_shift __read_mostly; -static struct hlist_head *dentry_hashtable __read_mostly; + +struct dcache_hash_bucket { + spinlock_t lock; + struct hlist_head head; +}; +static struct dcache_hash_bucket *dentry_hashtable __read_mostly; /* Statistics gathering. */ struct dentry_stat_t dentry_stat = { @@ -93,6 +96,14 @@ struct dentry_stat_t dentry_stat = { .age_limit = 45, }; +static inline struct dcache_hash_bucket *d_hash(struct dentry *parent, + unsigned long hash) +{ + hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES; + hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS); + return dentry_hashtable + (hash & D_HASHMASK); +} + static void __d_free(struct dentry *dentry) { WARN_ON(!list_empty(&dentry->d_alias)); @@ -233,6 +244,79 @@ static struct dentry *d_kill(struct dentry *dentry) return parent; } +void __d_drop(struct dentry *dentry) +{ + if (!(dentry->d_flags & DCACHE_UNHASHED)) { + struct dcache_hash_bucket *b; + b = d_hash(dentry->d_parent, dentry->d_name.hash); + dentry->d_flags |= DCACHE_UNHASHED; + spin_lock(&b->lock); + hlist_del_rcu(&dentry->d_hash); + spin_unlock(&b->lock); + } +} +EXPORT_SYMBOL(__d_drop); + +void d_drop(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); +} +EXPORT_SYMBOL(d_drop); + +/* This should be called _only_ with a lock pinning the dentry */ +static inline struct dentry * __dget_locked_dlock(struct dentry *dentry) +{ + dentry->d_count++; + dentry_lru_del_init(dentry); + return dentry; +} + +static inline struct dentry * __dget_locked(struct dentry *dentry) +{ + spin_lock(&dentry->d_lock); + __dget_locked_dlock(dentry); + spin_unlock(&dentry->d_lock); + return dentry; +} + +struct dentry * dget_locked_dlock(struct dentry *dentry) +{ + return __dget_locked_dlock(dentry); +} + +struct dentry * dget_locked(struct dentry *dentry) +{ + return __dget_locked(dentry); +} + +struct dentry *dget_parent(struct dentry *dentry) +{ + struct dentry *ret; + +repeat: + spin_lock(&dentry->d_lock); + ret = dentry->d_parent; + if (!ret) + goto out; + if (dentry == ret) { + ret->d_count++; + goto out; + } + if (!spin_trylock(&ret->d_lock)) { + spin_unlock(&dentry->d_lock); + goto repeat; + } + BUG_ON(!ret->d_count); + ret->d_count++; + spin_unlock(&ret->d_lock); +out: + spin_unlock(&dentry->d_lock); + return ret; +} +EXPORT_SYMBOL(dget_parent); + /* * This is dput * @@ -382,58 +466,6 @@ int d_invalidate(struct dentry * dentry) return 0; } -/* This should be called _only_ with a lock pinning the dentry */ -static inline struct dentry * __dget_locked_dlock(struct dentry *dentry) -{ - dentry->d_count++; - dentry_lru_del_init(dentry); - return dentry; -} - -static inline struct dentry * __dget_locked(struct dentry *dentry) -{ - spin_lock(&dentry->d_lock); - __dget_locked_dlock(dentry); - spin_unlock(&dentry->d_lock); - return dentry; -} - -struct dentry * dget_locked_dlock(struct dentry *dentry) -{ - return __dget_locked_dlock(dentry); -} - -struct dentry * dget_locked(struct dentry *dentry) -{ - return __dget_locked(dentry); -} - -struct dentry *dget_parent(struct dentry *dentry) -{ - struct dentry *ret; - -repeat: - spin_lock(&dentry->d_lock); - ret = dentry->d_parent; - if (!ret) - goto out; - if (dentry == ret) { - ret->d_count++; - goto out; - } - if (!spin_trylock(&ret->d_lock)) { - spin_unlock(&dentry->d_lock); - goto repeat; - } - BUG_ON(!ret->d_count); - ret->d_count++; - spin_unlock(&ret->d_lock); -out: - spin_unlock(&dentry->d_lock); - return ret; -} -EXPORT_SYMBOL(dget_parent); - /** * d_find_alias - grab a hashed alias of inode * @inode: inode in question @@ -1322,14 +1354,6 @@ struct dentry * d_alloc_root(struct inode * root_inode) return res; } -static inline struct hlist_head *d_hash(struct dentry *parent, - unsigned long hash) -{ - hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES; - hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS); - return dentry_hashtable + (hash & D_HASHMASK); -} - /** * d_obtain_alias - find or allocate a dentry for a given inode * @inode: inode to allocate the dentry for @@ -1576,7 +1600,8 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name) unsigned int len = name->len; unsigned int hash = name->hash; const unsigned char *str = name->name; - struct hlist_head *head = d_hash(parent,hash); + struct dcache_hash_bucket *b = d_hash(parent, hash); + struct hlist_head *head = &b->head; struct dentry *found = NULL; struct hlist_node *node; struct dentry *dentry; @@ -1670,6 +1695,7 @@ out: int d_validate(struct dentry *dentry, struct dentry *dparent) { + struct dcache_hash_bucket *b; struct hlist_head *base; struct hlist_node *lhp; @@ -1681,20 +1707,21 @@ int d_validate(struct dentry *dentry, struct dentry *dparent) goto out; spin_lock(&dentry->d_lock); - spin_lock(&dcache_hash_lock); - base = d_hash(dparent, dentry->d_name.hash); - hlist_for_each(lhp,base) { + b = d_hash(dparent, dentry->d_name.hash); + base = &b->head; + spin_lock(&b->lock); + hlist_for_each(lhp, base) { /* hlist_for_each_entry_rcu() not required for d_hash list - * as it is parsed under dcache_hash_lock + * as it is parsed under dcache_hash_bucket->lock */ if (dentry == hlist_entry(lhp, struct dentry, d_hash)) { - spin_unlock(&dcache_hash_lock); + spin_unlock(&b->lock); __dget_locked_dlock(dentry); spin_unlock(&dentry->d_lock); return 1; } } - spin_unlock(&dcache_hash_lock); + spin_unlock(&b->lock); spin_unlock(&dentry->d_lock); out: return 0; @@ -1745,11 +1772,12 @@ void d_delete(struct dentry * dentry) fsnotify_nameremove(dentry, isdir); } -static void __d_rehash(struct dentry * entry, struct hlist_head *list) +static void __d_rehash(struct dentry * entry, struct dcache_hash_bucket *b) { - entry->d_flags &= ~DCACHE_UNHASHED; - hlist_add_head_rcu(&entry->d_hash, list); + spin_lock(&b->lock); + hlist_add_head_rcu(&entry->d_hash, &b->head); + spin_unlock(&b->lock); } static void _d_rehash(struct dentry * entry) @@ -1767,9 +1795,7 @@ static void _d_rehash(struct dentry * entry) void d_rehash(struct dentry * entry) { spin_lock(&entry->d_lock); - spin_lock(&dcache_hash_lock); _d_rehash(entry); - spin_unlock(&dcache_hash_lock); spin_unlock(&entry->d_lock); } @@ -1847,6 +1873,7 @@ static void switch_names(struct dentry *dentry, struct dentry *target) */ static void d_move_locked(struct dentry * dentry, struct dentry * target) { + struct dcache_hash_bucket *b; if (!dentry->d_inode) printk(KERN_WARNING "VFS: moving negative dcache entry\n"); @@ -1879,11 +1906,13 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target) } /* Move the dentry to the target hash queue, if on different bucket */ - spin_lock(&dcache_hash_lock); - if (!d_unhashed(dentry)) + if (!d_unhashed(dentry)) { + b = d_hash(dentry->d_parent, dentry->d_name.hash); + spin_lock(&b->lock); hlist_del_rcu(&dentry->d_hash); + spin_unlock(&b->lock); + } __d_rehash(dentry, d_hash(target->d_parent, target->d_name.hash)); - spin_unlock(&dcache_hash_lock); /* Unhash the target: dput() will then get rid of it */ __d_drop(target); @@ -2091,9 +2120,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) found_lock: spin_lock(&actual->d_lock); found: - spin_lock(&dcache_hash_lock); _d_rehash(actual); - spin_unlock(&dcache_hash_lock); spin_unlock(&actual->d_lock); spin_unlock(&dcache_inode_lock); out_nolock: @@ -2567,7 +2594,7 @@ static void __init dcache_init_early(void) dentry_hashtable = alloc_large_system_hash("Dentry cache", - sizeof(struct hlist_head), + sizeof(struct dcache_hash_bucket), dhash_entries, 13, HASH_EARLY, @@ -2575,8 +2602,10 @@ static void __init dcache_init_early(void) &d_hash_mask, 0); - for (loop = 0; loop < (1 << d_hash_shift); loop++) - INIT_HLIST_HEAD(&dentry_hashtable[loop]); + for (loop = 0; loop < (1 << d_hash_shift); loop++) { + spin_lock_init(&dentry_hashtable[loop].lock); + INIT_HLIST_HEAD(&dentry_hashtable[loop].head); + } } static void __init dcache_init(void) @@ -2599,7 +2628,7 @@ static void __init dcache_init(void) dentry_hashtable = alloc_large_system_hash("Dentry cache", - sizeof(struct hlist_head), + sizeof(struct dcache_hash_bucket), dhash_entries, 13, 0, @@ -2607,8 +2636,10 @@ static void __init dcache_init(void) &d_hash_mask, 0); - for (loop = 0; loop < (1 << d_hash_shift); loop++) - INIT_HLIST_HEAD(&dentry_hashtable[loop]); + for (loop = 0; loop < (1 << d_hash_shift); loop++) { + spin_lock_init(&dentry_hashtable[loop].lock); + INIT_HLIST_HEAD(&dentry_hashtable[loop].head); + } } /* SLAB cache for __getname() consumers */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 14e4728dd31b..e3bec299113a 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -187,7 +187,6 @@ d_iput: no no yes #define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */ extern spinlock_t dcache_inode_lock; -extern spinlock_t dcache_hash_lock; extern seqlock_t rename_lock; /** @@ -205,23 +204,8 @@ extern seqlock_t rename_lock; * * __d_drop requires dentry->d_lock. */ - -static inline void __d_drop(struct dentry *dentry) -{ - if (!(dentry->d_flags & DCACHE_UNHASHED)) { - dentry->d_flags |= DCACHE_UNHASHED; - spin_lock(&dcache_hash_lock); - hlist_del_rcu(&dentry->d_hash); - spin_unlock(&dcache_hash_lock); - } -} - -static inline void d_drop(struct dentry *dentry) -{ - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); -} +void d_drop(struct dentry *dentry); +void __d_drop(struct dentry *dentry); static inline int dname_external(struct dentry *dentry) { |