Commit 68c025c5 authored by Al Viro's avatar Al Viro Committed by Luis Henriques

deal with deadlock in d_walk()

commit ca5358ef upstream.

... by not hitting rename_retry for reasons other than rename having
happened.  In other words, do _not_ restart when finding that
between unlocking the child and locking the parent the former got
into __dentry_kill().  Skip the killed siblings instead...
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Cc: Moritz Muehlenhoff <jmm@inutil.org>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent f185f12c
...@@ -466,7 +466,7 @@ static void __dentry_kill(struct dentry *dentry) ...@@ -466,7 +466,7 @@ static void __dentry_kill(struct dentry *dentry)
} }
/* if it was on the hash then remove it */ /* if it was on the hash then remove it */
__d_drop(dentry); __d_drop(dentry);
list_del(&dentry->d_child); __list_del_entry(&dentry->d_child);
/* /*
* Inform d_walk() that we are no longer attached to the * Inform d_walk() that we are no longer attached to the
* dentry tree * dentry tree
...@@ -1120,33 +1120,31 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1120,33 +1120,31 @@ static void d_walk(struct dentry *parent, void *data,
/* /*
* All done at this level ... ascend and resume the search. * All done at this level ... ascend and resume the search.
*/ */
rcu_read_lock();
ascend:
if (this_parent != parent) { if (this_parent != parent) {
struct dentry *child = this_parent; struct dentry *child = this_parent;
this_parent = child->d_parent; this_parent = child->d_parent;
rcu_read_lock();
spin_unlock(&child->d_lock); spin_unlock(&child->d_lock);
spin_lock(&this_parent->d_lock); spin_lock(&this_parent->d_lock);
/* /* might go back up the wrong parent if we have had a rename. */
* might go back up the wrong parent if we have had a rename if (need_seqretry(&rename_lock, seq))
* or deletion
*/
if (this_parent != child->d_parent ||
(child->d_flags & DCACHE_DENTRY_KILLED) ||
need_seqretry(&rename_lock, seq)) {
spin_unlock(&this_parent->d_lock);
rcu_read_unlock();
goto rename_retry; goto rename_retry;
next = child->d_child.next;
while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)) {
if (next == &this_parent->d_subdirs)
goto ascend;
child = list_entry(next, struct dentry, d_child);
next = next->next;
} }
rcu_read_unlock(); rcu_read_unlock();
next = child->d_child.next;
goto resume; goto resume;
} }
if (need_seqretry(&rename_lock, seq)) { if (need_seqretry(&rename_lock, seq))
spin_unlock(&this_parent->d_lock);
goto rename_retry; goto rename_retry;
} rcu_read_unlock();
if (finish) if (finish)
finish(data); finish(data);
...@@ -1156,6 +1154,9 @@ static void d_walk(struct dentry *parent, void *data, ...@@ -1156,6 +1154,9 @@ static void d_walk(struct dentry *parent, void *data,
return; return;
rename_retry: rename_retry:
spin_unlock(&this_parent->d_lock);
rcu_read_unlock();
BUG_ON(seq & 1);
if (!retry) if (!retry)
return; return;
seq = 1; seq = 1;
......
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