Skip to content

Commit

Permalink
fix EBUSY on umount() from MNT_SHRINKABLE
Browse files Browse the repository at this point in the history
We need the parents of victims alive until namespace_unlock() gets to
dput() of the (ex-)mountpoints.  However, that screws up the "is it
busy" checks in case when we have shrinkable mounts that need to be
killed.  Solution: go ahead and decrement refcounts of parents right
in umount_tree(), increment them again just before dropping rwsem in
namespace_unlock() (and let the loop in the end of namespace_unlock()
finally drop those references for good, as we do now).  Parents can't
get freed until we drop rwsem - at least one reference is kept until
then, both in case when parent is among the victims and when it is
not.  So they'll still be around when we get to namespace_unlock().

Cc: stable@vger.kernel.org # 3.12+
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Al Viro committed Aug 30, 2014
1 parent 88b368f commit 81b6b06
Showing 1 changed file with 6 additions and 0 deletions.
6 changes: 6 additions & 0 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,11 @@ static void namespace_unlock(void)
head.first->pprev = &head.first;
INIT_HLIST_HEAD(&unmounted);

/* undo decrements we'd done in umount_tree() */
hlist_for_each_entry(mnt, &head, mnt_hash)
if (mnt->mnt_ex_mountpoint.mnt)
mntget(mnt->mnt_ex_mountpoint.mnt);

up_write(&namespace_sem);

synchronize_rcu();
Expand Down Expand Up @@ -1268,6 +1273,7 @@ void umount_tree(struct mount *mnt, int how)
p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
if (mnt_has_parent(p)) {
put_mountpoint(p->mnt_mp);
mnt_add_count(p->mnt_parent, -1);
/* move the reference to mountpoint into ->mnt_ex_mountpoint */
p->mnt_ex_mountpoint.dentry = p->mnt_mountpoint;
p->mnt_ex_mountpoint.mnt = &p->mnt_parent->mnt;
Expand Down

0 comments on commit 81b6b06

Please sign in to comment.