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 */

_