summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-05-29 17:11:45 +0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-03-04 02:06:46 +0300
commit6179fe3753519bb624d767c02f9b685642cc63df (patch)
treee7053f21695ec61b54d4b3f75259e4d4838a35a9 /fs
parent286adf77d94e8b57a620e6cdbdcc72d99d8609b3 (diff)
downloadlinux-6179fe3753519bb624d767c02f9b685642cc63df.tar.xz
dealing with the rest of shrink_dentry_list() livelock
commit b2b80195d8829921506880f6dccd21cabd163d0d upstream. We have the same problem with ->d_lock order in the inner loop, where we are dropping references to ancestors. Same solution, basically - instead of using dentry_kill() we use lock_parent() (introduced in the previous commit) to get that lock in a safe way, recheck ->d_count (in case if lock_parent() has ended up dropping and retaking ->d_lock and somebody managed to grab a reference during that window), trylock the inode->i_lock and use __dentry_kill() to do the rest. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c22
1 files changed, 20 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index d30c45e7b819..9c9a3cd16411 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -886,8 +886,26 @@ static void shrink_dentry_list(struct list_head *list)
* fragmentation.
*/
dentry = parent;
- while (dentry && !lockref_put_or_lock(&dentry->d_lockref))
- dentry = dentry_kill(dentry, 1);
+ while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) {
+ parent = lock_parent(dentry);
+ if (dentry->d_lockref.count != 1) {
+ dentry->d_lockref.count--;
+ spin_unlock(&dentry->d_lock);
+ if (parent)
+ spin_unlock(&parent->d_lock);
+ break;
+ }
+ inode = dentry->d_inode; /* can't be NULL */
+ if (unlikely(!spin_trylock(&inode->i_lock))) {
+ spin_unlock(&dentry->d_lock);
+ if (parent)
+ spin_unlock(&parent->d_lock);
+ cpu_relax();
+ continue;
+ }
+ __dentry_kill(dentry);
+ dentry = parent;
+ }
}
}