Commit 6179fe37 authored by Al Viro's avatar Al Viro Committed by Greg Kroah-Hartman

dealing with the rest of shrink_dentry_list() livelock

commit b2b80195 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: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 286adf77
...@@ -886,8 +886,26 @@ static void shrink_dentry_list(struct list_head *list) ...@@ -886,8 +886,26 @@ static void shrink_dentry_list(struct list_head *list)
* fragmentation. * fragmentation.
*/ */
dentry = parent; dentry = parent;
while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) while (dentry && !lockref_put_or_lock(&dentry->d_lockref)) {
dentry = dentry_kill(dentry, 1); 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;
}
} }
} }
......
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