
From: Jan Blunck <j.blunck@tu-harburg.de>

d_drop() must use the dentry->d_lock spinlock.  In some cases __d_drop()
was used without holding the dentry->d_lock spinlock, too.  This could end
in a race with __d_lookup().

Signed-off-by: Jan Blunck <j.blunck@tu-harburg.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/autofs4/root.c      |    2 ++
 25-akpm/fs/dcache.c            |    3 +++
 25-akpm/fs/namei.c             |   14 +++++---------
 25-akpm/fs/proc/base.c         |    6 +++++-
 25-akpm/fs/sysfs/inode.c       |    6 +++++-
 25-akpm/include/linux/dcache.h |   19 ++++++++++---------
 6 files changed, 30 insertions(+), 20 deletions(-)

diff -puN fs/autofs4/root.c~d_drop-should-use-per-dentry-lock fs/autofs4/root.c
--- 25/fs/autofs4/root.c~d_drop-should-use-per-dentry-lock	2005-03-07 20:41:36.000000000 -0800
+++ 25-akpm/fs/autofs4/root.c	2005-03-07 20:41:36.000000000 -0800
@@ -605,7 +605,9 @@ static int autofs4_dir_rmdir(struct inod
 		spin_unlock(&dcache_lock);
 		return -ENOTEMPTY;
 	}
+	spin_lock(&dentry->d_lock);
 	__d_drop(dentry);
+	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dcache_lock);
 
 	dput(ino->dentry);
diff -puN fs/dcache.c~d_drop-should-use-per-dentry-lock fs/dcache.c
--- 25/fs/dcache.c~d_drop-should-use-per-dentry-lock	2005-03-07 20:41:36.000000000 -0800
+++ 25-akpm/fs/dcache.c	2005-03-07 20:41:36.000000000 -0800
@@ -340,13 +340,16 @@ restart:
 	tmp = head;
 	while ((tmp = tmp->next) != head) {
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+		spin_lock(&dentry->d_lock);
 		if (!atomic_read(&dentry->d_count)) {
 			__dget_locked(dentry);
 			__d_drop(dentry);
+			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dcache_lock);
 			dput(dentry);
 			goto restart;
 		}
+		spin_unlock(&dentry->d_lock);
 	}
 	spin_unlock(&dcache_lock);
 }
diff -puN fs/namei.c~d_drop-should-use-per-dentry-lock fs/namei.c
--- 25/fs/namei.c~d_drop-should-use-per-dentry-lock	2005-03-07 20:41:36.000000000 -0800
+++ 25-akpm/fs/namei.c	2005-03-07 20:41:36.000000000 -0800
@@ -1685,17 +1685,13 @@ out:
 void dentry_unhash(struct dentry *dentry)
 {
 	dget(dentry);
-	spin_lock(&dcache_lock);
-	switch (atomic_read(&dentry->d_count)) {
-	default:
-		spin_unlock(&dcache_lock);
+	if (atomic_read(&dentry->d_count))
 		shrink_dcache_parent(dentry);
-		spin_lock(&dcache_lock);
-		if (atomic_read(&dentry->d_count) != 2)
-			break;
-	case 2:
+	spin_lock(&dcache_lock);
+	spin_lock(&dentry->d_lock);
+	if (atomic_read(&dentry->d_count) == 2)
 		__d_drop(dentry);
-	}
+	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dcache_lock);
 }
 
diff -puN fs/proc/base.c~d_drop-should-use-per-dentry-lock fs/proc/base.c
--- 25/fs/proc/base.c~d_drop-should-use-per-dentry-lock	2005-03-07 20:41:36.000000000 -0800
+++ 25-akpm/fs/proc/base.c	2005-03-07 21:14:32.000000000 -0800
@@ -1630,11 +1630,15 @@ struct dentry *proc_pid_unhash(struct ta
 	if (proc_dentry != NULL) {
 
 		spin_lock(&dcache_lock);
+		spin_lock(&proc_dentry->d_lock);
 		if (!d_unhashed(proc_dentry)) {
 			dget_locked(proc_dentry);
 			__d_drop(proc_dentry);
-		} else
+			spin_unlock(&proc_dentry->d_lock);
+		} else {
+			spin_unlock(&proc_dentry->d_lock);
 			proc_dentry = NULL;
+		}
 		spin_unlock(&dcache_lock);
 	}
 	return proc_dentry;
diff -puN fs/sysfs/inode.c~d_drop-should-use-per-dentry-lock fs/sysfs/inode.c
--- 25/fs/sysfs/inode.c~d_drop-should-use-per-dentry-lock	2005-03-07 20:41:36.000000000 -0800
+++ 25-akpm/fs/sysfs/inode.c	2005-03-07 20:41:36.000000000 -0800
@@ -129,13 +129,17 @@ void sysfs_drop_dentry(struct sysfs_dire
 
 	if (dentry) {
 		spin_lock(&dcache_lock);
+		spin_lock(&dentry->d_lock);
 		if (!(d_unhashed(dentry) && dentry->d_inode)) {
 			dget_locked(dentry);
 			__d_drop(dentry);
+			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dcache_lock);
 			simple_unlink(parent->d_inode, dentry);
-		} else
+		} else {
+			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dcache_lock);
+		}
 	}
 }
 
diff -puN include/linux/dcache.h~d_drop-should-use-per-dentry-lock include/linux/dcache.h
--- 25/include/linux/dcache.h~d_drop-should-use-per-dentry-lock	2005-03-07 20:41:36.000000000 -0800
+++ 25-akpm/include/linux/dcache.h	2005-03-07 20:41:36.000000000 -0800
@@ -162,17 +162,16 @@ extern spinlock_t dcache_lock;
  * d_drop - drop a dentry
  * @dentry: dentry to drop
  *
- * d_drop() unhashes the entry from the parent
- * dentry hashes, so that it won't be found through
- * a VFS lookup any more. Note that this is different
- * from deleting the dentry - d_delete will try to
- * mark the dentry negative if possible, giving a
- * successful _negative_ lookup, while d_drop will
+ * d_drop() unhashes the entry from the parent dentry hashes, so that it won't
+ * be found through a VFS lookup any more. Note that this is different from
+ * deleting the dentry - d_delete will try to mark the dentry negative if
+ * possible, giving a successful _negative_ lookup, while d_drop will
  * just make the cache lookup fail.
  *
- * d_drop() is used mainly for stuff that wants
- * to invalidate a dentry for some reason (NFS
- * timeouts or autofs deletes).
+ * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
+ * reason (NFS timeouts or autofs deletes).
+ *
+ * __d_drop requires dentry->d_lock.
  */
 
 static inline void __d_drop(struct dentry *dentry)
@@ -186,7 +185,9 @@ static inline void __d_drop(struct dentr
 static inline void d_drop(struct dentry *dentry)
 {
 	spin_lock(&dcache_lock);
+	spin_lock(&dentry->d_lock);
  	__d_drop(dentry);
+	spin_unlock(&dentry->d_lock);
 	spin_unlock(&dcache_lock);
 }
 
_
