summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Blunck <j.blunck@tu-harburg.de>2005-03-07 17:48:44 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-03-07 17:48:44 -0800
commitcd67725a0cede85ca80348333efdd7063e3ebfb7 (patch)
treeb6a0ddb8df3bb31a58bcdd375aa8beab95c553f4
parent02721572728cccd31b54d69e1dfb8124fa02407e (diff)
[PATCH] d_drop should use per dentry lock
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> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/autofs4/root.c2
-rw-r--r--fs/dcache.c3
-rw-r--r--fs/namei.c14
-rw-r--r--fs/proc/base.c6
-rw-r--r--fs/sysfs/inode.c6
-rw-r--r--include/linux/dcache.h19
6 files changed, 30 insertions, 20 deletions
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 82ef8ed2fabc..3765c047f157 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -605,7 +605,9 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
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 --git a/fs/dcache.c b/fs/dcache.c
index ed90b724af78..e6acad700833 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -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 --git a/fs/namei.c b/fs/namei.c
index 281ca91fd1cc..63e3e6494f8d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -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 --git a/fs/proc/base.c b/fs/proc/base.c
index b30ee97c308f..9ab35875845d 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1630,11 +1630,15 @@ struct dentry *proc_pid_unhash(struct task_struct *p)
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 --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 204d071baa5c..97dc6db0870c 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -129,13 +129,17 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
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 --git a/include/linux/dcache.h b/include/linux/dcache.h
index 2da76867183c..50be290d24d2 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -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 dentry *dentry)
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);
}