aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-08 16:46:52 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-08 16:46:52 -0400
commit0d98439ea3c6ffb2af931f6de4480e744634e2c5 (patch)
treed272064d6bf26a38e246320ecd0445bb75345bf1 /fs/dcache.c
parent8aab6a27332bbf2abfcb35224738394e784d940b (diff)
vfs: use lockred "dead" flag to mark unrecoverably dead dentries
This simplifies the RCU to refcounting code in particular. I was originally intending to leave this for later, but walking through all the dput() logic (see previous commit), I realized that the dput() "might_sleep()" check was misleadingly weak. And I removed it as misleading, both for performance profiling and for debugging. However, the might_sleep() debugging case is actually true: the final dput() can indeed sleep, if the inode of the dentry that you are releasing ends up sleeping at iput time (see dentry_iput()). So the problem with the might_sleep() in dput() wasn't that it wasn't true, it was that it wasn't actually testing and triggering on the interesting case. In particular, just about *any* dput() can indeed sleep, if you happen to race with another thread deleting the file in question, and you then lose the race to the be the last dput() for that file. But because it's a very rare race, the debugging code would never trigger it in practice. Why is this problematic? The new d_rcu_to_refcount() (see commit 15570086b590: "vfs: reimplement d_rcu_to_refcount() using lockref_get_or_lock()") does a dput() for the failure case, and it does it under the RCU lock. So potentially sleeping really is a bug. But there's no way I'm going to fix this with the previous complicated "lockref_get_or_lock()" interface. And rather than revert to the old and crufty nested dentry locking code (which did get this right by delaying the reference count updates until they were verified to be safe), let's make forward progress. Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r--fs/dcache.c17
1 files changed, 10 insertions, 7 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index bf3c4f9569eb..ca8e9cd60f87 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -229,7 +229,7 @@ static void __d_free(struct rcu_head *head)
229 */ 229 */
230static void d_free(struct dentry *dentry) 230static void d_free(struct dentry *dentry)
231{ 231{
232 BUG_ON(dentry->d_lockref.count); 232 BUG_ON((int)dentry->d_lockref.count > 0);
233 this_cpu_dec(nr_dentry); 233 this_cpu_dec(nr_dentry);
234 if (dentry->d_op && dentry->d_op->d_release) 234 if (dentry->d_op && dentry->d_op->d_release)
235 dentry->d_op->d_release(dentry); 235 dentry->d_op->d_release(dentry);
@@ -445,7 +445,7 @@ EXPORT_SYMBOL(d_drop);
445 * If ref is non-zero, then decrement the refcount too. 445 * If ref is non-zero, then decrement the refcount too.
446 * Returns dentry requiring refcount drop, or NULL if we're done. 446 * Returns dentry requiring refcount drop, or NULL if we're done.
447 */ 447 */
448static inline struct dentry *dentry_kill(struct dentry *dentry, int ref) 448static inline struct dentry *dentry_kill(struct dentry *dentry)
449 __releases(dentry->d_lock) 449 __releases(dentry->d_lock)
450{ 450{
451 struct inode *inode; 451 struct inode *inode;
@@ -468,8 +468,11 @@ relock:
468 goto relock; 468 goto relock;
469 } 469 }
470 470
471 if (ref) 471 /*
472 dentry->d_lockref.count--; 472 * The dentry is now unrecoverably dead to the world.
473 */
474 lockref_mark_dead(&dentry->d_lockref);
475
473 /* 476 /*
474 * inform the fs via d_prune that this dentry is about to be 477 * inform the fs via d_prune that this dentry is about to be
475 * unhashed and destroyed. 478 * unhashed and destroyed.
@@ -535,7 +538,7 @@ repeat:
535 return; 538 return;
536 539
537kill_it: 540kill_it:
538 dentry = dentry_kill(dentry, 1); 541 dentry = dentry_kill(dentry);
539 if (dentry) 542 if (dentry)
540 goto repeat; 543 goto repeat;
541} 544}
@@ -760,7 +763,7 @@ static void try_prune_one_dentry(struct dentry *dentry)
760{ 763{
761 struct dentry *parent; 764 struct dentry *parent;
762 765
763 parent = dentry_kill(dentry, 0); 766 parent = dentry_kill(dentry);
764 /* 767 /*
765 * If dentry_kill returns NULL, we have nothing more to do. 768 * If dentry_kill returns NULL, we have nothing more to do.
766 * if it returns the same dentry, trylocks failed. In either 769 * if it returns the same dentry, trylocks failed. In either
@@ -781,7 +784,7 @@ static void try_prune_one_dentry(struct dentry *dentry)
781 while (dentry) { 784 while (dentry) {
782 if (lockref_put_or_lock(&dentry->d_lockref)) 785 if (lockref_put_or_lock(&dentry->d_lockref))
783 return; 786 return;
784 dentry = dentry_kill(dentry, 1); 787 dentry = dentry_kill(dentry);
785 } 788 }
786} 789}
787 790