diff options
Diffstat (limited to 'freed-ora/current/master/deal-with-deadlock-in-d_walk.patch')
-rw-r--r-- | freed-ora/current/master/deal-with-deadlock-in-d_walk.patch | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/freed-ora/current/master/deal-with-deadlock-in-d_walk.patch b/freed-ora/current/master/deal-with-deadlock-in-d_walk.patch new file mode 100644 index 000000000..fd0e21c33 --- /dev/null +++ b/freed-ora/current/master/deal-with-deadlock-in-d_walk.patch @@ -0,0 +1,86 @@ +From: Al Viro <viro@zeniv.linux.org.uk> +Date: Sun, 26 Oct 2014 19:31:10 -0400 +Subject: [PATCH] deal with deadlock in d_walk() + +... 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: Al Viro <viro@zeniv.linux.org.uk> +--- + fs/dcache.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +diff --git a/fs/dcache.c b/fs/dcache.c +index c3ea5b765f6a..71acf8d6f2be 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -495,7 +495,7 @@ static void __dentry_kill(struct dentry *dentry) + } + /* if it was on the hash then remove it */ + __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 + * dentry tree +@@ -1082,33 +1082,31 @@ resume: + /* + * All done at this level ... ascend and resume the search. + */ ++ rcu_read_lock(); ++ascend: + if (this_parent != parent) { + struct dentry *child = this_parent; + this_parent = child->d_parent; + +- rcu_read_lock(); + spin_unlock(&child->d_lock); + spin_lock(&this_parent->d_lock); + +- /* +- * might go back up the wrong parent if we have had a rename +- * 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(); ++ /* might go back up the wrong parent if we have had a rename. */ ++ if (need_seqretry(&rename_lock, seq)) + 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(); +- next = child->d_child.next; + goto resume; + } +- if (need_seqretry(&rename_lock, seq)) { +- spin_unlock(&this_parent->d_lock); ++ if (need_seqretry(&rename_lock, seq)) + goto rename_retry; +- } ++ rcu_read_unlock(); + if (finish) + finish(data); + +@@ -1118,6 +1116,9 @@ out_unlock: + return; + + rename_retry: ++ spin_unlock(&this_parent->d_lock); ++ rcu_read_unlock(); ++ BUG_ON(seq & 1); + if (!retry) + return; + seq = 1; +-- +2.1.0 + |