summaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-10-26 19:31:10 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2014-11-03 15:22:16 -0500
commitca5358ef75fc69fee5322a38a340f5739d997c10 (patch)
tree88d4abe38ce55bdac9c8b207a0dce743504e0a28 /fs/dcache.c
parent946e51f2bf37f1656916eb75bd0742ba33983c28 (diff)
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>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r--fs/dcache.c31
1 files changed, 16 insertions, 15 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 8b4c45e40834..e90aa825cc03 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -495,7 +495,7 @@ static void __dentry_kill(struct dentry *dentry)
495 } 495 }
496 /* if it was on the hash then remove it */ 496 /* if it was on the hash then remove it */
497 __d_drop(dentry); 497 __d_drop(dentry);
498 list_del(&dentry->d_child); 498 __list_del_entry(&dentry->d_child);
499 /* 499 /*
500 * Inform d_walk() that we are no longer attached to the 500 * Inform d_walk() that we are no longer attached to the
501 * dentry tree 501 * dentry tree
@@ -1081,33 +1081,31 @@ resume:
1081 /* 1081 /*
1082 * All done at this level ... ascend and resume the search. 1082 * All done at this level ... ascend and resume the search.
1083 */ 1083 */
1084 rcu_read_lock();
1085ascend:
1084 if (this_parent != parent) { 1086 if (this_parent != parent) {
1085 struct dentry *child = this_parent; 1087 struct dentry *child = this_parent;
1086 this_parent = child->d_parent; 1088 this_parent = child->d_parent;
1087 1089
1088 rcu_read_lock();
1089 spin_unlock(&child->d_lock); 1090 spin_unlock(&child->d_lock);
1090 spin_lock(&this_parent->d_lock); 1091 spin_lock(&this_parent->d_lock);
1091 1092
1092 /* 1093 /* might go back up the wrong parent if we have had a rename. */
1093 * might go back up the wrong parent if we have had a rename 1094 if (need_seqretry(&rename_lock, seq))
1094 * or deletion
1095 */
1096 if (this_parent != child->d_parent ||
1097 (child->d_flags & DCACHE_DENTRY_KILLED) ||
1098 need_seqretry(&rename_lock, seq)) {
1099 spin_unlock(&this_parent->d_lock);
1100 rcu_read_unlock();
1101 goto rename_retry; 1095 goto rename_retry;
1096 next = child->d_child.next;
1097 while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)) {
1098 if (next == &this_parent->d_subdirs)
1099 goto ascend;
1100 child = list_entry(next, struct dentry, d_child);
1101 next = next->next;
1102 } 1102 }
1103 rcu_read_unlock(); 1103 rcu_read_unlock();
1104 next = child->d_child.next;
1105 goto resume; 1104 goto resume;
1106 } 1105 }
1107 if (need_seqretry(&rename_lock, seq)) { 1106 if (need_seqretry(&rename_lock, seq))
1108 spin_unlock(&this_parent->d_lock);
1109 goto rename_retry; 1107 goto rename_retry;
1110 } 1108 rcu_read_unlock();
1111 if (finish) 1109 if (finish)
1112 finish(data); 1110 finish(data);
1113 1111
@@ -1117,6 +1115,9 @@ out_unlock:
1117 return; 1115 return;
1118 1116
1119rename_retry: 1117rename_retry:
1118 spin_unlock(&this_parent->d_lock);
1119 rcu_read_unlock();
1120 BUG_ON(seq & 1);
1120 if (!retry) 1121 if (!retry)
1121 return; 1122 return;
1122 seq = 1; 1123 seq = 1;