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

take ->mnt_expire handling under mount_lock [read_seqlock_excl]



Doesn't take much massage, and we no longer need to make sure that
by the time of final mntput() the victim has been removed from the
list.  Makes life safer for ->d_automount() instances...

Rules:
	* all ->mnt_expire accesses are under mount_lock.
	* insertion into the list is done by mnt_set_expiry(), and
caller (->d_automount() instance) must hold a reference to mount
in question.  It shouldn't be done more than once for a mount.
	* if a mount on an expiry list is not yet mounted, it will
be ignored by anything that walks that list.
	* if the final mntput() finds its victim still on an expiry
list (in which case it must've never been mounted - umount_tree()
would've taken it out), it will remove the victim from the list.

Reviewed-by: default avatarChristian Brauner <brauner@kernel.org>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent a8c764e1
Loading
Loading
Loading
Loading
+11 −17
Original line number Diff line number Diff line
@@ -1353,13 +1353,6 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
		list_add(&mnt->mnt_slave, &old->mnt_slave);
		mnt->mnt_master = old->mnt_master;
	}

	/* stick the duplicate mount on the same expiry list
	 * as the original if that was on one */
	if (flag & CL_EXPIRE) {
		if (!list_empty(&old->mnt_expire))
			list_add(&mnt->mnt_expire, &old->mnt_expire);
	}
	return mnt;

 out_free:
@@ -1452,6 +1445,8 @@ static void mntput_no_expire(struct mount *mnt)
	rcu_read_unlock();

	list_del(&mnt->mnt_instance);
	if (unlikely(!list_empty(&mnt->mnt_expire)))
		list_del(&mnt->mnt_expire);

	if (unlikely(!list_empty(&mnt->mnt_mounts))) {
		struct mount *p, *tmp;
@@ -2273,6 +2268,13 @@ struct mount *copy_tree(struct mount *src_root, struct dentry *dentry,
			lock_mount_hash();
			if (src_mnt->mnt.mnt_flags & MNT_LOCKED)
				dst_mnt->mnt.mnt_flags |= MNT_LOCKED;
			if (unlikely(flag & CL_EXPIRE)) {
				/* stick the duplicate mount on the same expiry
				 * list as the original if that was on one */
				if (!list_empty(&src_mnt->mnt_expire))
					list_add(&dst_mnt->mnt_expire,
						 &src_mnt->mnt_expire);
			}
			list_add_tail(&dst_mnt->mnt_list, &res->mnt_list);
			attach_mnt(dst_mnt, dst_parent, src_parent->mnt_mp);
			unlock_mount_hash();
@@ -3891,12 +3893,6 @@ int finish_automount(struct vfsmount *m, const struct path *path)
	namespace_unlock();
	inode_unlock(dentry->d_inode);
discard:
	/* remove m from any expiration list it may be on */
	if (!list_empty(&mnt->mnt_expire)) {
		namespace_lock();
		list_del_init(&mnt->mnt_expire);
		namespace_unlock();
	}
	mntput(m);
	return err;
}
@@ -3908,11 +3904,9 @@ int finish_automount(struct vfsmount *m, const struct path *path)
 */
void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
{
	namespace_lock();

	read_seqlock_excl(&mount_lock);
	list_add_tail(&real_mount(mnt)->mnt_expire, expiry_list);

	namespace_unlock();
	read_sequnlock_excl(&mount_lock);
}
EXPORT_SYMBOL(mnt_set_expiry);