diff options
author | Steven Rostedt <rostedt@goodmis.org> | 2010-06-05 00:13:05 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2010-06-08 20:02:30 +0200 |
commit | a06a8672c6e156fb443ee804dca7bc0245414bc6 (patch) | |
tree | 8e23c6c9b8702081ce4f396e436f31c4ba77d55d | |
parent | bdeadaf2f7daf6c098e83506d4d99142ca3912f8 (diff) | |
download | lwn-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.c | 10 | ||||
-rw-r--r-- | include/linux/dcache.h | 1 |
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; |