VFS: Add shrink_submounts()
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 9 Jun 2006 13:34:17 +0000 (09:34 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 9 Jun 2006 13:34:17 +0000 (09:34 -0400)
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>
fs/namespace.c
include/linux/mount.h

index bf478addb852b70c1ec036e33100f93031c5dc6b..b22e469ab560220d4b070d5becc4ccb9fbbe26be 100644 (file)
@@ -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
  * 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)
 {
-       struct namespace *namespace;
        struct vfsmount *mnt, *next;
        LIST_HEAD(graveyard);
 
@@ -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
index aff68c3660f5e04c27fbb77297218587650cb341..9b4e0071b92ef4625b200e91a234c8b7473f8cdc 100644 (file)
@@ -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 */
@@ -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);