Commit 406fea79 authored by Al Viro's avatar Al Viro
Browse files

mount: separate the flags accessed only under namespace_sem



Several flags are updated and checked only under namespace_sem; we are
already making use of that when we are checking them without mount_lock,
but we have to hold mount_lock for all updates, which makes things
clumsier than they have to be.

Take MNT_SHARED, MNT_UNBINDABLE, MNT_MARKED and MNT_UMOUNT_CANDIDATE
into a separate field (->mnt_t_flags), renaming them to T_SHARED,
etc. to avoid confusion.  All accesses must be under namespace_sem.

That changes locking requirements for mnt_change_propagation() and
set_mnt_shared() - only namespace_sem is needed now.  The same goes
for SET_MNT_MARKED et.al.

There might be more flags moved from ->mnt_flags to that field;
this is just the initial set.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 493a4beb
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -453,11 +453,11 @@ original set.
So let's go for
	* original set ("set").  Linkage via mnt_list
	* undecided candidates ("candidates").  Subset of a list,
consisting of all its elements marked with a new flag (MNT_UMOUNT_CANDIDATE).
consisting of all its elements marked with a new flag (T_UMOUNT_CANDIDATE).
Initially all elements of the list will be marked that way; in the
end the list will become empty and no mounts will remain marked with
that flag.
	* Reuse MNT_MARKED for "has been already seen by trim_ancestors()".
	* Reuse T_MARKED for "has been already seen by trim_ancestors()".
	* anything in U that hadn't been in the original set - elements of
candidates will gradually be either discarded or moved there.  In other
words, it's the candidates we have already decided to unmount.	Its role
@@ -465,13 +465,13 @@ is reasonably close to the old "to_umount", so let's use that name.
Linkage via mnt_list.

For gather_candidates() we'll need to maintain both candidates (S -
set) and intersection of S with set.  Use MNT_UMOUNT_CANDIDATE for
set) and intersection of S with set.  Use T_UMOUNT_CANDIDATE for
all elements we encounter, putting the ones not already in the original
set into the list of candidates.  When we are done, strip that flag from
all elements of the original set.  That gives a cheap way to check
if element belongs to S (in gather_candidates) and to candidates
itself (at later stages).  Call that predicate is_candidate(); it would
be m->mnt_flags & MNT_UMOUNT_CANDIDATE.
be m->mnt_t_flags & T_UMOUNT_CANDIDATE.

All elements of the original set are marked with MNT_UMOUNT and we'll
need the same for elements added when joining the contents of to_umount
@@ -480,5 +480,5 @@ to to_umount; that's close to what the old 'umount_one' is doing, so
let's keep that name.  It also gives us another predicate we need -
"belongs to union of set and to_umount"; will_be_unmounted() for now.

Removals from the candidates list should strip both MNT_MARKED and
MNT_UMOUNT_CANDIDATE; call it remove_from_candidates_list().
Removals from the candidates list should strip both T_MARKED and
T_UMOUNT_CANDIDATE; call it remove_from_candidates_list().
+17 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ struct mount {
	struct list_head to_notify;	/* need to queue notification */
	struct mnt_namespace *prev_ns;	/* previous namespace (NULL if none) */
#endif
	int mnt_t_flags;		/* namespace_sem-protected flags */
	int mnt_id;			/* mount identifier, reused */
	u64 mnt_id_unique;		/* mount ID unique until reboot */
	int mnt_group_id;		/* peer group identifier */
@@ -93,6 +94,22 @@ struct mount {
	struct mount *overmount;	/* mounted on ->mnt_root */
} __randomize_layout;

enum {
	T_SHARED		= 1, /* mount is shared */
	T_UNBINDABLE		= 2, /* mount is unbindable */
	T_MARKED		= 4, /* internal mark for propagate_... */
	T_UMOUNT_CANDIDATE	= 8, /* for propagate_umount */

	/*
	 * T_SHARED_MASK is the set of flags that should be cleared when a
	 * mount becomes shared.  Currently, this is only the flag that says a
	 * mount cannot be bind mounted, since this is how we create a mount
	 * that shares events with another mount.  If you add a new T_*
	 * flag, consider how it interacts with shared mounts.
	 */
	T_SHARED_MASK	= T_UNBINDABLE,
};

#define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */

static inline struct mount *real_mount(struct vfsmount *mnt)
+0 −4
Original line number Diff line number Diff line
@@ -2917,10 +2917,8 @@ static int do_change_type(struct path *path, int ms_flags)
			goto out_unlock;
	}

	lock_mount_hash();
	for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
		change_mnt_propagation(m, type);
	unlock_mount_hash();

 out_unlock:
	namespace_unlock();
@@ -3409,9 +3407,7 @@ static int do_set_group(struct path *from_path, struct path *to_path)
	if (IS_MNT_SHARED(from)) {
		to->mnt_group_id = from->mnt_group_id;
		list_add(&to->mnt_share, &from->mnt_share);
		lock_mount_hash();
		set_mnt_shared(to);
		unlock_mount_hash();
	}

	err = 0;
+10 −12
Original line number Diff line number Diff line
@@ -112,7 +112,7 @@ static int do_make_slave(struct mount *mnt)
}

/*
 * vfsmount lock must be held for write
 * EXCL[namespace_sem]
 */
void change_mnt_propagation(struct mount *mnt, int type)
{
@@ -125,9 +125,9 @@ void change_mnt_propagation(struct mount *mnt, int type)
		list_del_init(&mnt->mnt_slave);
		mnt->mnt_master = NULL;
		if (type == MS_UNBINDABLE)
			mnt->mnt.mnt_flags |= MNT_UNBINDABLE;
			mnt->mnt_t_flags |= T_UNBINDABLE;
		else
			mnt->mnt.mnt_flags &= ~MNT_UNBINDABLE;
			mnt->mnt_t_flags &= ~T_UNBINDABLE;
	}
}

@@ -263,9 +263,9 @@ static int propagate_one(struct mount *m, struct mountpoint *dest_mp)
		return PTR_ERR(child);
	read_seqlock_excl(&mount_lock);
	mnt_set_mountpoint(m, dest_mp, child);
	read_sequnlock_excl(&mount_lock);
	if (m->mnt_master != dest_master)
		SET_MNT_MARK(m->mnt_master);
	read_sequnlock_excl(&mount_lock);
	last_dest = m;
	last_source = child;
	hlist_add_head(&child->mnt_hash, list);
@@ -322,13 +322,11 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
		} while (n != m);
	}
out:
	read_seqlock_excl(&mount_lock);
	hlist_for_each_entry(n, tree_list, mnt_hash) {
		m = n->mnt_parent;
		if (m->mnt_master != dest_mnt->mnt_master)
			CLEAR_MNT_MARK(m->mnt_master);
	}
	read_sequnlock_excl(&mount_lock);
	return ret;
}

@@ -447,7 +445,7 @@ void propagate_mount_unlock(struct mount *mnt)

static inline bool is_candidate(struct mount *m)
{
	return m->mnt.mnt_flags & MNT_UMOUNT_CANDIDATE;
	return m->mnt_t_flags & T_UMOUNT_CANDIDATE;
}

static inline bool will_be_unmounted(struct mount *m)
@@ -464,7 +462,7 @@ static void umount_one(struct mount *m, struct list_head *to_umount)

static void remove_from_candidate_list(struct mount *m)
{
	m->mnt.mnt_flags &= ~(MNT_MARKED | MNT_UMOUNT_CANDIDATE);
	m->mnt_t_flags &= ~(T_MARKED | T_UMOUNT_CANDIDATE);
	list_del_init(&m->mnt_list);
}

@@ -476,7 +474,7 @@ static void gather_candidates(struct list_head *set,
	list_for_each_entry(m, set, mnt_list) {
		if (is_candidate(m))
			continue;
		m->mnt.mnt_flags |= MNT_UMOUNT_CANDIDATE;
		m->mnt_t_flags |= T_UMOUNT_CANDIDATE;
		p = m->mnt_parent;
		q = propagation_next(p, p);
		while (q) {
@@ -494,7 +492,7 @@ static void gather_candidates(struct list_head *set,
					q = skip_propagation_subtree(q, p);
					continue;
				}
				child->mnt.mnt_flags |= MNT_UMOUNT_CANDIDATE;
				child->mnt_t_flags |= T_UMOUNT_CANDIDATE;
				if (!will_be_unmounted(child))
					list_add(&child->mnt_list, candidates);
			}
@@ -502,7 +500,7 @@ static void gather_candidates(struct list_head *set,
		}
	}
	list_for_each_entry(m, set, mnt_list)
		m->mnt.mnt_flags &= ~MNT_UMOUNT_CANDIDATE;
		m->mnt_t_flags &= ~T_UMOUNT_CANDIDATE;
}

/*
@@ -519,7 +517,7 @@ static void trim_ancestors(struct mount *m)
			return;
		SET_MNT_MARK(m);
		if (m != p->overmount)
			p->mnt.mnt_flags &= ~MNT_UMOUNT_CANDIDATE;
			p->mnt_t_flags &= ~T_UMOUNT_CANDIDATE;
	}
}

+11 −8
Original line number Diff line number Diff line
@@ -10,14 +10,14 @@
#include <linux/list.h>
#include "mount.h"

#define IS_MNT_SHARED(m) ((m)->mnt.mnt_flags & MNT_SHARED)
#define IS_MNT_SHARED(m) ((m)->mnt_t_flags & T_SHARED)
#define IS_MNT_SLAVE(m) ((m)->mnt_master)
#define IS_MNT_NEW(m) (!(m)->mnt_ns)
#define CLEAR_MNT_SHARED(m) ((m)->mnt.mnt_flags &= ~MNT_SHARED)
#define IS_MNT_UNBINDABLE(m) ((m)->mnt.mnt_flags & MNT_UNBINDABLE)
#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)
#define SET_MNT_MARK(m) ((m)->mnt.mnt_flags |= MNT_MARKED)
#define CLEAR_MNT_MARK(m) ((m)->mnt.mnt_flags &= ~MNT_MARKED)
#define CLEAR_MNT_SHARED(m) ((m)->mnt_t_flags &= ~T_SHARED)
#define IS_MNT_UNBINDABLE(m) ((m)->mnt_t_flags & T_UNBINDABLE)
#define IS_MNT_MARKED(m) ((m)->mnt_t_flags & T_MARKED)
#define SET_MNT_MARK(m) ((m)->mnt_t_flags |= T_MARKED)
#define CLEAR_MNT_MARK(m) ((m)->mnt_t_flags &= ~T_MARKED)
#define IS_MNT_LOCKED(m) ((m)->mnt.mnt_flags & MNT_LOCKED)

#define CL_EXPIRE    		0x01
@@ -28,10 +28,13 @@
#define CL_SHARED_TO_SLAVE	0x20
#define CL_COPY_MNT_NS_FILE	0x40

/*
 * EXCL[namespace_sem]
 */
static inline void set_mnt_shared(struct mount *mnt)
{
	mnt->mnt.mnt_flags &= ~MNT_SHARED_MASK;
	mnt->mnt.mnt_flags |= MNT_SHARED;
	mnt->mnt_t_flags &= ~T_SHARED_MASK;
	mnt->mnt_t_flags |= T_SHARED;
}

static inline bool peers(const struct mount *m1, const struct mount *m2)
Loading