The qstr consolidation wasn't quite right, because it can cause qstr->len to be unstable during lookup lockless traverasl. Fix that up by taking d_lock earlier in lookup. This serialises against d_move. Take the lock after comparing the parent and hash to preserve the mostly-lockless behaviour. This obsoletes d_movecount, which is removed. --- 25-akpm/fs/dcache.c | 44 +++++++++++++++++++---------------------- 25-akpm/include/linux/dcache.h | 15 ++++++------- 2 files changed, 28 insertions(+), 31 deletions(-) diff -puN fs/dcache.c~dentry-qstr-consolidation-fix fs/dcache.c --- 25/fs/dcache.c~dentry-qstr-consolidation-fix 2004-05-08 13:25:24.387498008 -0700 +++ 25-akpm/fs/dcache.c 2004-05-08 13:55:26.697505240 -0700 @@ -712,7 +712,6 @@ struct dentry *d_alloc(struct dentry * p dentry->d_flags = 0; dentry->d_inode = NULL; dentry->d_parent = NULL; - dentry->d_move_count = 0; dentry->d_sb = NULL; dentry->d_op = NULL; dentry->d_fsdata = NULL; @@ -928,8 +927,7 @@ struct dentry *d_splice_alias(struct ino * * __d_lookup is dcache_lock free. The hash list is protected using RCU. * Memory barriers are used while updating and doing lockless traversal. - * To avoid races with d_move while rename is happening, d_move_count is - * used. + * To avoid races with d_move while rename is happening, d_lock is used. * * Overflows in memcmp(), while d_move, are avoided by keeping the length * and name pointer in one structure pointed by d_qstr. @@ -972,8 +970,7 @@ struct dentry * __d_lookup(struct dentry hlist_for_each (node, head) { struct dentry *dentry; - unsigned long move_count; - struct qstr * qstr; + struct qstr *qstr; smp_read_barrier_depends(); dentry = hlist_entry(node, struct dentry, d_hash); @@ -984,11 +981,6 @@ struct dentry * __d_lookup(struct dentry if (unlikely(dentry->d_bucket != head)) break; - /* - * We must take a snapshot of d_move_count followed by - * read memory barrier before any search key comparison - */ - move_count = dentry->d_move_count; smp_rmb(); if (dentry->d_name.hash != hash) @@ -996,29 +988,36 @@ struct dentry * __d_lookup(struct dentry if (dentry->d_parent != parent) continue; + spin_lock(&dentry->d_lock); + + /* + * Recheck the dentry after taking the lock - d_move may have + * changed things. Don't bother checking the hash because we're + * about to compare the whole name anyway. + */ + if (dentry->d_parent != parent) + goto next; + qstr = &dentry->d_name; smp_read_barrier_depends(); if (parent->d_op && parent->d_op->d_compare) { if (parent->d_op->d_compare(parent, qstr, name)) - continue; + goto next; } else { if (qstr->len != len) - continue; + goto next; if (memcmp(qstr->name, str, len)) - continue; + goto next; } - spin_lock(&dentry->d_lock); - /* - * If dentry is moved, fail the lookup - */ - if (likely(move_count == dentry->d_move_count)) { - if (!d_unhashed(dentry)) { - atomic_inc(&dentry->d_count); - found = dentry; - } + + if (!d_unhashed(dentry)) { + atomic_inc(&dentry->d_count); + found = dentry; } spin_unlock(&dentry->d_lock); break; +next: + spin_unlock(&dentry->d_lock); } rcu_read_unlock(); @@ -1251,7 +1250,6 @@ already_unhashed: } list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); - dentry->d_move_count++; spin_unlock(&target->d_lock); spin_unlock(&dentry->d_lock); write_sequnlock(&rename_lock); diff -puN include/linux/dcache.h~dentry-qstr-consolidation-fix include/linux/dcache.h --- 25/include/linux/dcache.h~dentry-qstr-consolidation-fix 2004-05-08 13:25:24.389497704 -0700 +++ 25-akpm/include/linux/dcache.h 2004-05-08 13:56:10.101906768 -0700 @@ -92,7 +92,6 @@ struct dentry { void * d_fsdata; /* fs-specific data */ struct rcu_head d_rcu; struct dcookie_struct * d_cookie; /* cookie, if any */ - unsigned long d_move_count; /* to indicated moved dentry while lockless lookup */ struct dentry * d_parent; /* parent directory */ struct qstr d_name; struct hlist_node d_hash; /* lookup hash list */ @@ -118,13 +117,13 @@ struct dentry_operations { /* locking rules: - big lock dcache_lock may block -d_revalidate: no no yes -d_hash no no yes -d_compare: no yes no -d_delete: no yes no -d_release: no no yes -d_iput: no no yes + big lock dcache_lock d_lock may block +d_revalidate: no no no yes +d_hash no no no yes +d_compare: no yes yes no +d_delete: no yes no no +d_release: no no no yes +d_iput: no no no yes */ /* d_flags entries */ _