VFS: Add shrink_submounts()
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>
This commit is contained in:
Родитель
1f5ce9e93a
Коммит
5528f911b4
130
fs/namespace.c
130
fs/namespace.c
|
@ -1162,6 +1162,40 @@ 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
|
* 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
|
* mountpoints that aren't in use and haven't been touched since last we came
|
||||||
|
@ -1169,7 +1203,6 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
|
||||||
*/
|
*/
|
||||||
void mark_mounts_for_expiry(struct list_head *mounts)
|
void mark_mounts_for_expiry(struct list_head *mounts)
|
||||||
{
|
{
|
||||||
struct namespace *namespace;
|
|
||||||
struct vfsmount *mnt, *next;
|
struct vfsmount *mnt, *next;
|
||||||
LIST_HEAD(graveyard);
|
LIST_HEAD(graveyard);
|
||||||
|
|
||||||
|
@ -1193,39 +1226,80 @@ void mark_mounts_for_expiry(struct list_head *mounts)
|
||||||
list_move(&mnt->mnt_expire, &graveyard);
|
list_move(&mnt->mnt_expire, &graveyard);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
expire_mount_list(&graveyard, 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
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&vfsmount_lock);
|
spin_unlock(&vfsmount_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
|
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;
|
||||||
|
/*
|
||||||
|
* Descend a level if the d_mounts list is non-empty.
|
||||||
|
*/
|
||||||
|
if (!list_empty(&mnt->mnt_mounts)) {
|
||||||
|
this_parent = mnt;
|
||||||
|
goto repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(shrink_submounts);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some copy_from_user() implementations do not return the exact number of
|
* 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.
|
* bytes remaining to copy on a fault. But copy_mount_options() requires that.
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#define MNT_NOATIME 0x08
|
#define MNT_NOATIME 0x08
|
||||||
#define MNT_NODIRATIME 0x10
|
#define MNT_NODIRATIME 0x10
|
||||||
|
|
||||||
|
#define MNT_SHRINKABLE 0x100
|
||||||
|
|
||||||
#define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */
|
#define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */
|
||||||
#define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */
|
#define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */
|
||||||
#define MNT_PNODE_MASK 0x3000 /* propogation flag mask */
|
#define MNT_PNODE_MASK 0x3000 /* propogation flag mask */
|
||||||
|
@ -84,6 +86,7 @@ extern int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
|
||||||
int mnt_flags, struct list_head *fslist);
|
int mnt_flags, struct list_head *fslist);
|
||||||
|
|
||||||
extern void mark_mounts_for_expiry(struct list_head *mounts);
|
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 spinlock_t vfsmount_lock;
|
||||||
extern dev_t name_to_dev_t(char *name);
|
extern dev_t name_to_dev_t(char *name);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче