Commit ee9966a5 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] d_delete-d_lookup race fix

From: Maneesh Soni <maneesh@in.ibm.com>

d_delete() calls dentry_iput() after releasing the per dentry lock.  This
can race with __d_lookup and lead to situation where we can make dentry
negative with ref count > 1.  The following patch makes dentry_iput() to
hold per dentry lock till d_inode is NULL and dentry has been removed from
d_alias list.
parent 1a30ac21
...@@ -82,7 +82,7 @@ static void d_free(struct dentry *dentry) ...@@ -82,7 +82,7 @@ static void d_free(struct dentry *dentry)
/* /*
* Release the dentry's inode, using the filesystem * Release the dentry's inode, using the filesystem
* d_iput() operation if defined. * d_iput() operation if defined.
* Called with dcache_lock held, drops it. * Called with dcache_lock and per dentry lock held, drops both.
*/ */
static inline void dentry_iput(struct dentry * dentry) static inline void dentry_iput(struct dentry * dentry)
{ {
...@@ -90,13 +90,16 @@ static inline void dentry_iput(struct dentry * dentry) ...@@ -90,13 +90,16 @@ static inline void dentry_iput(struct dentry * dentry)
if (inode) { if (inode) {
dentry->d_inode = NULL; dentry->d_inode = NULL;
list_del_init(&dentry->d_alias); list_del_init(&dentry->d_alias);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
if (dentry->d_op && dentry->d_op->d_iput) if (dentry->d_op && dentry->d_op->d_iput)
dentry->d_op->d_iput(dentry, inode); dentry->d_op->d_iput(dentry, inode);
else else
iput(inode); iput(inode);
} else } else {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
}
} }
/* /*
...@@ -177,9 +180,8 @@ kill_it: { ...@@ -177,9 +180,8 @@ kill_it: {
dentry_stat.nr_unused--; dentry_stat.nr_unused--;
} }
list_del(&dentry->d_child); list_del(&dentry->d_child);
spin_unlock(&dentry->d_lock);
dentry_stat.nr_dentry--; /* For d_free, below */ dentry_stat.nr_dentry--; /* For d_free, below */
/* drops the lock, at that point nobody can reach this dentry */ /*drops the locks, at that point nobody can reach this dentry */
dentry_iput(dentry); dentry_iput(dentry);
parent = dentry->d_parent; parent = dentry->d_parent;
d_free(dentry); d_free(dentry);
...@@ -341,7 +343,6 @@ static inline void prune_one_dentry(struct dentry * dentry) ...@@ -341,7 +343,6 @@ static inline void prune_one_dentry(struct dentry * dentry)
__d_drop(dentry); __d_drop(dentry);
list_del(&dentry->d_child); list_del(&dentry->d_child);
spin_unlock(&dentry->d_lock);
dentry_stat.nr_dentry--; /* For d_free, below */ dentry_stat.nr_dentry--; /* For d_free, below */
dentry_iput(dentry); dentry_iput(dentry);
parent = dentry->d_parent; parent = dentry->d_parent;
...@@ -1116,7 +1117,6 @@ void d_delete(struct dentry * dentry) ...@@ -1116,7 +1117,6 @@ void d_delete(struct dentry * dentry)
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
if (atomic_read(&dentry->d_count) == 1) { if (atomic_read(&dentry->d_count) == 1) {
spin_unlock(&dentry->d_lock);
dentry_iput(dentry); dentry_iput(dentry);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment