|
@@ -1011,6 +1011,34 @@ void shrink_dcache_for_umount(struct super_block *sb)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * This tries to ascend one level of parenthood, but
|
|
|
|
+ * we can race with renaming, so we need to re-check
|
|
|
|
+ * the parenthood after dropping the lock and check
|
|
|
|
+ * that the sequence number still matches.
|
|
|
|
+ */
|
|
|
|
+static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq)
|
|
|
|
+{
|
|
|
|
+ struct dentry *new = old->d_parent;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ spin_unlock(&old->d_lock);
|
|
|
|
+ spin_lock(&new->d_lock);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * might go back up the wrong parent if we have had a rename
|
|
|
|
+ * or deletion
|
|
|
|
+ */
|
|
|
|
+ if (new != old->d_parent ||
|
|
|
|
+ (!locked && read_seqretry(&rename_lock, seq))) {
|
|
|
|
+ spin_unlock(&new->d_lock);
|
|
|
|
+ new = NULL;
|
|
|
|
+ }
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ return new;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Search for at least 1 mount point in the dentry's subdirs.
|
|
* Search for at least 1 mount point in the dentry's subdirs.
|
|
* We descend to the next level whenever the d_subdirs
|
|
* We descend to the next level whenever the d_subdirs
|
|
@@ -1066,24 +1094,10 @@ resume:
|
|
* All done at this level ... ascend and resume the search.
|
|
* All done at this level ... ascend and resume the search.
|
|
*/
|
|
*/
|
|
if (this_parent != parent) {
|
|
if (this_parent != parent) {
|
|
- struct dentry *tmp;
|
|
|
|
- struct dentry *child;
|
|
|
|
-
|
|
|
|
- tmp = this_parent->d_parent;
|
|
|
|
- rcu_read_lock();
|
|
|
|
- spin_unlock(&this_parent->d_lock);
|
|
|
|
- child = this_parent;
|
|
|
|
- this_parent = tmp;
|
|
|
|
- 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 ||
|
|
|
|
- (!locked && read_seqretry(&rename_lock, seq))) {
|
|
|
|
- spin_unlock(&this_parent->d_lock);
|
|
|
|
- rcu_read_unlock();
|
|
|
|
|
|
+ struct dentry *child = this_parent;
|
|
|
|
+ this_parent = try_to_ascend(this_parent, locked, seq);
|
|
|
|
+ if (!this_parent)
|
|
goto rename_retry;
|
|
goto rename_retry;
|
|
- }
|
|
|
|
- rcu_read_unlock();
|
|
|
|
next = child->d_u.d_child.next;
|
|
next = child->d_u.d_child.next;
|
|
goto resume;
|
|
goto resume;
|
|
}
|
|
}
|
|
@@ -1181,24 +1195,10 @@ resume:
|
|
* All done at this level ... ascend and resume the search.
|
|
* All done at this level ... ascend and resume the search.
|
|
*/
|
|
*/
|
|
if (this_parent != parent) {
|
|
if (this_parent != parent) {
|
|
- struct dentry *tmp;
|
|
|
|
- struct dentry *child;
|
|
|
|
-
|
|
|
|
- tmp = this_parent->d_parent;
|
|
|
|
- rcu_read_lock();
|
|
|
|
- spin_unlock(&this_parent->d_lock);
|
|
|
|
- child = this_parent;
|
|
|
|
- this_parent = tmp;
|
|
|
|
- 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 ||
|
|
|
|
- (!locked && read_seqretry(&rename_lock, seq))) {
|
|
|
|
- spin_unlock(&this_parent->d_lock);
|
|
|
|
- rcu_read_unlock();
|
|
|
|
|
|
+ struct dentry *child = this_parent;
|
|
|
|
+ this_parent = try_to_ascend(this_parent, locked, seq);
|
|
|
|
+ if (!this_parent)
|
|
goto rename_retry;
|
|
goto rename_retry;
|
|
- }
|
|
|
|
- rcu_read_unlock();
|
|
|
|
next = child->d_u.d_child.next;
|
|
next = child->d_u.d_child.next;
|
|
goto resume;
|
|
goto resume;
|
|
}
|
|
}
|
|
@@ -2942,28 +2942,14 @@ resume:
|
|
spin_unlock(&dentry->d_lock);
|
|
spin_unlock(&dentry->d_lock);
|
|
}
|
|
}
|
|
if (this_parent != root) {
|
|
if (this_parent != root) {
|
|
- struct dentry *tmp;
|
|
|
|
- struct dentry *child;
|
|
|
|
-
|
|
|
|
- tmp = this_parent->d_parent;
|
|
|
|
|
|
+ struct dentry *child = this_parent;
|
|
if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
|
|
if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
|
|
this_parent->d_flags |= DCACHE_GENOCIDE;
|
|
this_parent->d_flags |= DCACHE_GENOCIDE;
|
|
this_parent->d_count--;
|
|
this_parent->d_count--;
|
|
}
|
|
}
|
|
- rcu_read_lock();
|
|
|
|
- spin_unlock(&this_parent->d_lock);
|
|
|
|
- child = this_parent;
|
|
|
|
- this_parent = tmp;
|
|
|
|
- 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 ||
|
|
|
|
- (!locked && read_seqretry(&rename_lock, seq))) {
|
|
|
|
- spin_unlock(&this_parent->d_lock);
|
|
|
|
- rcu_read_unlock();
|
|
|
|
|
|
+ this_parent = try_to_ascend(this_parent, locked, seq);
|
|
|
|
+ if (!this_parent)
|
|
goto rename_retry;
|
|
goto rename_retry;
|
|
- }
|
|
|
|
- rcu_read_unlock();
|
|
|
|
next = child->d_u.d_child.next;
|
|
next = child->d_u.d_child.next;
|
|
goto resume;
|
|
goto resume;
|
|
}
|
|
}
|