From: Jan Blunck 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 Signed-off-by: Andrew Morton --- 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-01-28 20:37:31.353893960 -0800 +++ 25-akpm/fs/autofs4/root.c 2005-01-28 20:37:31.369891528 -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-01-28 20:37:31.355893656 -0800 +++ 25-akpm/fs/dcache.c 2005-01-28 20:37:31.368891680 -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-01-28 20:37:31.357893352 -0800 +++ 25-akpm/fs/namei.c 2005-01-28 20:37:31.367891832 -0800 @@ -1692,17 +1692,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-01-28 20:37:31.359893048 -0800 +++ 25-akpm/fs/proc/base.c 2005-01-28 20:37:31.370891376 -0800 @@ -1649,11 +1649,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-01-28 20:37:31.360892896 -0800 +++ 25-akpm/fs/sysfs/inode.c 2005-01-28 20:37:31.367891832 -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-01-28 20:37:31.361892744 -0800 +++ 25-akpm/include/linux/dcache.h 2005-01-28 20:40:27.596101072 -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); } _