Skip to content

Commit

Permalink
vfs: make may_umount_tree() mount propagation aware
Browse files Browse the repository at this point in the history
Now that autofs has namespace aware mounted checks the expire needs
changes to make it aware of mount propagation.

When checking for expiration may_umount_tree() checks only if the
given mount is in use. This leads to a callback to the automount
daemon to umount the mount which will fail if any propagated mounts
are in use.

To avoid this unnecessary call back may_umount_tree() needs to check
propagated mount trees also.

Link: http://lkml.kernel.org/r/148029913966.27779.10094416563067823851.stgit@pluto.themaw.net
Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
Ian Kent authored and sfrothwell committed Dec 9, 2016
1 parent 10f5831 commit 477b1b3
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 13 deletions.
4 changes: 2 additions & 2 deletions fs/autofs4/expire.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
goto done;
}

/* Update the expiry counter if fs is busy */
/* Update the expiry counter if fs is busy in any namespace */
if (!may_umount_tree(path.mnt)) {
struct autofs_info *ino;

Expand Down Expand Up @@ -191,7 +191,7 @@ static int autofs4_direct_busy(struct vfsmount *mnt,
{
pr_debug("top %p %pd\n", top, top);

/* If it's busy update the expiry counters */
/* If it's busy in any namespace update the expiry counters */
if (!may_umount_tree(mnt)) {
struct autofs_info *ino;

Expand Down
71 changes: 62 additions & 9 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,33 @@ const struct seq_operations mounts_op = {
};
#endif /* CONFIG_PROC_FS */

struct mnt_tree_refs {
struct mount *root;
unsigned int refs;
unsigned int min_refs;
};

static void mnt_get_tree_refs(struct mnt_tree_refs *mtr)
{
struct mount *mnt = mtr->root;
struct mount *p;

/*
* Each propagated tree contribues 2 * #mounts - 1 to
* the minimal reference count. But when a mount is
* umounted and connected the mount doesn't hold a
* reference to its parent so it contributes a single
* reference.
*/
for (p = mnt; p; p = next_mnt(p, mnt)) {
mtr->refs += mnt_get_count(p);
if (p == mnt || p->mnt.mnt_flags & MNT_UMOUNT)
mtr->min_refs++;
else
mtr->min_refs += 2;
}
}

/**
* may_umount_tree - check if a mount tree is busy
* @mnt: root of mount tree
Expand All @@ -1322,25 +1349,51 @@ const struct seq_operations mounts_op = {
int may_umount_tree(struct vfsmount *m)
{
struct mount *mnt = real_mount(m);
int actual_refs = 0;
int minimum_refs = 0;
struct mount *p;
struct mount *parent = mnt->mnt_parent;
struct mnt_tree_refs mtr;
struct mount *p, *child;

BUG_ON(!m);

/* write lock needed for mnt_get_count */
down_read(&namespace_sem);
lock_mount_hash();
for (p = mnt; p; p = next_mnt(p, mnt)) {
actual_refs += mnt_get_count(p);
minimum_refs += 2;

mtr.root = mnt;
mtr.refs = 0;
mtr.min_refs = 0;

mnt_get_tree_refs(&mtr);
/*
* Caller holds a mount reference so minimum references
* to the tree at mnt is one greater than the minumum
* references.
*/
mtr.min_refs++;

/* The pnode.c propagation_next() function (as used below)
* returns each mount propogated from a given mount. Using
* the parent of mnt and matching the mnt->mnt_mountpoint
* gets the list of mounts propogated from mnt. To work
* out if the tree is in use (eg. open file or pwd) the
* reference counts of each of these mounts needs to be
* checked as well as mnt itself.
*/
for (p = propagation_next(parent, parent); p;
p = propagation_next(p, parent)) {
child = __lookup_mnt_last(&p->mnt, mnt->mnt_mountpoint);
if (child) {
mtr.root = child;
mnt_get_tree_refs(&mtr);
}
}
unlock_mount_hash();
up_read(&namespace_sem);

if (actual_refs > minimum_refs)
if (mtr.refs > mtr.min_refs)
return 0;

return 1;
}

EXPORT_SYMBOL(may_umount_tree);

/**
Expand Down
3 changes: 1 addition & 2 deletions fs/pnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@ void change_mnt_propagation(struct mount *mnt, int type)
* vfsmount found while iterating with propagation_next() is
* a peer of one we'd found earlier.
*/
static struct mount *propagation_next(struct mount *m,
struct mount *origin)
struct mount *propagation_next(struct mount *m, struct mount *origin)
{
/* are there any slaves of this mount? */
if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
Expand Down
1 change: 1 addition & 0 deletions fs/pnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static inline void set_mnt_shared(struct mount *mnt)
mnt->mnt.mnt_flags |= MNT_SHARED;
}

struct mount *propagation_next(struct mount *, struct mount *);
void change_mnt_propagation(struct mount *, int);
int propagate_mnt(struct mount *, struct mountpoint *, struct mount *,
struct hlist_head *);
Expand Down

0 comments on commit 477b1b3

Please sign in to comment.