Skip to content

Commit

Permalink
VFS: Add shrink_submounts()
Browse files Browse the repository at this point in the history
Allow a submount to be marked as being 'shrinkable' by means of the
vfsmount->mnt_flags, and then add a function 'shrink_submounts()' which
attempts to recursively unmount these submounts.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Jun 9, 2006
1 parent 1f5ce9e commit 5528f91
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 25 deletions.
124 changes: 99 additions & 25 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1162,14 +1162,47 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
}
}

/*
* 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 namespace *namespace;
struct vfsmount *mnt;

while (!list_empty(graveyard)) {
LIST_HEAD(umounts);
mnt = list_entry(graveyard->next, 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 */
namespace = mnt->mnt_namespace;
if (!namespace || !namespace->root)
continue;
get_namespace(namespace);

spin_unlock(&vfsmount_lock);
down_write(&namespace_sem);
expire_mount(mnt, mounts, &umounts);
up_write(&namespace_sem);
release_mounts(&umounts);
mntput(mnt);
put_namespace(namespace);
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
* here
*/
void mark_mounts_for_expiry(struct list_head *mounts)
{
struct namespace *namespace;
struct vfsmount *mnt, *next;
LIST_HEAD(graveyard);

Expand All @@ -1193,38 +1226,79 @@ void mark_mounts_for_expiry(struct list_head *mounts)
list_move(&mnt->mnt_expire, &graveyard);
}

/*
* 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
*/
while (!list_empty(&graveyard)) {
LIST_HEAD(umounts);
mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire);
list_del_init(&mnt->mnt_expire);
expire_mount_list(&graveyard, mounts);

/* don't do anything if the namespace is dead - all the
* vfsmounts from it are going away anyway */
namespace = mnt->mnt_namespace;
if (!namespace || !namespace->root)
spin_unlock(&vfsmount_lock);
}

EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);

/*
* Ripoff of 'select_parent()'
*
* search the list of submounts for a given mountpoint, and move any
* shrinkable submounts to the 'graveyard' list.
*/
static int select_submounts(struct vfsmount *parent, struct list_head *graveyard)
{
struct vfsmount *this_parent = parent;
struct list_head *next;
int found = 0;

repeat:
next = this_parent->mnt_mounts.next;
resume:
while (next != &this_parent->mnt_mounts) {
struct list_head *tmp = next;
struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child);

next = tmp->next;
if (!(mnt->mnt_flags & MNT_SHRINKABLE))
continue;
get_namespace(namespace);
/*
* Descend a level if the d_mounts list is non-empty.
*/
if (!list_empty(&mnt->mnt_mounts)) {
this_parent = mnt;
goto repeat;
}

spin_unlock(&vfsmount_lock);
down_write(&namespace_sem);
expire_mount(mnt, mounts, &umounts);
up_write(&namespace_sem);
release_mounts(&umounts);
mntput(mnt);
put_namespace(namespace);
spin_lock(&vfsmount_lock);
if (!propagate_mount_busy(mnt, 1)) {
mntget(mnt);
list_move_tail(&mnt->mnt_expire, graveyard);
found++;
}
}
/*
* All done at this level ... ascend and resume the search
*/
if (this_parent != parent) {
next = this_parent->mnt_child.next;
this_parent = this_parent->mnt_parent;
goto resume;
}
return 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)
{
LIST_HEAD(graveyard);
int found;

spin_lock(&vfsmount_lock);

/* extract submounts of 'mountpoint' from the expiration list */
while ((found = select_submounts(mountpoint, &graveyard)) != 0)
expire_mount_list(&graveyard, mounts);

spin_unlock(&vfsmount_lock);
}

EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
EXPORT_SYMBOL_GPL(shrink_submounts);

/*
* Some copy_from_user() implementations do not return the exact number of
Expand Down
3 changes: 3 additions & 0 deletions include/linux/mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#define MNT_NOATIME 0x08
#define MNT_NODIRATIME 0x10

#define MNT_SHRINKABLE 0x100

#define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */
#define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */
#define MNT_PNODE_MASK 0x3000 /* propogation flag mask */
Expand Down Expand Up @@ -84,6 +86,7 @@ extern int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
int mnt_flags, struct list_head *fslist);

extern void mark_mounts_for_expiry(struct list_head *mounts);
extern void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts);

extern spinlock_t vfsmount_lock;
extern dev_t name_to_dev_t(char *name);
Expand Down

0 comments on commit 5528f91

Please sign in to comment.