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

Commit 8e6ee05d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull autofs updates from Al Viro:
 "The most interesting part here is getting rid of the last trylock loop
  on dentry->d_lock.

  The ones in fs/dcache.c had been dealt with several years ago, but
  there'd been leftovers in fs/autofs/expire.c"

* 'work.autofs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  autofs_lookup(): hold ->d_lock over playing with ->d_flags
  get rid of autofs_info->active_count
  autofs: simplify get_next_positive_...(), get rid of trylocks
parents bc7d9aee 5f68056c
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ struct autofs_info {
	struct completion expire_complete;

	struct list_head active;
	int active_count;

	struct list_head expiring;

+32 −71
Original line number Diff line number Diff line
@@ -70,6 +70,27 @@ static int autofs_mount_busy(struct vfsmount *mnt,
	return status;
}

/* p->d_lock held */
static struct dentry *positive_after(struct dentry *p, struct dentry *child)
{
	if (child)
		child = list_next_entry(child, d_child);
	else
		child = list_first_entry(&p->d_subdirs, struct dentry, d_child);

	list_for_each_entry_from(child, &p->d_subdirs, d_child) {
		spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
		if (simple_positive(child)) {
			dget_dlock(child);
			spin_unlock(&child->d_lock);
			return child;
		}
		spin_unlock(&child->d_lock);
	}

	return NULL;
}

/*
 * Calculate and dget next entry in the subdirs list under root.
 */
@@ -77,43 +98,14 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
					       struct dentry *root)
{
	struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
	struct list_head *next;
	struct dentry *q;

	spin_lock(&sbi->lookup_lock);
	spin_lock(&root->d_lock);

	if (prev)
		next = prev->d_child.next;
	else {
		prev = dget_dlock(root);
		next = prev->d_subdirs.next;
	}

cont:
	if (next == &root->d_subdirs) {
		spin_unlock(&root->d_lock);
		spin_unlock(&sbi->lookup_lock);
		dput(prev);
		return NULL;
	}

	q = list_entry(next, struct dentry, d_child);

	spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
	/* Already gone or negative dentry (under construction) - try next */
	if (!d_count(q) || !simple_positive(q)) {
		spin_unlock(&q->d_lock);
		next = q->d_child.next;
		goto cont;
	}
	dget_dlock(q);
	spin_unlock(&q->d_lock);
	q = positive_after(root, prev);
	spin_unlock(&root->d_lock);
	spin_unlock(&sbi->lookup_lock);

	dput(prev);

	return q;
}

@@ -124,59 +116,28 @@ static struct dentry *get_next_positive_dentry(struct dentry *prev,
					       struct dentry *root)
{
	struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
	struct list_head *next;
	struct dentry *p, *ret;
	struct dentry *p = prev, *ret = NULL, *d = NULL;

	if (prev == NULL)
		return dget(root);

	spin_lock(&sbi->lookup_lock);
relock:
	p = prev;
	spin_lock(&p->d_lock);
again:
	next = p->d_subdirs.next;
	if (next == &p->d_subdirs) {
	while (1) {
		struct dentry *parent;

			if (p == root) {
				spin_unlock(&p->d_lock);
				spin_unlock(&sbi->lookup_lock);
				dput(prev);
				return NULL;
			}

		ret = positive_after(p, d);
		if (ret || p == root)
			break;
		parent = p->d_parent;
			if (!spin_trylock(&parent->d_lock)) {
		spin_unlock(&p->d_lock);
				cpu_relax();
				goto relock;
			}
			spin_unlock(&p->d_lock);
			next = p->d_child.next;
		spin_lock(&parent->d_lock);
		d = p;
		p = parent;
			if (next != &parent->d_subdirs)
				break;
	}
	}
	ret = list_entry(next, struct dentry, d_child);

	spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
	/* Negative dentry - try next */
	if (!simple_positive(ret)) {
		spin_unlock(&p->d_lock);
		lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_);
		p = ret;
		goto again;
	}
	dget_dlock(ret);
	spin_unlock(&ret->d_lock);
	spin_unlock(&p->d_lock);
	spin_unlock(&sbi->lookup_lock);

	dput(prev);

	return ret;
}

+12 −32
Original line number Diff line number Diff line
@@ -60,39 +60,16 @@ const struct dentry_operations autofs_dentry_operations = {
	.d_release	= autofs_dentry_release,
};

static void autofs_add_active(struct dentry *dentry)
{
	struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
	struct autofs_info *ino;

	ino = autofs_dentry_ino(dentry);
	if (ino) {
		spin_lock(&sbi->lookup_lock);
		if (!ino->active_count) {
			if (list_empty(&ino->active))
				list_add(&ino->active, &sbi->active_list);
		}
		ino->active_count++;
		spin_unlock(&sbi->lookup_lock);
	}
}

static void autofs_del_active(struct dentry *dentry)
{
	struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
	struct autofs_info *ino;

	ino = autofs_dentry_ino(dentry);
	if (ino) {
	spin_lock(&sbi->lookup_lock);
		ino->active_count--;
		if (!ino->active_count) {
			if (!list_empty(&ino->active))
	list_del_init(&ino->active);
		}
	spin_unlock(&sbi->lookup_lock);
}
}

static int autofs_dir_open(struct inode *inode, struct file *file)
{
@@ -527,19 +504,22 @@ static struct dentry *autofs_lookup(struct inode *dir,
		if (!autofs_oz_mode(sbi) && !IS_ROOT(dentry->d_parent))
			return ERR_PTR(-ENOENT);

		/* Mark entries in the root as mount triggers */
		if (IS_ROOT(dentry->d_parent) &&
		    autofs_type_indirect(sbi->type))
			__managed_dentry_set_managed(dentry);

		ino = autofs_new_ino(sbi);
		if (!ino)
			return ERR_PTR(-ENOMEM);

		spin_lock(&sbi->lookup_lock);
		spin_lock(&dentry->d_lock);
		/* Mark entries in the root as mount triggers */
		if (IS_ROOT(dentry->d_parent) &&
		    autofs_type_indirect(sbi->type))
			__managed_dentry_set_managed(dentry);
		dentry->d_fsdata = ino;
		ino->dentry = dentry;

		autofs_add_active(dentry);
		list_add(&ino->active, &sbi->active_list);
		spin_unlock(&sbi->lookup_lock);
		spin_unlock(&dentry->d_lock);
	}
	return NULL;
}