/* stick the duplicate mount on the same expiry list
* as the original if that was on one */
if (flag & CL_EXPIRE) {
- spin_lock(&vfsmount_lock);
if (!list_empty(&old->mnt_expire))
list_add(&mnt->mnt_expire, &old->mnt_expire);
- spin_unlock(&vfsmount_lock);
}
}
return mnt;
m = mnt->mnt_parent;
mnt->mnt_mountpoint = mnt->mnt_root;
mnt->mnt_parent = mnt;
+ m->mnt_ghosts--;
spin_unlock(&vfsmount_lock);
dput(dentry);
mntput(m);
__touch_mnt_namespace(p->mnt_ns);
p->mnt_ns = NULL;
list_del_init(&p->mnt_child);
- if (p->mnt_parent != p)
+ if (p->mnt_parent != p) {
+ p->mnt_parent->mnt_ghosts++;
p->mnt_mountpoint->d_mounted--;
+ }
change_mnt_propagation(p, MS_PRIVATE);
}
}
+static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts);
+
static int do_umount(struct vfsmount *mnt, int flags)
{
struct super_block *sb = mnt->mnt_sb;
spin_lock(&vfsmount_lock);
event++;
+ if (!(flags & MNT_DETACH))
+ shrink_submounts(mnt, &umount_list);
+
retval = -EBUSY;
if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
if (!list_empty(&mnt->mnt_list))
if (err)
goto out1;
- spin_lock(&vfsmount_lock);
/* if the mount is moved, it should no longer be expire
* automatically */
list_del_init(&old_nd.path.mnt->mnt_expire);
- spin_unlock(&vfsmount_lock);
out1:
mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
out:
if ((err = graft_tree(newmnt, nd)))
goto unlock;
- if (fslist) {
- /* add to the specified expiration list */
- spin_lock(&vfsmount_lock);
+ if (fslist) /* add to the specified expiration list */
list_add_tail(&newmnt->mnt_expire, fslist);
- spin_unlock(&vfsmount_lock);
- }
+
up_write(&namespace_sem);
return 0;
EXPORT_SYMBOL_GPL(do_add_mount);
-static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
- struct list_head *umounts)
-{
- spin_lock(&vfsmount_lock);
-
- /*
- * Check if mount is still attached, if not, let whoever holds it deal
- * with the sucker
- */
- if (mnt->mnt_parent == mnt) {
- spin_unlock(&vfsmount_lock);
- return;
- }
-
- /*
- * Check that it is still dead: the count should now be 2 - as
- * contributed by the vfsmount parent and the mntget above
- */
- if (!propagate_mount_busy(mnt, 2)) {
- /* delete from the namespace */
- touch_mnt_namespace(mnt->mnt_ns);
- list_del_init(&mnt->mnt_list);
- mnt->mnt_ns = NULL;
- umount_tree(mnt, 1, umounts);
- spin_unlock(&vfsmount_lock);
- } else {
- /*
- * Someone brought it back to life whilst we didn't have any
- * locks held so return it to the expiration list
- */
- list_add_tail(&mnt->mnt_expire, mounts);
- spin_unlock(&vfsmount_lock);
- }
-}
-
-/*
- * go through the vfsmounts we've just consigned to the graveyard to
- * - check that they're still dead
- * - delete the vfsmount from the appropriate namespace under lock
- * - dispose of the corpse
- */
-static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts)
-{
- struct mnt_namespace *ns;
- struct vfsmount *mnt;
-
- while (!list_empty(graveyard)) {
- LIST_HEAD(umounts);
- mnt = list_first_entry(graveyard, struct vfsmount, mnt_expire);
- list_del_init(&mnt->mnt_expire);
-
- /* don't do anything if the namespace is dead - all the
- * vfsmounts from it are going away anyway */
- ns = mnt->mnt_ns;
- if (!ns || !ns->root)
- continue;
- get_mnt_ns(ns);
-
- spin_unlock(&vfsmount_lock);
- down_write(&namespace_sem);
- expire_mount(mnt, mounts, &umounts);
- up_write(&namespace_sem);
- release_mounts(&umounts);
- mntput(mnt);
- put_mnt_ns(ns);
- spin_lock(&vfsmount_lock);
- }
-}
-
/*
* process a list of expirable mountpoints with the intent of discarding any
* mountpoints that aren't in use and haven't been touched since last we came
{
struct vfsmount *mnt, *next;
LIST_HEAD(graveyard);
+ LIST_HEAD(umounts);
if (list_empty(mounts))
return;
+ down_write(&namespace_sem);
spin_lock(&vfsmount_lock);
/* extract from the expiration list every vfsmount that matches the
*/
list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
if (!xchg(&mnt->mnt_expiry_mark, 1) ||
- atomic_read(&mnt->mnt_count) != 1)
+ propagate_mount_busy(mnt, 1))
continue;
-
- mntget(mnt);
list_move(&mnt->mnt_expire, &graveyard);
}
-
- expire_mount_list(&graveyard, mounts);
-
+ while (!list_empty(&graveyard)) {
+ mnt = list_first_entry(&graveyard, struct vfsmount, mnt_expire);
+ touch_mnt_namespace(mnt->mnt_ns);
+ umount_tree(mnt, 1, &umounts);
+ }
spin_unlock(&vfsmount_lock);
+ up_write(&namespace_sem);
+
+ release_mounts(&umounts);
}
EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
}
if (!propagate_mount_busy(mnt, 1)) {
- mntget(mnt);
list_move_tail(&mnt->mnt_expire, graveyard);
found++;
}
* process a list of expirable mountpoints with the intent of discarding any
* submounts of a specific parent mountpoint
*/
-void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts)
+static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts)
{
LIST_HEAD(graveyard);
- int found;
-
- spin_lock(&vfsmount_lock);
+ struct vfsmount *m;
/* extract submounts of 'mountpoint' from the expiration list */
- while ((found = select_submounts(mountpoint, &graveyard)) != 0)
- expire_mount_list(&graveyard, mounts);
-
- spin_unlock(&vfsmount_lock);
+ while (select_submounts(mnt, &graveyard)) {
+ while (!list_empty(&graveyard)) {
+ m = list_first_entry(&graveyard, struct vfsmount,
+ mnt_expire);
+ touch_mnt_namespace(mnt->mnt_ns);
+ umount_tree(mnt, 1, umounts);
+ }
+ }
}
-EXPORT_SYMBOL_GPL(shrink_submounts);
-
/*
* Some copy_from_user() implementations do not return the exact number of
* bytes remaining to copy on a fault. But copy_mount_options() requires that.