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

Commit 58db63d0 authored by Nick Piggin's avatar Nick Piggin
Browse files

fs: dcache avoid starvation in dcache multi-step operations



Long lived dcache "multi-step" operations which retry on rename seq can
be starved with a lot of rename activity. If they fail after the 1st pass,
take the rename_lock for writing to avoid further starvation.

Signed-off-by: default avatarNick Piggin <npiggin@kernel.dk>
parent b5c84bf6
Loading
Loading
Loading
Loading
+42 −14
Original line number Diff line number Diff line
@@ -973,10 +973,11 @@ int have_submounts(struct dentry *parent)
	struct dentry *this_parent;
	struct list_head *next;
	unsigned seq;
	int locked = 0;

rename_retry:
	this_parent = parent;
	seq = read_seqbegin(&rename_lock);
again:
	this_parent = parent;

	if (d_mountpoint(parent))
		goto positive;
@@ -1021,7 +1022,7 @@ int have_submounts(struct dentry *parent)
		/* might go back up the wrong parent if we have had a rename
		 * or deletion */
		if (this_parent != child->d_parent ||
				read_seqretry(&rename_lock, seq)) {
			 (!locked && read_seqretry(&rename_lock, seq))) {
			spin_unlock(&this_parent->d_lock);
			rcu_read_unlock();
			goto rename_retry;
@@ -1031,13 +1032,22 @@ int have_submounts(struct dentry *parent)
		goto resume;
	}
	spin_unlock(&this_parent->d_lock);
	if (read_seqretry(&rename_lock, seq))
	if (!locked && read_seqretry(&rename_lock, seq))
		goto rename_retry;
	if (locked)
		write_sequnlock(&rename_lock);
	return 0; /* No mount points found in tree */
positive:
	if (read_seqretry(&rename_lock, seq))
	if (!locked && read_seqretry(&rename_lock, seq))
		goto rename_retry;
	if (locked)
		write_sequnlock(&rename_lock);
	return 1;

rename_retry:
	locked = 1;
	write_seqlock(&rename_lock);
	goto again;
}
EXPORT_SYMBOL(have_submounts);

@@ -1061,11 +1071,11 @@ static int select_parent(struct dentry * parent)
	struct list_head *next;
	unsigned seq;
	int found = 0;
	int locked = 0;

rename_retry:
	this_parent = parent;
	seq = read_seqbegin(&rename_lock);

again:
	this_parent = parent;
	spin_lock(&this_parent->d_lock);
repeat:
	next = this_parent->d_subdirs.next;
@@ -1127,7 +1137,7 @@ static int select_parent(struct dentry * parent)
		/* might go back up the wrong parent if we have had a rename
		 * or deletion */
		if (this_parent != child->d_parent ||
				read_seqretry(&rename_lock, seq)) {
			(!locked && read_seqretry(&rename_lock, seq))) {
			spin_unlock(&this_parent->d_lock);
			rcu_read_unlock();
			goto rename_retry;
@@ -1138,9 +1148,18 @@ static int select_parent(struct dentry * parent)
	}
out:
	spin_unlock(&this_parent->d_lock);
	if (read_seqretry(&rename_lock, seq))
	if (!locked && read_seqretry(&rename_lock, seq))
		goto rename_retry;
	if (locked)
		write_sequnlock(&rename_lock);
	return found;

rename_retry:
	if (found)
		return found;
	locked = 1;
	write_seqlock(&rename_lock);
	goto again;
}

/**
@@ -2655,10 +2674,11 @@ void d_genocide(struct dentry *root)
	struct dentry *this_parent;
	struct list_head *next;
	unsigned seq;
	int locked = 0;

rename_retry:
	this_parent = root;
	seq = read_seqbegin(&rename_lock);
again:
	this_parent = root;
	spin_lock(&this_parent->d_lock);
repeat:
	next = this_parent->d_subdirs.next;
@@ -2703,7 +2723,7 @@ void d_genocide(struct dentry *root)
		/* might go back up the wrong parent if we have had a rename
		 * or deletion */
		if (this_parent != child->d_parent ||
				read_seqretry(&rename_lock, seq)) {
			 (!locked && read_seqretry(&rename_lock, seq))) {
			spin_unlock(&this_parent->d_lock);
			rcu_read_unlock();
			goto rename_retry;
@@ -2713,8 +2733,16 @@ void d_genocide(struct dentry *root)
		goto resume;
	}
	spin_unlock(&this_parent->d_lock);
	if (read_seqretry(&rename_lock, seq))
	if (!locked && read_seqretry(&rename_lock, seq))
		goto rename_retry;
	if (locked)
		write_sequnlock(&rename_lock);
	return;

rename_retry:
	locked = 1;
	write_seqlock(&rename_lock);
	goto again;
}

/**