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

Commit 14386af6 authored by Al Viro's avatar Al Viro Committed by Sasha Levin
Browse files

autofs races



[ Upstream commit ea01a18494b3d7a91b2f1f2a6a5aaef4741bc294 ]

* make autofs4_expire_indirect() skip the dentries being in process of
expiry
* do *not* mess with list_move(); making sure that dentry with
AUTOFS_INF_EXPIRING are not picked for expiry is enough.
* do not remove NO_RCU when we set EXPIRING, don't bother with smp_mb()
there.  Clear it at the same time we clear EXPIRING.  Makes a bunch of
tests simpler.
* rename NO_RCU to WANT_EXPIRE, which is what it really is.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
parent de32bc4c
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -79,9 +79,13 @@ struct autofs_info {
};

#define AUTOFS_INF_EXPIRING	(1<<0) /* dentry is in the process of expiring */
#define AUTOFS_INF_NO_RCU	(1<<1) /* the dentry is being considered
#define AUTOFS_INF_WANT_EXPIRE	(1<<1) /* the dentry is being considered
					* for expiry, so RCU_walk is
					* not permitted
					* not permitted.  If it progresses to
					* actual expiry attempt, the flag is
					* not cleared when EXPIRING is set -
					* in that case it gets cleared only
					* when it comes to clearing EXPIRING.
					*/
#define AUTOFS_INF_PENDING	(1<<2) /* dentry pending mount */

+8 −19
Original line number Diff line number Diff line
@@ -321,19 +321,17 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
	if (ino->flags & AUTOFS_INF_PENDING)
		goto out;
	if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
		ino->flags |= AUTOFS_INF_NO_RCU;
		ino->flags |= AUTOFS_INF_WANT_EXPIRE;
		spin_unlock(&sbi->fs_lock);
		synchronize_rcu();
		spin_lock(&sbi->fs_lock);
		if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
			ino->flags |= AUTOFS_INF_EXPIRING;
			smp_mb();
			ino->flags &= ~AUTOFS_INF_NO_RCU;
			init_completion(&ino->expire_complete);
			spin_unlock(&sbi->fs_lock);
			return root;
		}
		ino->flags &= ~AUTOFS_INF_NO_RCU;
		ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
	}
out:
	spin_unlock(&sbi->fs_lock);
@@ -452,7 +450,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
	while ((dentry = get_next_positive_subdir(dentry, root))) {
		spin_lock(&sbi->fs_lock);
		ino = autofs4_dentry_ino(dentry);
		if (ino->flags & AUTOFS_INF_NO_RCU)
		if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
			expired = NULL;
		else
			expired = should_expire(dentry, mnt, timeout, how);
@@ -461,7 +459,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
			continue;
		}
		ino = autofs4_dentry_ino(expired);
		ino->flags |= AUTOFS_INF_NO_RCU;
		ino->flags |= AUTOFS_INF_WANT_EXPIRE;
		spin_unlock(&sbi->fs_lock);
		synchronize_rcu();
		spin_lock(&sbi->fs_lock);
@@ -471,7 +469,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
			goto found;
		}

		ino->flags &= ~AUTOFS_INF_NO_RCU;
		ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
		if (expired != dentry)
			dput(expired);
		spin_unlock(&sbi->fs_lock);
@@ -482,17 +480,8 @@ found:
	DPRINTK("returning %p %.*s",
		expired, (int)expired->d_name.len, expired->d_name.name);
	ino->flags |= AUTOFS_INF_EXPIRING;
	smp_mb();
	ino->flags &= ~AUTOFS_INF_NO_RCU;
	init_completion(&ino->expire_complete);
	spin_unlock(&sbi->fs_lock);
	spin_lock(&sbi->lookup_lock);
	spin_lock(&expired->d_parent->d_lock);
	spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
	list_move(&expired->d_parent->d_subdirs, &expired->d_child);
	spin_unlock(&expired->d_lock);
	spin_unlock(&expired->d_parent->d_lock);
	spin_unlock(&sbi->lookup_lock);
	return expired;
}

@@ -503,7 +492,7 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
	int status;

	/* Block on any pending expire */
	if (!(ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU)))
	if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
		return 0;
	if (rcu_walk)
		return -ECHILD;
@@ -561,7 +550,7 @@ int autofs4_expire_run(struct super_block *sb,
	ino = autofs4_dentry_ino(dentry);
	/* avoid rapid-fire expire attempts if expiry fails */
	ino->last_used = now;
	ino->flags &= ~AUTOFS_INF_EXPIRING;
	ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
	complete_all(&ino->expire_complete);
	spin_unlock(&sbi->fs_lock);

@@ -589,7 +578,7 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
		spin_lock(&sbi->fs_lock);
		/* avoid rapid-fire expire attempts if expiry fails */
		ino->last_used = now;
		ino->flags &= ~AUTOFS_INF_EXPIRING;
		ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
		complete_all(&ino->expire_complete);
		spin_unlock(&sbi->fs_lock);
		dput(dentry);
+1 −1
Original line number Diff line number Diff line
@@ -459,7 +459,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
		 * a mount-trap.
		 */
		struct inode *inode;
		if (ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU))
		if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
			return 0;
		if (d_mountpoint(dentry))
			return 0;