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

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

lockless next_positive()



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 4f42c1b5
Loading
Loading
Loading
Loading
+27 −5
Original line number Diff line number Diff line
@@ -89,31 +89,53 @@ static struct dentry *next_positive(struct dentry *parent,
				    struct list_head *from,
				    int count)
{
	struct dentry *res = NULL;
	unsigned *seq = &parent->d_inode->i_dir_seq, n;
	struct dentry *res;
	struct list_head *p;
	bool skipped;
	int i;

	spin_lock(&parent->d_lock);
retry:
	i = count;
	skipped = false;
	n = smp_load_acquire(seq) & ~1;
	res = NULL;
	rcu_read_lock();
	for (p = from->next; p != &parent->d_subdirs; p = p->next) {
		struct dentry *d = list_entry(p, struct dentry, d_child);
		if (simple_positive(d) && !--count) {
		if (!simple_positive(d)) {
			skipped = true;
		} else if (!--i) {
			res = d;
			break;
		}
	}
	spin_unlock(&parent->d_lock);
	rcu_read_unlock();
	if (skipped) {
		smp_rmb();
		if (unlikely(*seq != n))
			goto retry;
	}
	return res;
}

static void move_cursor(struct dentry *cursor, struct list_head *after)
{
	struct dentry *parent = cursor->d_parent;

	unsigned n, *seq = &parent->d_inode->i_dir_seq;
	spin_lock(&parent->d_lock);
	for (;;) {
		n = *seq;
		if (!(n & 1) && cmpxchg(seq, n, n + 1) == n)
			break;
		cpu_relax();
	}
	__list_del(cursor->d_child.prev, cursor->d_child.next);
	if (after)
		list_add(&cursor->d_child, after);
	else
		list_add_tail(&cursor->d_child, &parent->d_subdirs);
	smp_store_release(seq, n + 2);
	spin_unlock(&parent->d_lock);
}