|
@@ -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
|