summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2010-06-05 00:13:05 -0400
committerThomas Gleixner <tglx@linutronix.de>2010-06-08 20:02:30 +0200
commita06a8672c6e156fb443ee804dca7bc0245414bc6 (patch)
tree8e23c6c9b8702081ce4f396e436f31c4ba77d55d
parentbdeadaf2f7daf6c098e83506d4d99142ca3912f8 (diff)
downloadlwn-a06a8672c6e156fb443ee804dca7bc0245414bc6.tar.gz
lwn-a06a8672c6e156fb443ee804dca7bc0245414bc6.zip
dcache: Prevent d_genocide() from decrementing d_count more than once
When d_genocide is called, it traverses the dentries decrementing the d_count. After the loops, a check is made to see if a rename or other change has been made, if so, it traverse the tree again. Unfortunately, this will decrement the same dentries that it decremented the first time. To avoid this multiple decrement, I added a DCACHE_GENOCIDE flag that will be set to a dentry when the d_genocide has decremented its counter. It will only decrement the counter if this flag is not set. Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Cc: Nick Piggin <npiggin@suse.de> Cc: John Kacur <jkacur@redhat.com> Cc: "Luis Claudio R. Goncalves" <lclaudio@uudg.org> Cc: Clark Williams <williams@redhat.com> Acked-by: john stultz <johnstul@us.ibm.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--fs/dcache.c10
-rw-r--r--include/linux/dcache.h1
2 files changed, 9 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 673cdc22ca46..18a3b762297c 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -2568,7 +2568,10 @@ resume:
spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
goto repeat;
}
- atomic_dec(&dentry->d_count);
+ if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
+ atomic_dec(&dentry->d_count);
+ dentry->d_flags |= DCACHE_GENOCIDE;
+ }
spin_unlock(&dentry->d_lock);
}
if (this_parent != root) {
@@ -2576,7 +2579,10 @@ resume:
struct dentry *child;
tmp = this_parent->d_parent;
- atomic_dec(&this_parent->d_count);
+ if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
+ atomic_dec(&this_parent->d_count);
+ this_parent->d_flags |= DCACHE_GENOCIDE;
+ }
rcu_read_lock();
spin_unlock(&this_parent->d_lock);
child = this_parent;
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index bc5a5ff141c8..63b98bec6630 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -201,6 +201,7 @@ d_iput: no no yes
/* Parent inode is watched by some fsnotify listener */
#define DCACHE_MOUNTED 0x0100 /* is a mountpoint */
+#define DCACHE_GENOCIDE 0x0200 /* being genocided */
extern seqlock_t rename_lock;