Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca5358ef authored by Al Viro's avatar Al Viro
Browse files

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 946e51f2
Loading
Loading
Loading
Loading
+16 −15
Original line number Diff line number Diff line
@@ -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 @@ static void d_walk(struct dentry *parent, void *data,
	/*
	 * 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 @@ static void d_walk(struct dentry *parent, void *data,
	return;

rename_retry:
	spin_unlock(&this_parent->d_lock);
	rcu_read_unlock();
	BUG_ON(seq & 1);
	if (!retry)
		return;
	seq = 1;