From: Mike Waychison <michael.waychison@sun.com>
To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org
Cc: raven@themaw.net
Subject: [PATCH 6/28] VFS: Make expiry recursive
Date: Mon, 25 Oct 2004 10:41:31 -0400 [thread overview]
Message-ID: <1098715291724@sun.com> (raw)
In-Reply-To: <10987152612887@sun.com>
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);
}
next prev parent reply other threads:[~2004-10-25 14:44 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-10-25 14:38 [PATCH 0/28] Autofs NG Patchset 0.2 Mike Waychison
2004-10-25 14:39 ` [PATCH 1/28] VFS: Unexport umount_tree Mike Waychison
2004-10-25 14:39 ` [PATCH 2/28] VFS: mnt_fslink -> mnt_expire Mike Waychison
2004-10-25 14:40 ` [PATCH 3/28] VFS: Move expiry into vfs Mike Waychison
2004-10-25 14:40 ` [PATCH 4/28] VFS: Stat shouldn't stop expire Mike Waychison
2004-10-25 14:41 ` [PATCH 5/28] VFS: Make expiry timeout configurable Mike Waychison
2004-10-25 14:41 ` Mike Waychison [this message]
2004-10-25 14:42 ` [PATCH 7/28] AFS: Update AFS to use new expiry interface Mike Waychison
2004-10-25 14:42 ` [PATCH 8/28] VFS: Remove MNT_EXPIRE support Mike Waychison
2004-10-25 14:43 ` [PATCH 9/28] VFS: Give sane expiry semantics Mike Waychison
2004-10-25 14:43 ` [PATCH 10/28] VFS: Move next_mnt() Mike Waychison
2004-10-25 14:44 ` [PATCH 11/28] VFS: Allow for detachable subtrees Mike Waychison
2004-10-25 14:44 ` [PATCH 12/28] VFS: Remove (now bogus) check_mnt Mike Waychison
2004-10-25 14:45 ` [PATCH 13/28] VFS: Introduce soft reference counts Mike Waychison
2004-10-25 15:25 ` Christoph Hellwig
2004-10-25 15:35 ` [PATCH 14/28] VFS: Introduce Mountpoint file descriptors (resend) Mike Waychison
2004-10-25 17:20 ` [PATCH 13/28] VFS: Introduce soft reference counts Mika Penttilä
2004-10-25 17:25 ` Mike Waychison
2004-10-25 17:25 ` Mike Waychison
2004-10-25 17:52 ` Mika Penttilä
2004-10-25 17:52 ` Mika Penttilä
2004-10-25 17:56 ` [PATCH 11/28] VFS: Allow for detachable subtrees (resend) Mike Waychison
2004-10-25 15:09 ` [PATCH 12/28] VFS: Remove (now bogus) check_mnt Christoph Hellwig
2004-10-25 15:15 ` Mike Waychison
2004-10-25 15:04 ` [PATCH 8/28] VFS: Remove MNT_EXPIRE support Christoph Hellwig
2004-10-25 15:12 ` Mike Waychison
2004-10-25 15:16 ` Christoph Hellwig
2004-10-25 15:30 ` Mike Waychison
2004-10-25 17:16 ` Mike Waychison
2004-10-25 17:29 ` Mike Waychison
2004-10-25 15:04 ` [PATCH 6/28] VFS: Make expiry recursive Christoph Hellwig
2004-10-26 10:27 ` [PATCH 4/28] VFS: Stat shouldn't stop expire Christoph Hellwig
2004-10-27 18:36 ` Mike Waychison
2004-10-25 14:59 ` [PATCH 3/28] VFS: Move expiry into vfs Christoph Hellwig
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1098715291724@sun.com \
--to=michael.waychison@sun.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=raven@themaw.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.