lkml.org 
[lkml]   [2004]   [Oct]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH 6/28] VFS: Make expiry recursive
Date
From
This patch allows for tagging of vfsmounts as being part of a sub-tree
expiry. It introduces a new vfsmount flag, MNT_CHILDEXPIRE which is used to
let the system know that the given mountpoint expires with its parent. This
is a recursive definition.

mnt_expiry, the call used to specify that a mount should expire, now takes an
int described as follows:
- 0 - The mountpoint should not expire (default)
- >0 - The value is used to specify the amount of idle time before the
given mountpoint expires.
- <0 - The mountpoint must expire with it's immediate parent. (parent
must be set to expire, or must be itself be marked to expire
along with _its_ parent.

This allows atomic expiry of a complex hierarchy of mountpoints. This means
userspace will either 'see' or 'not see' the hierarchy of mountpoints. (This
required when using a generic automount facility that acts like a mounted
filesystem on top of any other filesystem).

Signed-off-by: Mike Waychison <michael.waychison@sun.com>
---

fs/namespace.c | 236 +++++++++++++++++++++++++++++++++++++-------------
include/linux/mount.h | 3
2 files changed, 179 insertions(+), 60 deletions(-)

Index: linux-2.6.9-quilt/include/linux/mount.h
===================================================================
--- linux-2.6.9-quilt.orig/include/linux/mount.h 2004-10-22 17:17:35.377086008 -0400
+++ linux-2.6.9-quilt/include/linux/mount.h 2004-10-22 17:17:35.927002408 -0400
@@ -17,6 +17,7 @@
#define MNT_NOSUID 1
#define MNT_NODEV 2
#define MNT_NOEXEC 4
+#define MNT_CHILDEXPIRE 8

struct vfsmount
{
@@ -71,7 +72,7 @@ extern struct vfsmount *do_kern_mount(co
struct nameidata;

extern int do_graft_mount(struct vfsmount *newmnt, struct nameidata *nd);
-extern void mnt_expire(struct vfsmount *mnt, unsigned expire);
+extern int mnt_expire(struct vfsmount *mnt, int expire);

extern spinlock_t vfsmount_lock;

Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
--- linux-2.6.9-quilt.orig/fs/namespace.c 2004-10-22 17:17:35.378085856 -0400
+++ linux-2.6.9-quilt/fs/namespace.c 2004-10-22 17:17:35.929002104 -0400
@@ -157,6 +157,34 @@ static struct vfsmount *next_mnt(struct
return list_entry(next, struct vfsmount, mnt_child);
}

+static int __can_expire(struct vfsmount *root, int offset)
+{
+ struct vfsmount *mnt;
+ int count;
+
+ /* handle the case of a root or orphaned mountpoint */
+ if (root->mnt_parent == root || root->mnt_parent == NULL)
+ return 0;
+ count = atomic_read(&root->mnt_count) - 1 - offset;
+ for (mnt = next_mnt(root, root); mnt; mnt = next_mnt(mnt, root)) {
+ if (!(mnt->mnt_flags & MNT_CHILDEXPIRE))
+ return 0;
+ count += atomic_read(&mnt->mnt_count) - 2;
+ }
+
+ WARN_ON(count < 0);
+ return count == 0;
+}
+
+static int can_expire(struct vfsmount *root)
+{
+ int ret;
+ spin_lock(&vfsmount_lock);
+ ret = __can_expire(root, 1);
+ spin_unlock(&vfsmount_lock);
+ return ret;
+}
+
static struct vfsmount *
clone_mnt(struct vfsmount *old, struct dentry *root)
{
@@ -164,20 +192,13 @@ clone_mnt(struct vfsmount *old, struct d
struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);

if (mnt) {
- mnt->mnt_flags = old->mnt_flags;
+ mnt->mnt_flags = old->mnt_flags & ~MNT_CHILDEXPIRE;
atomic_inc(&sb->s_active);
mnt->mnt_sb = sb;
mnt->mnt_root = dget(root);
mnt->mnt_mountpoint = mnt->mnt_root;
mnt->mnt_parent = mnt;
mnt->mnt_namespace = old->mnt_namespace;
-
- /* stick the duplicate mount on the same expiry list
- * as the original if that was on one */
- spin_lock(&vfsmount_lock);
- if (!list_empty(&old->mnt_expire))
- list_add(&mnt->mnt_expire, &old->mnt_expire);
- spin_unlock(&vfsmount_lock);
}
return mnt;
}
@@ -275,6 +296,43 @@ struct seq_operations mounts_op = {
.show = show_vfsmnt
};

+/*
+ * Clear out MNT_CHILDEXPIRE from the given mountpoint (recursively) to ensure
+ * that our invariant that nodes that have MNT_CHILDEXPIRE set recursivly have
+ * a parent that will eventually expire.
+ */
+static void clear_childexpire(struct vfsmount *root)
+{
+ struct list_head *next;
+ struct vfsmount *this_parent = root;
+
+ if (!(root->mnt_flags & MNT_CHILDEXPIRE))
+ return;
+
+ root->mnt_flags &= ~MNT_CHILDEXPIRE;
+ next = this_parent->mnt_mounts.next;
+again:
+ for ( ; next != &this_parent->mnt_mounts ; next = next->next ) {
+ struct vfsmount *p = list_entry(next, struct vfsmount,
+ mnt_child);
+
+ if (p->mnt_flags & MNT_CHILDEXPIRE) {
+ p->mnt_flags &= ~MNT_CHILDEXPIRE;
+ if (!list_empty(&p->mnt_mounts)) {
+ this_parent = p;
+ next = this_parent->mnt_mounts.next;
+ continue;
+ }
+ }
+ }
+
+ if (this_parent != root) {
+ next = this_parent->mnt_child.next;
+ this_parent = this_parent->mnt_parent;
+ goto again;
+ }
+}
+
/**
* may_umount_tree - check if a mount tree is busy
* @mnt: root of mount tree
@@ -347,6 +405,17 @@ int may_umount(struct vfsmount *mnt)

EXPORT_SYMBOL(may_umount);

+/* clear all expire related information in the subtree rooted at root */
+static void clear_expire(struct vfsmount *root)
+{
+ struct vfsmount *p;
+
+ for (p = root; p; p = next_mnt(p, root)) {
+ list_del_init(&p->mnt_expire);
+ p->mnt_flags &= ~MNT_CHILDEXPIRE;
+ }
+}
+
static void umount_tree(struct vfsmount *mnt)
{
struct vfsmount *p;
@@ -394,7 +463,7 @@ static int do_umount(struct vfsmount *mn
flags & (MNT_FORCE | MNT_DETACH))
return -EINVAL;

- if (atomic_read(&mnt->mnt_count) != 2)
+ if (!can_expire(mnt))
return -EBUSY;

if (--mnt->mnt_expiry_countdown != 0)
@@ -455,9 +524,10 @@ static int do_umount(struct vfsmount *mn
spin_lock(&vfsmount_lock);
}
retval = -EBUSY;
- if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
+ if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH
+ || (flags & MNT_EXPIRE && can_expire(mnt))) {
if (!list_empty(&mnt->mnt_list)) {
- list_del_init(&mnt->mnt_expire);
+ clear_expire(mnt);
umount_tree(mnt);
}
retval = 0;
@@ -658,6 +728,7 @@ static int do_loopback(struct nameidata
/* stop bind mounts from expiring */
spin_lock(&vfsmount_lock);
list_del_init(&mnt->mnt_expire);
+ clear_childexpire(mnt);
spin_unlock(&vfsmount_lock);

err = graft_tree(mnt, nd);
@@ -698,7 +769,8 @@ static int do_remount(struct nameidata *
down_write(&sb->s_umount);
err = do_remount_sb(sb, flags, data, 0);
if (!err)
- nd->mnt->mnt_flags=mnt_flags;
+ nd->mnt->mnt_flags = mnt_flags |
+ (nd->mnt->mnt_flags & MNT_CHILDEXPIRE);
up_write(&sb->s_umount);
if (!err)
security_sb_post_remount(nd->mnt, flags, data);
@@ -754,9 +826,8 @@ static int do_move_mount(struct nameidat
detach_mnt(old_nd.mnt, &parent_nd);
attach_mnt(old_nd.mnt, nd);

- /* if the mount is moved, it should no longer be expire
- * automatically */
- list_del_init(&old_nd.mnt->mnt_expire);
+ /* if the mount is moved, we need to clear it's child expire flag */
+ clear_childexpire(old_nd.mnt);
out2:
spin_unlock(&vfsmount_lock);
out1:
@@ -829,8 +900,18 @@ unlock:
}
EXPORT_SYMBOL_GPL(do_graft_mount);

-void mnt_expire(struct vfsmount *mnt, unsigned expire)
+/*
+ * Change the expiry settings for a given mountpoint
+ * 0 - Disable expiry for a given mountpoint
+ * <0 - Set this mountpoint to be part of a sub-tree expiry
+ * >0 - This mountpoint will expire after expire ticks
+ *
+ * Returns zero on success.
+ */
+int mnt_expire(struct vfsmount *mnt, int expire)
{
+ int ret = 1;
+
down(&expiry_sem);
spin_lock(&vfsmount_lock);

@@ -841,17 +922,68 @@ void mnt_expire(struct vfsmount *mnt, un
if (!mnt->mnt_namespace)
goto out;

- list_del_init(&mnt->mnt_expire);
- mnt->mnt_expiry_ticks = mnt->mnt_expiry_countdown = expire;
- mnt->mnt_active = 1;
- if (expire > 0)
- list_add_tail(&mnt->mnt_expire, &expiry_list);
+ if (expire < 0) {
+ if (!list_empty(&mnt->mnt_expire))
+ goto out;
+ if (mnt->mnt_parent == mnt || mnt->mnt_parent == NULL)
+ goto out;
+ if (!(mnt->mnt_parent->mnt_flags & MNT_CHILDEXPIRE)
+ && list_empty(&mnt->mnt_parent->mnt_expire))
+ goto out;
+ mnt->mnt_flags |= MNT_CHILDEXPIRE;
+ } else {
+ if (mnt->mnt_flags & MNT_CHILDEXPIRE && expire)
+ goto out;
+ clear_childexpire(mnt);
+ list_del_init(&mnt->mnt_expire);
+ mnt->mnt_expiry_ticks = mnt->mnt_expiry_countdown = expire;
+ mnt->mnt_active = 1;
+ if (expire > 0)
+ list_add_tail(&mnt->mnt_expire, &expiry_list);
+ }
+ ret = 0;
+
out:
spin_unlock(&vfsmount_lock);
up(&expiry_sem);
+ return ret;
}
EXPORT_SYMBOL_GPL(mnt_expire);

+/* return the first ancestor mount that impacts expiry, or NULL if none do */
+static struct vfsmount *find_expiring_parent(struct vfsmount *mnt)
+{
+ struct vfsmount *p = mnt->mnt_parent;
+ /*
+ * one of three things is true:
+ * 1. all parents are normal mounts
+ * 2. parent is a simple expiry mount
+ * 3. parent is a CHILDEXPIRE -- walk up the tree until we get to
+ * a mount that fits case 1 or case 2
+ */
+ while (p && p->mnt_parent != p && p->mnt_flags & MNT_CHILDEXPIRE)
+ p = p->mnt_parent;
+ if (p && list_empty(&p->mnt_expire))
+ return NULL;
+ return mntget(p);
+}
+
+static void bump_expiry_counter(struct vfsmount *mnt, struct vfsmount *parent)
+{
+ int diff;
+
+ /*
+ * If the parent is set to expire, then its counter has been
+ * counting down. If it thinks it has been idle for longer than
+ * the child, we need to bump it up. This child has been used more
+ * recently than the parent, so the parent can only possibly be idle
+ * for as long as this child (or less).
+ */
+ diff = parent->mnt_expiry_ticks - mnt->mnt_expiry_ticks;
+ if (parent->mnt_expiry_countdown < diff)
+ parent->mnt_expiry_countdown = diff;
+}
+
/*
* 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
@@ -887,7 +1019,7 @@ static void do_expiry_run(void *nothing)
}
if (mnt->mnt_expiry_countdown >= 1)
mnt->mnt_expiry_countdown--;
- if (atomic_read(&mnt->mnt_count) == 2 && mnt->mnt_expiry_countdown == 0) {
+ if (__can_expire(mnt, 0) && mnt->mnt_expiry_countdown == 0) {
mntget(mnt);
list_move(&mnt->mnt_expire, &graveyard);
}
@@ -900,6 +1032,8 @@ static void do_expiry_run(void *nothing)
* - dispose of the corpse
*/
while (!list_empty(&graveyard)) {
+ struct vfsmount *parent = NULL;
+
mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire);
list_del_init(&mnt->mnt_expire);

@@ -914,50 +1048,33 @@ static void do_expiry_run(void *nothing)
down_write(&namespace->sem);
spin_lock(&vfsmount_lock);

- /* check that it is still dead: the count should now be 2 - as
- * contributed by the vfsmount parent and the mntget above */
- if (atomic_read(&mnt->mnt_count) == 2 && !mnt->mnt_active) {
- struct vfsmount *xdmnt;
- struct dentry *xdentry;
-
- /* delete from the namespace */
- list_del_init(&mnt->mnt_list);
- list_del_init(&mnt->mnt_child);
- list_del_init(&mnt->mnt_hash);
- mnt->mnt_mountpoint->d_mounted--;
-
- xdentry = mnt->mnt_mountpoint;
- mnt->mnt_mountpoint = mnt->mnt_root;
- xdmnt = mnt->mnt_parent;
- mnt->mnt_parent = mnt;
-
- spin_unlock(&vfsmount_lock);
-
- mntput(xdmnt);
- dput(xdentry);
+ if (!__can_expire(mnt, 1) || mnt->mnt_active) {
+ list_add_tail(&mnt->mnt_expire, &expiry_list);
+ } else {
+ parent = find_expiring_parent(mnt);
+ if (parent)
+ bump_expiry_counter(mnt, parent);
+ umount_tree(mnt);

- /* now lay it to rest if this was the last ref on the
- * superblock */
- if (atomic_read(&mnt->mnt_sb->s_active) == 1) {
- /* last instance - try to be smart */
- lock_kernel();
- DQUOT_OFF(mnt->mnt_sb);
- acct_auto_close(mnt->mnt_sb);
- unlock_kernel();
+ /* the parent may be expirable now */
+ if (parent && __can_expire(parent, 1) &&
+ parent->mnt_expiry_countdown == 0 &&
+ !parent->mnt_active) {
+ list_move_tail(&parent->mnt_expire, &graveyard);
+
+ /* The ref from find_expiring_parent is now used
+ * for the graveyard. Set the parent to NULL so
+ * that it isn't decremented by the _mntput
+ * below */
+ parent = NULL;
}
-
- mntput(mnt);
- } 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, &expiry_list);
- spin_unlock(&vfsmount_lock);
}

+ spin_unlock(&vfsmount_lock);
up_write(&namespace->sem);

_mntput(mnt);
+ _mntput(parent);
put_namespace(namespace);

spin_lock(&vfsmount_lock);
@@ -1472,6 +1589,7 @@ void __put_namespace(struct namespace *n

list_for_each_entry(mnt, &namespace->list, mnt_list) {
mnt->mnt_namespace = NULL;
+ mnt->mnt_flags &= ~MNT_CHILDEXPIRE;
list_del_init(&mnt->mnt_expire);
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 14:07    [W:0.049 / U:0.032 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site