|
@@ -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
|
|
@@ -1081,33 +1081,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);
|
|
|
|
|
@@ -1117,6 +1115,9 @@ out_unlock:
|
|
|
return;
|
|
|
|
|
|
rename_retry:
|
|
|
+ spin_unlock(&this_parent->d_lock);
|
|
|
+ rcu_read_unlock();
|
|
|
+ BUG_ON(seq & 1);
|
|
|
if (!retry)
|
|
|
return;
|
|
|
seq = 1;
|