Skip to contentSkip to conversation

Autofs NG Patchset 0.2

PATCH In review Mike Waychison 3 participants 32 messages Oct 25, 2004 – Oct 27, 2004

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover Autofs NG Patchset 0.2

Hi All,

The following patchset (against 2.6.9) is a breakdown of all the changes required to support autofng (currently hosted at http://autofsng.bkbits.net/).
I'm posting this patchset to get more people's eyes on the code.

The series consists of core vfs changes as well as a couple small changes to
the call_usermodehelper interface. I've also sent the autofsng filesystem as a single patch to introduce it seperated from the rest of the changes.

This isn't ready for inclusion as there are bound to be errors and the interfaces described haven't settled.

Please review / test / comment / flame.

01-unexport_umount_tree.diff
- drop an export with no intree users

02-rename_mnt_fslink_mnt_expire.diff
- give vfsmount->mnt_fslink a more appropriate name

03-move_expiry_into_vfs.diff
- pull expiry stuff that was recently added to be contained within vfs

04-stat_on_root_shouldnt_stop_expire.diff
- don't let ops on the root of a mountpoint affect expiry timeouts

05-expiry_is_countdown.diff
- mountpoint expiry now has configurable timeouts

06-expiry_is_recursive.diff
- allow for atomic expiry of subtrees of mountpoints

07-update_kafs_automount_expiry.diff
- update AFS to use new expiry interface

08-drop_expire_umount_flag.diff
- drop unused old expiry interface (MNT_EXPIRE flag to umount(2))

09-expiry_semantics_bind.diff
- fix up the expiry semantics and document them

10-move_next_mnt.diff
- move next_mnt() in preparation for later patches

11-detachable_subtrees.diff
- allow subtrees of mountpoints to not be bound to a struct namespace

12-remove_check_mnt.diff
- remove the now bogus check_mnt calls (bogus with detachable_subtree.diff)

13-introduce_soft_ref_counts.diff
- introduce 'soft' reference counts that don't affect umount(2) == EBUSY semantics

14-introduce_mountfd.diff
- introduce the mountfd() syscall

15-mountfd_umounts.diff
- add unmount functionality to mountfd interface

16-mountfd_attach.diff
- add attach interface to mountfd interface

17-mountfd_walk.diff
- allow for userspace to walk a tree of mountfds

18-mountfd_read.diff
- allow for reading properties of a mountfd

19-mountfd_vfsexpire.diff
- give vfs expiry an interface through mountfds

20-call_usermodehelper_cb.diff
- add a way to have a caller of call_usermodehelper get a callback before execvce.

21-call_usermodehelper_execve_hack.diff
- quick hack that allows for execve to be called without having to define errno.

22-export_put_namespace.diff
- autofsng wants to call put_namespace. export it.

23-export_get_sb_pseudo.diff
- autofsng wants get_sb_pseudo.

24-follow_link_on_root.diff
- update follow_link logic to pass the right vfsmount to follow_link on root directory.

25-statfs_nofollow.diff
- hack - statfs doesn't follow symlink on last component.

26-umount_mnt_nofollow.diff
- allow umount to not follow symlink on last component.

27-temp_expiry_syscall.diff
- dummy interface to expiry testing

28-autofsng_support.diff
- big patch for autofsng.

#1 lore raw

PATCH 1/28 VFS: Unexport umount_tree

Unexport umount_tree. I don't see any in-kernel users of this call.

---

 fs/namespace.c            |    2 +-
 include/linux/namespace.h |    1 -
 2 files changed, 1 insertion(+), 2 deletions(-)
include/linux/namespace.h +0 -1
Index: linux-2.6.9-quilt/include/linux/namespace.h
===================================================================
@@ -12,7 +12,6 @@ struct namespace {
 	struct rw_semaphore	sem;
 };
 
-extern void umount_tree(struct vfsmount *);
 extern int copy_namespace(int, struct task_struct *);
 extern void __put_namespace(struct namespace *namespace);
 
fs/namespace.c +1 -1
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -338,7 +338,7 @@ int may_umount(struct vfsmount *mnt)
 
 EXPORT_SYMBOL(may_umount);
 
-void umount_tree(struct vfsmount *mnt)
+static void umount_tree(struct vfsmount *mnt)
 {
 	struct vfsmount *p;
 	LIST_HEAD(kill);
#2 lore raw

PATCH 2/28 VFS: mnt_fslink -> mnt_expire

This patch renames vfsmount->mnt_fslink to something a little more
descriptive: vfsmount->mnt_expire.

---

 fs/namespace.c        |   24 ++++++++++++------------
 include/linux/mount.h |    2 +-
 2 files changed, 13 insertions(+), 13 deletions(-)
include/linux/mount.h +1 -1
Index: linux-2.6.9-quilt/include/linux/mount.h
===================================================================
@@ -32,7 +32,7 @@ struct vfsmount
 	int mnt_expiry_mark;		/* true if marked for expiry */
 	char *mnt_devname;		/* Name of device e.g. /dev/dsk/hda1 */
 	struct list_head mnt_list;
-	struct list_head mnt_fslink;	/* link in fs-specific expiry list */
+	struct list_head mnt_expire;	/* link in fs-specific expiry list */
 	struct namespace *mnt_namespace; /* containing namespace */
 };
 
fs/namespace.c +12 -12
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -60,7 +60,7 @@ struct vfsmount *alloc_vfsmnt(const char
 		INIT_LIST_HEAD(&mnt->mnt_child);
 		INIT_LIST_HEAD(&mnt->mnt_mounts);
 		INIT_LIST_HEAD(&mnt->mnt_list);
-		INIT_LIST_HEAD(&mnt->mnt_fslink);
+		INIT_LIST_HEAD(&mnt->mnt_expire);
 		if (name) {
 			int size = strlen(name)+1;
 			char *newname = kmalloc(size, GFP_KERNEL);
@@ -166,8 +166,8 @@ clone_mnt(struct vfsmount *old, struct d
 		/* 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_fslink))
-			list_add(&mnt->mnt_fslink, &old->mnt_fslink);
+		if (!list_empty(&old->mnt_expire))
+			list_add(&mnt->mnt_expire, &old->mnt_expire);
 		spin_unlock(&vfsmount_lock);
 	}
 	return mnt;
@@ -351,7 +351,7 @@ static void umount_tree(struct vfsmount 
 	while (!list_empty(&kill)) {
 		mnt = list_entry(kill.next, struct vfsmount, mnt_list);
 		list_del_init(&mnt->mnt_list);
-		list_del_init(&mnt->mnt_fslink);
+		list_del_init(&mnt->mnt_expire);
 		if (mnt->mnt_parent == mnt) {
 			spin_unlock(&vfsmount_lock);
 		} else {
@@ -644,7 +644,7 @@ static int do_loopback(struct nameidata 
 	if (mnt) {
 		/* stop bind mounts from expiring */
 		spin_lock(&vfsmount_lock);
-		list_del_init(&mnt->mnt_fslink);
+		list_del_init(&mnt->mnt_expire);
 		spin_unlock(&vfsmount_lock);
 
 		err = graft_tree(mnt, nd);
@@ -743,7 +743,7 @@ static int do_move_mount(struct nameidat
 
 	/* if the mount is moved, it should no longer be expire
 	 * automatically */
-	list_del_init(&old_nd.mnt->mnt_fslink);
+	list_del_init(&old_nd.mnt->mnt_expire);
 out2:
 	spin_unlock(&vfsmount_lock);
 out1:
@@ -812,7 +812,7 @@ int do_add_mount(struct vfsmount *newmnt
 	if (err == 0 && fslist) {
 		/* add to the specified expiration list */
 		spin_lock(&vfsmount_lock);
-		list_add_tail(&newmnt->mnt_fslink, fslist);
+		list_add_tail(&newmnt->mnt_expire, fslist);
 		spin_unlock(&vfsmount_lock);
 	}
 
@@ -846,13 +846,13 @@ void mark_mounts_for_expiry(struct list_
 	 * - still marked for expiry (marked on the last call here; marks are
 	 *   cleared by mntput())
 	 */
-	list_for_each_entry_safe(mnt, next, mounts, mnt_fslink) {
+	list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
 		if (!xchg(&mnt->mnt_expiry_mark, 1) ||
 		    atomic_read(&mnt->mnt_count) != 1)
 			continue;
 
 		mntget(mnt);
-		list_move(&mnt->mnt_fslink, &graveyard);
+		list_move(&mnt->mnt_expire, &graveyard);
 	}
 
 	/*
@@ -862,8 +862,8 @@ void mark_mounts_for_expiry(struct list_
 	 * - dispose of the corpse
 	 */
 	while (!list_empty(&graveyard)) {
-		mnt = list_entry(graveyard.next, struct vfsmount, mnt_fslink);
-		list_del_init(&mnt->mnt_fslink);
+		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 */
@@ -913,7 +913,7 @@ void mark_mounts_for_expiry(struct list_
 			/* 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_fslink, mounts);
+			list_add_tail(&mnt->mnt_expire, mounts);
 			spin_unlock(&vfsmount_lock);
 		}
 
#3 lore raw

PATCH 3/28 VFS: Move expiry into vfs

This patch moves the recently added expiry functionality directly into the VFS layer. Doing this gives us a couple advantages:

- Allows for configurable timeouts using a single consolidated timer
- Keeps filesystems from having to each implement their own expiry logic
- Provides a generic interface that can be used for _any_ filesystem, as desired by user applications and/or the system admninistrator.

This patch implements expiry by having the VFS recursively register work to do. Checks are done for expiry every 1 second, so expiry is configurable to that granularity.

---

 fs/namespace.c        |   75 ++++++++++++++++++++++++++++++++++++--------------
 include/linux/mount.h |    6 +---
 2 files changed, 57 insertions(+), 24 deletions(-)
include/linux/mount.h +2 -4
Index: linux-2.6.9-quilt/include/linux/mount.h
===================================================================
@@ -68,10 +68,8 @@ extern struct vfsmount *do_kern_mount(co
 
 struct nameidata;
 
-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 int do_graft_mount(struct vfsmount *newmnt, struct nameidata *nd);
+extern void mnt_expire(struct vfsmount *mnt, unsigned expire);
 
 extern spinlock_t vfsmount_lock;
 
fs/namespace.c +55 -20

192 lines not on this page · raw message

#4 lore raw

PATCH 4/28 VFS: Stat shouldn't stop expire

This patch fixes the problem where if you have a mountpoint that is going to expire, it fails to expire before somebody keeps stat(2)ing the root of it's filesystem. For example, consider the case where a user has his home
directory automounted on /home/mikew. Some other user can keep the
filesystem mounted forever by simply calling ls(1) in /home, because the stat action resets the marker on each call.

---

 namei.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletion(-)
fs/namei.c +10 -1
Index: linux-2.6.9-quilt/fs/namei.c
===================================================================
@@ -275,7 +275,16 @@ int deny_write_access(struct file * file
 void path_release(struct nameidata *nd)
 {
 	dput(nd->dentry);
-	mntput(nd->mnt);
+	/*
+	 * In order to ensure that access to an automounted filesystems'
+	 * root does not reset it's expire counter, we check to see if the path
+	 * being released here is a mountpoint itself.  If it is, then we call
+	 * _mntput which leaves the expire counter alone.
+	 */
+	if (nd->mnt && nd->mnt->mnt_root == nd->dentry)
+		_mntput(nd->mnt);
+	else  
+		mntput(nd->mnt);
 }
 
 /*
#5 lore raw

PATCH 5/28 VFS: Make expiry timeout configurable

This patch modifies the expiry logic to make timeouts configurable. We do this by using an atomic counter that ticks downwards every second of non-usage. We also store a per vfsmount value of what this timer gets reset to on every mntput.

---

 fs/namespace.c        |   33 +++++++++++++++++++++++----------
 include/linux/mount.h |    6 ++++--
 2 files changed, 27 insertions(+), 12 deletions(-)
include/linux/mount.h +4 -2
Index: linux-2.6.9-quilt/include/linux/mount.h
===================================================================
@@ -29,7 +29,9 @@ struct vfsmount
 	struct list_head mnt_child;	/* and going through their mnt_child */
 	atomic_t mnt_count;
 	int mnt_flags;
-	int mnt_expiry_mark;		/* true if marked for expiry */
+	int mnt_active;			/* flag set on mntput() */
+	int mnt_expiry_countdown;	/* countdown of ticks until expiry */
+	int mnt_expiry_ticks;		/* total ticks before expiry */
 	char *mnt_devname;		/* Name of device e.g. /dev/dsk/hda1 */
 	struct list_head mnt_list;
 	struct list_head mnt_expire;	/* link in fs-specific expiry list */
@@ -56,7 +58,7 @@ static inline void _mntput(struct vfsmou
 static inline void mntput(struct vfsmount *mnt)
 {
 	if (mnt) {
-		mnt->mnt_expiry_mark = 0;
+		xchg(&mnt->mnt_active, 1);
 		_mntput(mnt);
 	}
 }
fs/namespace.c +23 -10
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -68,6 +68,8 @@ struct vfsmount *alloc_vfsmnt(const char
 		INIT_LIST_HEAD(&mnt->mnt_mounts);
 		INIT_LIST_HEAD(&mnt->mnt_list);
 		INIT_LIST_HEAD(&mnt->mnt_expire);
+		mnt->mnt_expiry_ticks = mnt->mnt_expiry_countdown = 0;
+		mnt->mnt_active = 1;
 		if (name) {
 			int size = strlen(name)+1;
 			char *newname = kmalloc(size, GFP_KERNEL);
@@ -365,9 +367,9 @@ static void umount_tree(struct vfsmount 
 			struct nameidata old_nd;
 			detach_mnt(mnt, &old_nd);
 			spin_unlock(&vfsmount_lock);
-			path_release(&old_nd);
+			path_release_on_umount(&old_nd);
 		}
-		mntput(mnt);
+		_mntput(mnt);
 		spin_lock(&vfsmount_lock);
 	}
 }
@@ -395,7 +397,7 @@ static int do_umount(struct vfsmount *mn
 		if (atomic_read(&mnt->mnt_count) != 2)
 			return -EBUSY;
 
-		if (!xchg(&mnt->mnt_expiry_mark, 1))
+		if (--mnt->mnt_expiry_countdown != 0)
 			return -EAGAIN;
 	}
 
@@ -840,6 +842,8 @@ void mnt_expire(struct vfsmount *mnt, un
 		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);
 out:
@@ -847,7 +851,7 @@ out:
 	up(&expiry_sem);
 }
 EXPORT_SYMBOL_GPL(mnt_expire);
-  
+
 /*
  * 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
@@ -872,12 +876,21 @@ static void do_expiry_run(void *nothing)
 	 *   cleared by mntput())
 	 */
 	list_for_each_entry_safe(mnt, next, &expiry_list, mnt_expire) {
-		if (!xchg(&mnt->mnt_expiry_mark, 1) ||
-		    atomic_read(&mnt->mnt_count) != 1)
+		/*
+		 * Something might still hold a reference to this mount. If
+		 * mnt_active is set, we can just move on.  If it gets set
+		 * after we xchg() here, we'll catch it on the graveyard list.
+		 */
+		if (xchg(&mnt->mnt_active, 0)) {
+			mnt->mnt_expiry_countdown = mnt->mnt_expiry_ticks;
 			continue;
-
-		mntget(mnt);
-		list_move(&mnt->mnt_expire, &graveyard);
+		}
+		if (mnt->mnt_expiry_countdown >= 1)
+			mnt->mnt_expiry_countdown--;
+		if (atomic_read(&mnt->mnt_count) == 2 && mnt->mnt_expiry_countdown == 0) {
+			mntget(mnt);
+			list_move(&mnt->mnt_expire, &graveyard);
+		}
 	}
 
 	/*
@@ -903,7 +916,7 @@ static void do_expiry_run(void *nothing)
 
 		/* 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) {
+		if (atomic_read(&mnt->mnt_count) == 2 && !mnt->mnt_active) {
 			struct vfsmount *xdmnt;
 			struct dentry *xdentry;
 
#6 lore raw

PATCH 6/28 VFS: Make expiry recursive

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).

---

 fs/namespace.c        |  236 +++++++++++++++++++++++++++++++++++++-------------
 include/linux/mount.h |    3 
 2 files changed, 179 insertions(+), 60 deletions(-)
include/linux/mount.h +2 -1
Index: linux-2.6.9-quilt/include/linux/mount.h
===================================================================
@@ -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;
 
fs/namespace.c +177 -59

347 lines not on this page · raw message

#7 lore raw

PATCH 7/28 AFS: Update AFS to use new expiry interface

Update kAFS to use the new mountpoint expire infrastructure.

---

 cmservice.c |    5 -----
 mntpt.c     |   34 +++++++---------------------------
 super.c     |    2 --
 3 files changed, 7 insertions(+), 34 deletions(-)
fs/afs/mntpt.c +7 -27
Index: linux-2.6.9-quilt/fs/afs/mntpt.c
===================================================================
@@ -43,16 +43,6 @@ struct inode_operations afs_mntpt_inode_
 	.getattr	= afs_inode_getattr,
 };
 
-static LIST_HEAD(afs_vfsmounts);
-
-static void afs_mntpt_expiry_timed_out(struct afs_timer *timer);
-
-struct afs_timer_ops afs_mntpt_expiry_timer_ops = {
-	.timed_out	= afs_mntpt_expiry_timed_out,
-};
-
-struct afs_timer afs_mntpt_expiry_timer;
-
 unsigned long afs_mntpt_expiry_timeout = 20;
 
 /*****************************************************************************/
@@ -252,9 +242,15 @@ static int afs_mntpt_follow_link(struct 
 
 	newnd = *nd;
 	newnd.dentry = dentry;
-	err = do_add_mount(newmnt, &newnd, 0, &afs_vfsmounts);
+	err = do_graft_mount(newmnt, &newnd);
 
 	if (!err) {
+		/*
+		 * We currently don't test to see if we were able to set 
+		 * expiry.
+		 */
+		mnt_expire(newmnt, afs_mntpt_expiry_timeout);
+
 		path_release(nd);
 		mntget(newmnt);
 		nd->mnt = newmnt;
@@ -265,19 +261,3 @@ static int afs_mntpt_follow_link(struct 
 	kleave(" = %d", err);
 	return err;
 } /* end afs_mntpt_follow_link() */
-
-/*****************************************************************************/
-/*
- * handle mountpoint expiry timer going off
- */
-static void afs_mntpt_expiry_timed_out(struct afs_timer *timer)
-{
-	kenter("");
-
-	mark_mounts_for_expiry(&afs_vfsmounts);
-
-	afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
-				afs_mntpt_expiry_timeout * HZ);
-
-	kleave("");
-} /* end afs_mntpt_expiry_timed_out() */
fs/afs/cmservice.c +0 -5
Index: linux-2.6.9-quilt/fs/afs/cmservice.c
===================================================================
@@ -306,9 +306,6 @@ int afscm_start(void)
 		ret = rxrpc_add_service(afs_transport, &AFSCM_service);
 		if (ret < 0)
 			goto kill;
-
-		afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
-					afs_mntpt_expiry_timeout * HZ);
 	}
 
 	afscm_usage++;
@@ -389,8 +386,6 @@ void afscm_stop(void)
 			spin_lock(&kafscmd_attention_lock);
 		}
 		spin_unlock(&kafscmd_attention_lock);
-
-		afs_kafstimod_del_timer(&afs_mntpt_expiry_timer);
 	}
 
 	up_write(&afscm_sem);
fs/afs/super.c +0 -2
Index: linux-2.6.9-quilt/fs/afs/super.c
===================================================================
@@ -78,8 +78,6 @@ int __init afs_fs_init(void)
 
 	_enter("");
 
-	afs_timer_init(&afs_mntpt_expiry_timer, &afs_mntpt_expiry_timer_ops);
-
 	/* create ourselves an inode cache */
 	atomic_set(&afs_count_active_inodes, 0);
 
#8 lore raw

PATCH 8/28 VFS: Remove MNT_EXPIRE support

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

Note: maybe make this bit a DO NOT USE bit?

---

 fs/namespace.c     |   45 +++++++++------------------------------------
 include/linux/fs.h |    1 -
 2 files changed, 9 insertions(+), 37 deletions(-)
include/linux/fs.h +0 -1
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -719,7 +719,6 @@ extern int send_sigurg(struct fown_struc
 
 #define MNT_FORCE	0x00000001	/* Attempt to forcibily umount */
 #define MNT_DETACH	0x00000002	/* Just detach from the tree */
-#define MNT_EXPIRE	0x00000004	/* Mark for expiry */
 
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
fs/namespace.c +9 -36

91 lines not on this page · raw message

#9 lore raw

PATCH 9/28 VFS: Give sane expiry semantics

This patch changes the semantics of expiry slightly when dealing with stuff like --move, --bind and --rbind. The newly added file Documentation/filesystems/expire_semantics.txt describes how it works.

---

 Documentation/filesystems/expire_semantics.txt |  184 +++++++++++++++++++++++++
 fs/namespace.c                                 |   26 ++-
 2 files changed, 202 insertions(+), 8 deletions(-)
fs/namespace.c +18 -8
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -178,13 +178,22 @@ static int can_expire(struct vfsmount *r
 }
 
 static struct vfsmount *
-clone_mnt(struct vfsmount *old, struct dentry *root)
+clone_mnt(struct vfsmount *old, struct dentry *root, int keep_expiry)
 {
 	struct super_block *sb = old->mnt_sb;
 	struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);
 
 	if (mnt) {
-		mnt->mnt_flags = old->mnt_flags & ~MNT_CHILDEXPIRE;
+		if (keep_expiry) {
+			mnt->mnt_flags = old->mnt_flags;
+			if (!list_empty(&old->mnt_expire)) {
+				spin_lock(&vfsmount_lock);
+				list_add_tail(&mnt->mnt_expire, &expiry_list);
+				spin_unlock(&vfsmount_lock);
+			}
+		} else {
+			mnt->mnt_flags = old->mnt_flags & ~MNT_CHILDEXPIRE;
+		}
 		atomic_inc(&sb->s_active);
 		mnt->mnt_sb = sb;
 		mnt->mnt_root = dget(root);
@@ -589,13 +598,14 @@ lives_below_in_same_fs(struct dentry *d,
 	}
 }
 
-static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
+static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
+		int keep_expiry)
 {
 	struct vfsmount *res, *p, *q, *r, *s;
 	struct list_head *h;
 	struct nameidata nd;
 
-	res = q = clone_mnt(mnt, dentry);
+	res = q = clone_mnt(mnt, dentry, keep_expiry);
 	if (!q)
 		goto Enomem;
 	q->mnt_mountpoint = mnt->mnt_mountpoint;
@@ -614,7 +624,7 @@ static struct vfsmount *copy_tree(struct
 			p = s;
 			nd.mnt = q;
 			nd.dentry = p->mnt_mountpoint;
-			q = clone_mnt(p, p->mnt_root);
+			q = clone_mnt(p, p->mnt_root, keep_expiry);
 			if (!q)
 				goto Enomem;
 			spin_lock(&vfsmount_lock);
@@ -692,9 +702,9 @@ static int do_loopback(struct nameidata 
 	if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) {
 		err = -ENOMEM;
 		if (recurse)
-			mnt = copy_tree(old_nd.mnt, old_nd.dentry);
+			mnt = copy_tree(old_nd.mnt, old_nd.dentry, 0);
 		else
-			mnt = clone_mnt(old_nd.mnt, old_nd.dentry);
+			mnt = clone_mnt(old_nd.mnt, old_nd.dentry, 0);
 	}
 
 	if (mnt) {
@@ -1197,7 +1207,7 @@ int copy_namespace(int flags, struct tas
 
 	down_write(&tsk->namespace->sem);
 	/* First pass: copy the tree topology */
-	new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root);
+	new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root, 1);
 	if (!new_ns->root) {
 		up_write(&tsk->namespace->sem);
 		kfree(new_ns);
Documentation/filesystems/expire_semantics.txt new file +184 -0

184 lines not on this page · raw message

#10 lore raw

PATCH 10/28 VFS: Move next_mnt()

This patch simply moves next_mnt in preparation for the next patch that implements detachable subtrees.

---

 namespace.c |   32 ++++++++++++++++----------------
 1 files changed, 16 insertions(+), 16 deletions(-)
fs/namespace.c +16 -16
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -121,6 +121,22 @@ static inline int check_mnt(struct vfsmo
 	return mnt->mnt_namespace == current->namespace;
 }
 
+static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
+{
+	struct list_head *next = p->mnt_mounts.next;
+	if (next == &p->mnt_mounts) {
+		while (1) {
+			if (p == root)
+				return NULL;
+			next = p->mnt_child.next;
+			if (next != &p->mnt_parent->mnt_mounts)
+				break;
+			p = p->mnt_parent;
+		}
+	}
+	return list_entry(next, struct vfsmount, mnt_child);
+}
+
 static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
 {
 	old_nd->dentry = mnt->mnt_mountpoint;
@@ -141,22 +157,6 @@ static void attach_mnt(struct vfsmount *
 	nd->dentry->d_mounted++;
 }
 
-static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
-{
-	struct list_head *next = p->mnt_mounts.next;
-	if (next == &p->mnt_mounts) {
-		while (1) {
-			if (p == root)
-				return NULL;
-			next = p->mnt_child.next;
-			if (next != &p->mnt_parent->mnt_mounts)
-				break;
-			p = p->mnt_parent;
-		}
-	}
-	return list_entry(next, struct vfsmount, mnt_child);
-}
-
 /* this expects the caller to hold vfsmount_lock */
 static int can_expire(struct vfsmount *root, int offset)
 {
#11 lore raw

PATCH 11/28 VFS: Allow for detachable subtrees

This patch adds support for having detached sub-tree of vfsmounts. Detached sub-trees of vfsmounts means we can now have trees that are not required to have an associated struct namespace holding them together. We call such a subtree data structure a 'group'.

This is implemented by associated a new state to struct vfsmount, MNT_ISBASE. This flag is used to determine whether or not a given vfsmount is the base of a group. When this flag is set, the vfsmount->mnt_group._count value stores a an atomic count that reflects the number of all references to vfsmounts in the tree, excluding those that used for group stitching (a child holds a reference to a parent and vice-versa). For any group of N vfsmounts, (N-1)*2 reference counts are used solely for holding them together. Therefor the invariant to this structure is: for any group with N vfsmounts, rooted at R, R->mnt_group._count = (i->mnt_count for all i=1..N vfsmounts) - ((N - 1)*2).

In order to not have to walk up the tree each time we mntput/mntget to manage this group count, we store a pointer to the base of the tree in vfsmount->mnt_group._base in each vfsmount where !(vfsmount->mnt_flags & MNT_ISBASE). (The exercise of showing that we do not need to hold a reference count to the base vfsmount in each node in the tree is left to the reader).

Because we now are holding maintaining the reference counts in two different places, they have to be synchronized in some way with actions that may invalidate the vfsmount->mnt_group._base relationship. The following implementation uses a rwlock called vfsmountref_lock along with an atomic_t group counter to do so. The semantics of this lock are as follows:

- To change the reference count of a vfsmount, one must take a read lock on vfsmountref_lock. This keeps the mnt_group._base pointer from changing and ensures that the ->mnt_count values are consistent with the vfsmnt_base(mnt)->mnt_group._count value.
- When a split or merge of the tree occurs, you need to take a write lock on vfsmountref_lock. This keeps people from messing with the reference counts and ensures nobody is walking the mnt_group._base pointers.
- To check to see if a given vfsmount is busy (->mnt_count == 1, implying it is a leaf), there is no need to take vfsmountref_lock. You'd only do so with vfsmount_lock held, so you are sure nobody else can walk into it (current behaviour).

The actions of splitting off/attaching a group of vfsmounts from/to a tree remains handled in detach_mnt and attach_mnt. These work as before, but now also take care of updating group counts.

When the group count for a given sub-tree hits zero, then we know that nobody is holding a reference count to _any_ of the vfsmounts within the tree. When this happens, we call __mntgroupput which unstitches the pieces of the tree, and frees them. For example, assume a user were to umount a filesystem from his namespace. A check would be made to see if the vfsmount in question had ->mnt_count == 2 (one for the parent->child reference and one for the calling code's reference). If so, the code will proceed to call detach_tree on the vfsmount. This call updates the base count on the namespace's group (which would go down by one due to the reference to the unmounted filesystem leaving it), and would mark the detached vfsmount with MNT_ISBASE with a ->mnt_group._count == 1. When the umount syscall them drops the locks and calls mntput on the given mountpoint, both it's mnt_count == 0 and mnt_group._count == 0. The later invokes a call to __mntgroupput which walks the group (which only has the one vfsmount) and calls __mntput on it to let it release it's resource, decrement sb->count and so on.

One of the interesting side effects of this is that now when we do a lazy umount of a tree of filesystems, we no longer need to break apart each element of the tree. It also allows us to implement a new interface that allows for having sub-trees of mountpoints that aren't associated with any namespace. This would allow for process-specific sandboxes of filesystems that are mounted yet not available in the process' namespace (implemented in a later patch using mountpoint file descriptors).

One of the downsides to this implementation is that we grab a read on a rwlock on _every_ mntget/mntput. I looked into using a brlock, but apparently those have been removed from the kernel a while ago. I've thought about how to do the same kind of synchronization using RCU, but haven't yet figured out how to apply it to this kind of data-structure. This patch hasn't been tested for scalability in any sense.

---

 fs/namespace.c        |  276 +++++++++++++++++++++++++++++++++++++++-----------
 fs/super.c            |    1 
 include/linux/mount.h |   46 +++++++-
 3 files changed, 263 insertions(+), 60 deletions(-)
fs/namespace.c +220 -56
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -38,6 +38,15 @@ static inline int sysfs_init(void)
 /* spinlock for vfsmount related operations, inplace of dcache_lock */
 spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
+/*
+ * spinlock for mnt_group entries.  A write lock indicates that a sub-tree is
+ * being attached/detached and all reference count changes must wait until this
+ * is done.  As such, any vfsmount reference count changes must hold a read
+ * lock on vfsmountref_lock.
+ */
+rwlock_t vfsmountref_lock __cacheline_aligned_in_smp = RW_LOCK_UNLOCKED;
+EXPORT_SYMBOL(vfsmountref_lock);
+
 static struct list_head *mount_hashtable;
 static int hash_mask, hash_bits;
 static kmem_cache_t *mnt_cache; 
@@ -62,7 +71,9 @@ struct vfsmount *alloc_vfsmnt(const char
 	struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL); 
 	if (mnt) {
 		memset(mnt, 0, sizeof(struct vfsmount));
+		mnt->mnt_flags = MNT_ISBASE;
 		atomic_set(&mnt->mnt_count,1);
+		atomic_set(&mnt->mnt_group.count, 1);
 		INIT_LIST_HEAD(&mnt->mnt_hash);
 		INIT_LIST_HEAD(&mnt->mnt_child);
 		INIT_LIST_HEAD(&mnt->mnt_mounts);
@@ -137,24 +148,141 @@ static struct vfsmount *next_mnt(struct 
 	return list_entry(next, struct vfsmount, mnt_child);
 }
 
+/*
+ * detach_mnt breaks apart mnt from its parent.  Caller is expected to call this
+ * with a reference held on mnt, as well as an empty nameidata struct.  Once mnt
+ * is detached, the caller is responsible for dropping the references in
+ * nameidata as well as a reference to mnt, on top of the caller-owned
+ * reference to mnt.
+ *
+ * Called with namespace->sem held and vfsmount_lock held (if appropriate)
+ */
 static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
 {
+	struct vfsmount *p;
+	long nmounts = 0, nrefs = 0;
+	long basecount;
+
+
+	/* save copy old mountpoint for delayed release */
+	old_nd->mnt    = mnt->mnt_parent;
 	old_nd->dentry = mnt->mnt_mountpoint;
-	old_nd->mnt = mnt->mnt_parent;
-	mnt->mnt_parent = mnt;
-	mnt->mnt_mountpoint = mnt->mnt_root;
+
+	/* undo mountpoint stitching */
 	list_del_init(&mnt->mnt_child);
 	list_del_init(&mnt->mnt_hash);
-	old_nd->dentry->d_mounted--;
+	mnt->mnt_parent = mnt;
+	mnt->mnt_mountpoint->d_mounted--;
+
+	/* messing with mount groups requires a write lock */
+	write_lock(&vfsmountref_lock);
+
+	/* mnt is now the root of it's own sub-tree */
+	mnt->mnt_flags |= MNT_ISBASE;
+
+	/* dissassociate sub-tree with parent namespace */
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		list_del_init(&p->mnt_list);
+		p->mnt_namespace = NULL;
+
+		/* point to mnt as the base of their sub-tree */
+		p->mnt_group.base = mnt;
+
+		/* count the number of mounts in the detached sub-tree */
+		nmounts++;
+
+		/* count the total number of refcounts in the sub-tree */
+		nrefs += atomic_read(&p->mnt_count);
+	}
+
+	/*
+	 * calculate the number of group-refs in the base sub-tree.
+	 *
+	 * Lemma: within a tree, 2 references are used for stitching
+	 * parent->child and child->parent for each child.
+	 * Therefore, the total count of references used for stitching in a tree
+	 * is the number of children times 2.  nmounts here represents the total
+	 * count of mountpoints, so nmounts - 1 is number of children within the
+	 * tree.
+	 * We subtract this value from nrefs to get the total number of refs not
+	 * used for stitching.
+	 */
+	basecount = nrefs - ((nmounts - 1) * 2);
+
+	/* set the new base counts in the child tree */
+	memset(&mnt->mnt_group.count, 0, sizeof(mnt->mnt_group.count));
+	atomic_set(&mnt->mnt_group.count, basecount);
+
+	/*
+	 * - the parent tree gets +1 because old_nd->mnt is the callers
+	 *   responsiblity to free.
+	 * - the parent tree gets another +1 because mnt is the callers
+	 *   responsibility to free as well.
+	 * These stem from the fact that we can't mntput the references
+	 * previously used for stitching because we are under spinlocks.
+	 */
+	atomic_sub(basecount - 2 , &vfsmnt_base(old_nd->mnt)->mnt_group.count);
+
+	write_unlock(&vfsmountref_lock);
 }
 
-static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
+/*
+ * attach_mnt attaches mountpoint mnt at the location given by nd.
+ * This should be called with a reference to mnt, as well as a reference to
+ * nd->mnt/dentry.  The reference to mnt is consumed by this call, however the
+ * references in nd are _not_ consumed.
+ *
+ * called with namespace->sem and vfsmount_lock held if appropriate
+ */
+static void attach_mnt (struct vfsmount *mnt, struct nameidata *nd)
 {
-	mnt->mnt_parent = mntget(nd->mnt);
+	struct vfsmount *p;
+	struct vfsmount *base;
+	struct namespace *namespace = nd->mnt->mnt_namespace;
+	long basecount;
+
+	/* do the stitching */
+	nd->dentry->d_mounted++;
+	mnt->mnt_parent     = mntget(nd->mnt);
 	mnt->mnt_mountpoint = dget(nd->dentry);
-	list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
 	list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);
-	nd->dentry->d_mounted++;
+	list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
+
+	/* messing with mount groups requires a write lock */
+	write_lock(&vfsmountref_lock);
+
+	/* get a reference to the base of the subtree */
+	/* (protected by vfsmountref_lock) */
+	base = vfsmnt_base(nd->mnt);
+
+	/* mnt is no longer the base of its own subtree */
+	mnt->mnt_flags &= ~MNT_ISBASE;
+
+	/*
+	 * calculate the new base count.
+	 *
+	 * -1 because mnt is consumed by stitching
+	 * -1 because nd->mnt is consumed by stitching
+	 */
+	basecount = atomic_read(&base->mnt_group.count)
+	          + atomic_read(&mnt->mnt_group.count)
+		  - 2;
+
+
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+
+		/* update the base pointers */
+		p->mnt_group.base = base;
+
+		/* chain in the namespace of the parenting subtree */
+		p->mnt_namespace = namespace;
+		if (namespace)
+			list_add_tail(&p->mnt_list, &namespace->list);
+	}
+
+	atomic_set(&base->mnt_group.count, basecount);
+
+	write_unlock(&vfsmountref_lock);
 }
 
 /* this expects the caller to hold vfsmount_lock */
@@ -194,12 +322,13 @@ clone_mnt(struct vfsmount *old, struct d
 		} else {
 			mnt->mnt_flags = old->mnt_flags & ~MNT_CHILDEXPIRE;
 		}
+		mnt->mnt_flags |= MNT_ISBASE;
 		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;
+		mnt->mnt_namespace = NULL;
 	}
 	return mnt;
 }
@@ -212,7 +341,49 @@ void __mntput(struct vfsmount *mnt)
 	deactivate_super(sb);
 }
 
-EXPORT_SYMBOL(__mntput);
+void __mntgroupput(struct vfsmount *mnt)
+{
+	struct vfsmount *p;
+	LIST_HEAD(kill);
+
+
+	/*
+	 * We don't need to grab vfsmount_lock here because we are assured
+	 * that no-one holds a reference to us.
+	 * The kill list ordering depends on next_mnt doing a pre-order
+	 * traversal.
+	 */
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		p->mnt_namespace = NULL;
+		list_del(&p->mnt_list);
+		list_add(&p->mnt_list, &kill);
+	}
+
+	while (!list_empty(&kill)) {
+		struct nameidata old_nd;
+
+
+		mnt = list_entry(kill.next, struct vfsmount, mnt_list);
+		WARN_ON(!list_empty(&mnt->mnt_mounts));
+
+		if (mnt == mnt->mnt_parent) {
+			WARN_ON(atomic_read(&mnt->mnt_count) != 0);
+			list_del_init(&mnt->mnt_list);
+			__mntput(mnt);
+		} else {
+			/* detach_mnt will clear mnt's mnt_list, however, we are
+			 * working on a depth_first pass, so this is okay */
+			detach_mnt(mnt, &old_nd);
+			/* we don't mntput because that would cause recursion */
+			atomic_dec(&old_nd.mnt->mnt_count);
+			dput(old_nd.dentry);
+			atomic_dec(&mnt->mnt_count);
+			WARN_ON(atomic_read(&mnt->mnt_count) != 0);
+			__mntput(mnt);
+		}
+	}
+}
+EXPORT_SYMBOL(__mntgroupput);
 
 /* iterator */
 static void *m_start(struct seq_file *m, loff_t *pos)
@@ -417,31 +588,18 @@ static void clear_expire(struct vfsmount
 	}
 }
 
+/* called with vfsmount_lock held */
 static void umount_tree(struct vfsmount *mnt)
 {
-	struct vfsmount *p;
-	LIST_HEAD(kill);
-
-	for (p = mnt; p; p = next_mnt(p, mnt)) {
-		list_del(&p->mnt_list);
-		list_add(&p->mnt_list, &kill);
-	}
-
-	while (!list_empty(&kill)) {
-		mnt = list_entry(kill.next, struct vfsmount, mnt_list);
-		list_del_init(&mnt->mnt_list);
-		list_del_init(&mnt->mnt_expire);
-		if (mnt->mnt_parent == mnt) {
-			spin_unlock(&vfsmount_lock);
-		} else {
-			struct nameidata old_nd;
-			detach_mnt(mnt, &old_nd);
-			spin_unlock(&vfsmount_lock);
-			path_release_on_umount(&old_nd);
-		}
-		_mntput(mnt);
-		spin_lock(&vfsmount_lock);
-	}
+	struct nameidata old_nd;
+	
+	list_del_init(&mnt->mnt_expire);
+	clear_childexpire(mnt);
+	detach_mnt(mnt, &old_nd);
+	spin_unlock(&vfsmount_lock);
+	path_release(&old_nd);	
+	mntput(mnt);
+	spin_lock(&vfsmount_lock);
 }
 
 static int do_umount(struct vfsmount *mnt, int flags)
@@ -506,14 +664,16 @@ static int do_umount(struct vfsmount *mn
 		security_sb_umount_close(mnt);
 		spin_lock(&vfsmount_lock);
 	}
+	retval = -EEXIST;
+	if (flags & MNT_DETACH && mnt->mnt_parent == mnt)
+		goto out;
 	retval = -EBUSY;
 	if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
-		if (!list_empty(&mnt->mnt_list)) {
-			clear_expire(mnt);
-			umount_tree(mnt);
-		}
+		clear_expire(mnt);
+		umount_tree(mnt);
 		retval = 0;
 	}
+out:
 	spin_unlock(&vfsmount_lock);
 	if (retval)
 		security_sb_umount_busy(mnt);
@@ -662,14 +822,23 @@ static int graft_tree(struct vfsmount *m
 	if (err)
 		goto out_unlock;
 
-	err = -ENOENT;
 	spin_lock(&vfsmount_lock);
+	err = -EEXIST;
+	if (mnt->mnt_parent != mnt) {
+		spin_unlock(&vfsmount_lock);
+		goto out_unlock;
+	}
+	read_lock(&vfsmountref_lock);
+	err = -ELOOP;
+	if (vfsmnt_base(nd->mnt) == mnt) {
+		read_unlock(&vfsmountref_lock);
+		spin_unlock(&vfsmount_lock);
+		goto out_unlock;
+	}
+	read_unlock(&vfsmountref_lock);
+	err = -ENOENT;
 	if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) {
-		struct list_head head;
-
 		attach_mnt(mnt, nd);
-		list_add_tail(&head, &mnt->mnt_list);
-		list_splice(&head, current->namespace->list.prev);
 		mntget(mnt);
 		err = 0;
 	}
@@ -753,7 +922,7 @@ static int do_remount(struct nameidata *
 	err = do_remount_sb(sb, flags, data, 0);
 	if (!err)
 		nd->mnt->mnt_flags = mnt_flags | 
-		    (nd->mnt->mnt_flags & MNT_CHILDEXPIRE);
+		    (nd->mnt->mnt_flags & (MNT_CHILDEXPIRE | MNT_ISBASE));
 	up_write(&sb->s_umount);
 	if (!err)
 		security_sb_post_remount(nd->mnt, flags, data);
@@ -843,7 +1012,7 @@ static int do_add_mount(struct nameidata
 	if (IS_ERR(mnt))
 		return PTR_ERR(mnt);
 
-	mnt->mnt_flags = mnt_flags;
+	mnt->mnt_flags = mnt_flags | MNT_ISBASE;
 	return do_graft_mount(mnt, nd);
 }
 
@@ -874,8 +1043,6 @@ int do_graft_mount(struct vfsmount *newm
 		goto unlock;
 
 	err = graft_tree(newmnt, nd);
-
-
 unlock:
 	up_write(&current->namespace->sem);
 	mntput(newmnt);
@@ -1213,9 +1380,9 @@ int copy_namespace(int flags, struct tas
 		kfree(new_ns);
 		goto out;
 	}
-	spin_lock(&vfsmount_lock);
 	list_add_tail(&new_ns->list, &new_ns->root->mnt_list);
-	spin_unlock(&vfsmount_lock);
+	list_for_each_entry(p, &new_ns->list, mnt_list)
+		p->mnt_namespace = new_ns;
 
 	/*
 	 * Second pass: switch the tsk->fs->* elements and mark new vfsmounts
@@ -1454,6 +1621,8 @@ asmlinkage long sys_pivot_root(const cha
 			goto out3;
 	} else if (!is_subdir(old_nd.dentry, new_nd.dentry))
 		goto out3;
+	mntget(user_nd.mnt);
+	mntget(new_nd.mnt);
 	detach_mnt(new_nd.mnt, &parent_nd);
 	detach_mnt(user_nd.mnt, &root_parent);
 	attach_mnt(user_nd.mnt, &old_nd);
@@ -1564,21 +1733,16 @@ void __init mnt_init(unsigned long mempa
 
 void __put_namespace(struct namespace *namespace)
 {
-	struct vfsmount *mnt;
-
 	down(&expiry_sem);
 	down_write(&namespace->sem);
 	spin_lock(&vfsmount_lock);
 
-	list_for_each_entry(mnt, &namespace->list, mnt_list) {
-		mnt->mnt_namespace = NULL;
-		mnt->mnt_flags &= ~MNT_CHILDEXPIRE;
-		list_del_init(&mnt->mnt_expire);
-	}
+	clear_expire(namespace->root);
 
-	umount_tree(namespace->root);
 	spin_unlock(&vfsmount_lock);
 	up_write(&namespace->sem);
 	up(&expiry_sem);
+
+	mntput(namespace->root);
 	kfree(namespace);
 }
include/linux/mount.h +43 -3

74 lines not on this page · raw message

fs/super.c +0 -1
Index: linux-2.6.9-quilt/fs/super.c
===================================================================
@@ -793,7 +793,6 @@ do_kern_mount(const char *fstype, int fl
 	mnt->mnt_root = dget(sb->s_root);
 	mnt->mnt_mountpoint = sb->s_root;
 	mnt->mnt_parent = mnt;
-	mnt->mnt_namespace = current->namespace;
 	up_write(&sb->s_umount);
 	put_filesystem(type);
 	return mnt;
#12 lore raw

PATCH 12/28 VFS: Remove (now bogus) check_mnt

check_mnt used to be used to see if a mountpoint was actually grafted or not to a namespace. This was done because we didn't support mountpoints being attached to one another if they weren't associated with a namespace. We now support this, so all check_mnt calls are bogus. The only exception is that pivot_root still requires all participants to exist within the same namespace.

---

 namespace.c |   41 +++++++++++------------------------------
 1 files changed, 11 insertions(+), 30 deletions(-)
fs/namespace.c +11 -30
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -124,14 +124,8 @@ struct vfsmount *lookup_mnt(struct vfsmo
 	spin_unlock(&vfsmount_lock);
 	return found;
 }
-
 EXPORT_SYMBOL(lookup_mnt);
 
-static inline int check_mnt(struct vfsmount *mnt)
-{
-	return mnt->mnt_namespace == current->namespace;
-}
-
 static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
 {
 	struct list_head *next = p->mnt_mounts.next;
@@ -701,8 +695,6 @@ asmlinkage long sys_umount(char __user *
 	retval = -EINVAL;
 	if (nd.dentry != nd.mnt->mnt_root)
 		goto dput_and_out;
-	if (!check_mnt(nd.mnt))
-		goto dput_and_out;
 
 	retval = -EPERM;
 	if (!capable(CAP_SYS_ADMIN))
@@ -867,14 +859,11 @@ static int do_loopback(struct nameidata 
 		return err;
 
 	down_write(&current->namespace->sem);
-	err = -EINVAL;
-	if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) {
-		err = -ENOMEM;
-		if (recurse)
-			mnt = copy_tree(old_nd.mnt, old_nd.dentry, 0);
-		else
-			mnt = clone_mnt(old_nd.mnt, old_nd.dentry, 0);
-	}
+	err = -ENOMEM;
+	if (recurse)
+		mnt = copy_tree(old_nd.mnt, old_nd.dentry, 0);
+	else
+		mnt = clone_mnt(old_nd.mnt, old_nd.dentry, 0);
 
 	if (mnt) {
 		/* stop bind mounts from expiring */
@@ -912,9 +901,6 @@ static int do_remount(struct nameidata *
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (!check_mnt(nd->mnt))
-		return -EINVAL;
-
 	if (nd->dentry != nd->mnt->mnt_root)
 		return -EINVAL;
 
@@ -945,9 +931,6 @@ static int do_move_mount(struct nameidat
 	down_write(&current->namespace->sem);
 	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
 		;
-	err = -EINVAL;
-	if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
-		goto out;
 
 	err = -ENOENT;
 	down(&nd->dentry->d_inode->i_sem);
@@ -984,7 +967,6 @@ out2:
 	spin_unlock(&vfsmount_lock);
 out1:
 	up(&nd->dentry->d_inode->i_sem);
-out:
 	up_write(&current->namespace->sem);
 	if (!err)
 		path_release(&parent_nd);
@@ -1028,9 +1010,6 @@ int do_graft_mount(struct vfsmount *newm
 	/* Something was mounted here while we slept */
 	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
 		;
-	err = -EINVAL;
-	if (!check_mnt(nd->mnt))
-		goto unlock;
 
 	/* Refuse the same filesystem on the same mount point */
 	err = -EBUSY;
@@ -1569,9 +1548,6 @@ asmlinkage long sys_pivot_root(const cha
 	error = __user_walk(new_root, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd);
 	if (error)
 		goto out0;
-	error = -EINVAL;
-	if (!check_mnt(new_nd.mnt))
-		goto out1;
 
 	error = __user_walk(put_old, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd);
 	if (error)
@@ -1589,9 +1565,14 @@ asmlinkage long sys_pivot_root(const cha
 	read_unlock(&current->fs->lock);
 	down_write(&current->namespace->sem);
 	down(&old_nd.dentry->d_inode->i_sem);
+	
+	/* All mountpoints must exist within the same namespace */
 	error = -EINVAL;
-	if (!check_mnt(user_nd.mnt))
+	if (user_nd.mnt->mnt_namespace != current->namespace
+	 || user_nd.mnt->mnt_namespace != old_nd.mnt->mnt_namespace
+	 || user_nd.mnt->mnt_namespace != new_nd.mnt->mnt_namespace)
 		goto out2;
+
 	error = -ENOENT;
 	if (IS_DEADDIR(new_nd.dentry->d_inode))
 		goto out2;
#13 lore raw

PATCH 13/28 VFS: Introduce soft reference counts

This patch introduces the concept of a 'soft' reference count for a vfsmount. This type of reference count allows for references to be held on mountpoints that do not affect their busy states for userland unmounting. Some might argue that this is wrong because 'when I unmount a filesystem, I want the resources associated with it to go away too', but this way of thinking was deprecated with the addition of namespaces and --bind back in the 2.4 series.

A future addition may see a callback mechanism so that in kernel users can use a given mountpoint and have it deregistered some way (quota and accounting come to mind).

These soft reference counts are used by a later patch that adds an interface for holding and manipulating mountpoints using filedescriptors.

---

 fs/namespace.c        |    4 ++++
 include/linux/mount.h |   28 ++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)
include/linux/mount.h +28 -0
Index: linux-2.6.9-quilt/include/linux/mount.h
===================================================================
@@ -30,6 +30,7 @@ struct vfsmount
 	struct list_head mnt_mounts;	/* list of children, anchored here */
 	struct list_head mnt_child;	/* and going through their mnt_child */
 	atomic_t mnt_count;
+	atomic_t mnt_softcount; 	/* hold reference w/o going busy */
 	union {
 		struct vfsmount *base;	/* pointer to root of vfsmount tree */
 		atomic_t count;		/* user ref count on this tree */
@@ -104,6 +105,33 @@ static inline void mntput(struct vfsmoun
 	}
 }
 
+static inline struct vfsmount *mntsoftget(struct vfsmount *mnt)
+{
+	if (mnt) {
+		read_lock(&vfsmountref_lock);
+		atomic_inc(&mnt->mnt_softcount);
+		mntgroupget(mnt);
+		read_unlock(&vfsmountref_lock);
+	}
+	return mnt;
+}
+
+static inline void mntsoftput(struct vfsmount *mnt)
+{
+	struct vfsmount *cleanup;
+	might_sleep();
+	if (mnt) {
+		if (atomic_dec_and_test(&mnt->mnt_count))
+			__mntput(mnt);
+		read_lock(&vfsmountref_lock);
+		cleanup = mntgroupput(mnt);
+		atomic_dec(&mnt->mnt_softcount);
+		read_unlock(&vfsmountref_lock);
+		if (cleanup)
+			__mntgroupput(cleanup);
+	}
+}
+
 extern void free_vfsmnt(struct vfsmount *mnt);
 extern struct vfsmount *alloc_vfsmnt(const char *name);
 extern struct vfsmount *do_kern_mount(const char *fstype, int flags,
fs/namespace.c +4 -0
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -73,6 +73,7 @@ struct vfsmount *alloc_vfsmnt(const char
 		memset(mnt, 0, sizeof(struct vfsmount));
 		mnt->mnt_flags = MNT_ISBASE;
 		atomic_set(&mnt->mnt_count,1);
+		atomic_set(&mnt->mnt_softcount,0);
 		atomic_set(&mnt->mnt_group.count, 1);
 		INIT_LIST_HEAD(&mnt->mnt_hash);
 		INIT_LIST_HEAD(&mnt->mnt_child);
@@ -187,6 +188,7 @@ static void detach_mnt(struct vfsmount *
 
 		/* count the total number of refcounts in the sub-tree */
 		nrefs += atomic_read(&p->mnt_count);
+		       + atomic_read(&p->mnt_softcount);
 	}
 
 	/*
@@ -362,6 +364,7 @@ void __mntgroupput(struct vfsmount *mnt)
 
 		if (mnt == mnt->mnt_parent) {
 			WARN_ON(atomic_read(&mnt->mnt_count) != 0);
+			WARN_ON(atomic_read(&mnt->mnt_softcount) != 0);
 			list_del_init(&mnt->mnt_list);
 			__mntput(mnt);
 		} else {
@@ -373,6 +376,7 @@ void __mntgroupput(struct vfsmount *mnt)
 			dput(old_nd.dentry);
 			atomic_dec(&mnt->mnt_count);
 			WARN_ON(atomic_read(&mnt->mnt_count) != 0);
+			WARN_ON(atomic_read(&mnt->mnt_softcount) != 0);
 			__mntput(mnt);
 		}
 	}
#14 lore raw

Re: [PATCH 13/28] VFS: Introduce soft reference counts

Quoted 7 lines · Mike Waychison

This patch introduces the concept of a 'soft' reference count for a vfsmount. This type of reference count allows for references to be held on mountpoints that do not affect their busy states for userland unmounting. Some might argue that this is wrong because 'when I unmount a filesystem, I want the resources associated with it to go away too', but this way of thinking was deprecated with the addition of namespaces and --bind back in the 2.4 series.

Quoted 6 lines

A future addition may see a callback mechanism so that in kernel users can use a given mountpoint and have it deregistered some way (quota and accounting come to mind).

These soft reference counts are used by a later patch that adds an interface for holding and manipulating mountpoints using filedescriptors.

You haven't explained why you actually need it, though.

#15 lore raw

Re: [PATCH 14/28] VFS: Introduce Mountpoint file descriptors (resend)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 2 lines · Christoph Hellwig

You haven't explained why you actually need it, though.

Apparently I used the wrong server and a couple patches bounced :\

I'll try to make the next series more 'forward self-describing' :) -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfR1RdQs4kOxk3/MRAkVnAKCJXfCmyzk1UaL0GcuPwQsdexHMhgCeMbGl Wz9LPh+FJbdMpyPPYiVrtoY=
=+WQa
-----END PGP SIGNATURE-----

This patch introduces the concept of a mountpoint file descriptor (mountfd). All interfacing with mountfds are done through the mountfd(2) syscall (which currently uses a command op argument to determine what you really are trying to do.

This patch only adds the following abilities:
- grab a reference to a mountfd given a directory fd (of the root of the mountpoint).
- get the directory fd of the root of the mountpoint given a mountfd.

NOTE: the entire interface is highly likely to change and comments/suggestions are more than welcome as the provided api is mostly a prototype at this stage and far from complete.

Further patches add the following functionalities:
- attach a mountpoint given a mountfd to a directory (given a dirfd)
- detach/umount a mountpoint given a mountfd (umount if not busy, forced umount, lazy umount)

NOTE AGAIN: most of the interface is half-baked, and really does require input from others. Most of this functionality is not neccesarily needed for autofsng, however I thought I'd start the ball rolling for _some_ interface.

---

 Documentation/ioctl-number.txt |    1 
 arch/i386/kernel/entry.S       |   17 ++
 fs/Makefile                    |    2 
 fs/mountfd.c                   |  256 +++++++++++++++++++++++++++++++++++++++++
 fs/namespace.c                 |    2 
 include/asm-i386/unistd.h      |    3 
 include/linux/fs.h             |    2 
 7 files changed, 281 insertions(+), 2 deletions(-)
arch/i386/kernel/entry.S +17 -0
Index: linux-2.6.9-quilt/arch/i386/kernel/entry.S
===================================================================
@@ -886,5 +886,22 @@ ENTRY(sys_call_table)
 	.long sys_mq_notify
 	.long sys_mq_getsetattr
 	.long sys_ni_syscall		/* reserved for kexec */
+	.long sys_ni_syscall
+	.long sys_ni_syscall		/* 285 */
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall		/* 290 */
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall		/* 295 */
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_mountfd		/* 300 */
 
 syscall_table_size=(.-sys_call_table)
include/asm-i386/unistd.h +2 -1
Index: linux-2.6.9-quilt/include/asm-i386/unistd.h
===================================================================
@@ -289,8 +289,9 @@
 #define __NR_mq_notify		(__NR_mq_open+4)
 #define __NR_mq_getsetattr	(__NR_mq_open+5)
 #define __NR_sys_kexec_load	283
+#define __NR_mountfd		300
 
-#define NR_syscalls 284
+#define NR_syscalls 301
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
fs/mountfd.c new file +256 -0

256 lines not on this page · raw message

fs/Makefile +1 -1
Index: linux-2.6.9-quilt/fs/Makefile
===================================================================
@@ -10,7 +10,7 @@ obj-y :=	open.o read_write.o file_table.
 		namei.o fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
 		dcache.o inode.o attr.o bad_inode.o file.o dnotify.o \
 		filesystems.o namespace.o seq_file.o xattr.o libfs.o \
-		fs-writeback.o mpage.o direct-io.o aio.o
+		fs-writeback.o mpage.o direct-io.o aio.o mountfd.o
 
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
 obj-$(CONFIG_COMPAT)		+= compat.o
fs/namespace.c +2 -0
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -34,6 +34,7 @@ static inline int sysfs_init(void)
 	return 0;
 }
 #endif
+extern void __init mfdfs_init(void);
 
 /* spinlock for vfsmount related operations, inplace of dcache_lock */
 spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
@@ -1711,6 +1712,7 @@ void __init mnt_init(unsigned long mempa
 		d++;
 		i--;
 	} while (i);
+	mfdfs_init();
 	sysfs_init();
 	init_rootfs();
 	init_mount_tree();
Documentation/ioctl-number.txt +1 -0
Index: linux-2.6.9-quilt/Documentation/ioctl-number.txt
===================================================================
@@ -144,6 +144,7 @@ Code	Seq#	Include File		Comments
 'p'	40-7F	linux/nvram.h
 'p'	80-9F				user-space parport
 					<mailto:[email protected]>
+'p'	A0-BF				mountpoint file descriptors
 'q'	00-1F	linux/videotext.h	conflict!
 'q'	80-FF				Internet PhoneJACK, Internet LineJACK
 					<http://www.quicknet.net>
include/linux/fs.h +2 -0
Index: linux-2.6.9-quilt/include/linux/fs.h
===================================================================
@@ -214,6 +214,8 @@ extern int leases_enable, dir_notify_ena
 #define FIBMAP	   _IO(0x00,1)	/* bmap access */
 #define FIGETBSZ   _IO(0x00,2)	/* get the block size used for bmap */
 
+#define MOUNTFD_IOC_GETDIRFD _IO('p', 0xa0)
+
 #ifdef __KERNEL__
 
 #include <linux/list.h>
#16 lore raw

Re: [PATCH 13/28] VFS: Introduce soft reference counts

In reply to Mike Waychison
Quoted 43 lines · Mike Waychison

This patch introduces the concept of a 'soft' reference count for a vfsmount. This type of reference count allows for references to be held on mountpoints that do not affect their busy states for userland unmounting. Some might argue that this is wrong because 'when I unmount a filesystem, I want the resources associated with it to go away too', but this way of thinking was deprecated with the addition of namespaces and --bind back in the 2.4 series.

A future addition may see a callback mechanism so that in kernel users can use a given mountpoint and have it deregistered some way (quota and accounting come to mind).

These soft reference counts are used by a later patch that adds an interface for holding and manipulating mountpoints using filedescriptors.

include/linux/mount.h +27 -0
+static inline struct vfsmount *mntsoftget(struct vfsmount *mnt)
+{
+	if (mnt) {
+		read_lock(&vfsmountref_lock);
+		atomic_inc(&mnt->mnt_softcount);
+		mntgroupget(mnt);
+		read_unlock(&vfsmountref_lock);
+	}
+	return mnt;
+}
+
+static inline void mntsoftput(struct vfsmount *mnt)
+{
+	struct vfsmount *cleanup;
+	might_sleep();
+	if (mnt) {
+		if (atomic_dec_and_test(&mnt->mnt_count))
+			__mntput(mnt);
+		read_lock(&vfsmountref_lock);
+		cleanup = mntgroupput(mnt);
+		atomic_dec(&mnt->mnt_softcount);
+		read_unlock(&vfsmountref_lock);
+		if (cleanup)
+			__mntgroupput(cleanup);
+	}
+}
+
Quoted 3 lines

extern void free_vfsmnt(struct vfsmount *mnt);

What is this against? What are mntgroupput and mntgroupget? Why does soft put decrement mnt_count which isn't increment by soft get? How do soft references allow userland umount? I don't see soft references used anywhere...

--Mika

#17 lore raw

Re: [PATCH 13/28] VFS: Introduce soft reference counts

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 47 lines · Mika Penttilä
Quoted 45 lines · Mike Waychison

This patch introduces the concept of a 'soft' reference count for a vfsmount.
This type of reference count allows for references to be held on mountpoints
that do not affect their busy states for userland unmounting. Some might argue that this is wrong because 'when I unmount a filesystem, I want the resources associated with it to go away too', but this way of thinking was
deprecated with the addition of namespaces and --bind back in the 2.4 series.

A future addition may see a callback mechanism so that in kernel users can
use a given mountpoint and have it deregistered some way (quota and accounting come to mind).

These soft reference counts are used by a later patch that adds an interface
for holding and manipulating mountpoints using filedescriptors.

include/linux/mount.h +23 -0
+static inline struct vfsmount *mntsoftget(struct vfsmount *mnt)
+{
+    if (mnt) {
+        read_lock(&vfsmountref_lock);
+        atomic_inc(&mnt->mnt_softcount);
+        mntgroupget(mnt);
+        read_unlock(&vfsmountref_lock);
+    }
+    return mnt;
+}
+
+static inline void mntsoftput(struct vfsmount *mnt)
+{
+    struct vfsmount *cleanup;
+    might_sleep();
+    if (mnt) {
+        if (atomic_dec_and_test(&mnt->mnt_count))
+            __mntput(mnt);
+        read_lock(&vfsmountref_lock);
+        cleanup = mntgroupput(mnt);
+        atomic_dec(&mnt->mnt_softcount);
+        read_unlock(&vfsmountref_lock);
+        if (cleanup)
Quoted 8 lines
Quoted 7 lines
include/linux/mount.h +4 -0
+            __mntgroupput(cleanup);
+    }
+}
+

extern void free_vfsmnt(struct vfsmount *mnt);

What is this against? What are mntgroupput and mntgroupget?

This is against patch [PATCH 11/28] VFS: Allow detachable subtrees.

In that patch, mntgroup(get|put) handles the count of all non-glue references for a given tree of vfsmounts.

Quoted 1 line

Why does soft put decrement mnt_count which isn't increment by soft get?

Ah, thanks for pointing that out. It got messed up when I created the patchset from the bk tree. Will fix.

Quoted 3 lines

How do
soft references allow userland umount? I don't see soft references used anywhere...

Soft references are used by the mountpoint file descriptor patch [14/28]. They allow references to be had on a vfsmount such that the mountpoint itself is not kept busy in the namespace. This allows a program to 'grab a mountpoint' by a magic file (gotten by sys_mountfd),
and perform ops on it. The mountfd holds a reference to the vfsmount,
but it doesn't keep userspace from trying to umount(2) the path.

Does that help?

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfTcNdQs4kOxk3/MRAnZwAKCTv7SMsT/+o4WLMJGapFVKURsbNwCeI+iF sZCOzRRNcnesK8rFN2haEww=
=eFwD
-----END PGP SIGNATURE-----
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#18 lore raw

Re: [PATCH 13/28] VFS: Introduce soft reference counts

Quoted 91 lines · Mike Waychison

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 51 lines · Mika Penttilä
Quoted 47 lines · Mike Waychison

This patch introduces the concept of a 'soft' reference count for a vfsmount.
This type of reference count allows for references to be held on mountpoints
that do not affect their busy states for userland unmounting. Some might argue that this is wrong because 'when I unmount a filesystem, I want the resources associated with it to go away too', but this way of thinking was
deprecated with the addition of namespaces and --bind back in the 2.4 series.

A future addition may see a callback mechanism so that in kernel users can
use a given mountpoint and have it deregistered some way (quota and accounting come to mind).

These soft reference counts are used by a later patch that adds an interface
for holding and manipulating mountpoints using filedescriptors.

include/linux/mount.h +25 -0
+static inline struct vfsmount *mntsoftget(struct vfsmount *mnt)
+{
+    if (mnt) {
+        read_lock(&vfsmountref_lock);
+        atomic_inc(&mnt->mnt_softcount);
+        mntgroupget(mnt);
+        read_unlock(&vfsmountref_lock);
+    }
+    return mnt;
+}
+
+static inline void mntsoftput(struct vfsmount *mnt)
+{
+    struct vfsmount *cleanup;
+    might_sleep();
+    if (mnt) {
+        if (atomic_dec_and_test(&mnt->mnt_count))
+            __mntput(mnt);
+        read_lock(&vfsmountref_lock);
+        cleanup = mntgroupput(mnt);
+        atomic_dec(&mnt->mnt_softcount);
+        read_unlock(&vfsmountref_lock);
+        if (cleanup)
+            __mntgroupput(cleanup);
+    }
Quoted 10 lines
Quoted 7 lines
(quoted diff) +2 -0
+}
+

extern void free_vfsmnt(struct vfsmount *mnt);

What is this against? What are mntgroupput and mntgroupget?

This is against patch [PATCH 11/28] VFS: Allow detachable subtrees.

In that patch, mntgroup(get|put) handles the count of all non-glue references for a given tree of vfsmounts.

Quoted 3 lines

Why does soft put decrement mnt_count which isn't increment by soft get?

Ah, thanks for pointing that out. It got messed up when I created the patchset from the bk tree. Will fix.

Quoted 5 lines

How do
soft references allow userland umount? I don't see soft references used anywhere...

Quoted 11 lines

Soft references are used by the mountpoint file descriptor patch [14/28]. They allow references to be had on a vfsmount such that the mountpoint itself is not kept busy in the namespace. This allows a program to 'grab a mountpoint' by a magic file (gotten by sys_mountfd),
and perform ops on it. The mountfd holds a reference to the vfsmount,
but it doesn't keep userspace from trying to umount(2) the path.

Does that help?

I think at least patches 11 and 14 got lost...

--Mika

-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#19 lore raw

PATCH 11/28 VFS: Allow for detachable subtrees (resend)

Quoted 1 line · Mika Penttilä

I think at least patches 11 and 14 got lost...

11 attached. 14 was resent as a reply to Christoph.

Sorry for the confusion.

This patch adds support for having detached sub-tree of vfsmounts. Detached sub-trees of vfsmounts means we can now have trees that are not required to have an associated struct namespace holding them together. We call such a subtree data structure a 'group'.

This is implemented by associated a new state to struct vfsmount, MNT_ISBASE. This flag is used to determine whether or not a given vfsmount is the base of a group. When this flag is set, the vfsmount->mnt_group._count value stores a an atomic count that reflects the number of all references to vfsmounts in the tree, excluding those that used for group stitching (a child holds a reference to a parent and vice-versa). For any group of N vfsmounts, (N-1)*2 reference counts are used solely for holding them together. Therefor the invariant to this structure is: for any group with N vfsmounts, rooted at R, R->mnt_group._count = (i->mnt_count for all i=1..N vfsmounts) - ((N - 1)*2).

In order to not have to walk up the tree each time we mntput/mntget to manage this group count, we store a pointer to the base of the tree in vfsmount->mnt_group._base in each vfsmount where !(vfsmount->mnt_flags & MNT_ISBASE). (The exercise of showing that we do not need to hold a reference count to the base vfsmount in each node in the tree is left to the reader).

Because we now are holding maintaining the reference counts in two different places, they have to be synchronized in some way with actions that may invalidate the vfsmount->mnt_group._base relationship. The following implementation uses a rwlock called vfsmountref_lock along with an atomic_t group counter to do so. The semantics of this lock are as follows:

- To change the reference count of a vfsmount, one must take a read lock on vfsmountref_lock. This keeps the mnt_group._base pointer from changing and ensures that the ->mnt_count values are consistent with the vfsmnt_base(mnt)->mnt_group._count value.
- When a split or merge of the tree occurs, you need to take a write lock on vfsmountref_lock. This keeps people from messing with the reference counts and ensures nobody is walking the mnt_group._base pointers.
- To check to see if a given vfsmount is busy (->mnt_count == 1, implying it is a leaf), there is no need to take vfsmountref_lock. You'd only do so with vfsmount_lock held, so you are sure nobody else can walk into it (current behaviour).

The actions of splitting off/attaching a group of vfsmounts from/to a tree remains handled in detach_mnt and attach_mnt. These work as before, but now also take care of updating group counts.

When the group count for a given sub-tree hits zero, then we know that nobody is holding a reference count to _any_ of the vfsmounts within the tree. When this happens, we call __mntgroupput which unstitches the pieces of the tree, and frees them. For example, assume a user were to umount a filesystem from his namespace. A check would be made to see if the vfsmount in question had ->mnt_count == 2 (one for the parent->child reference and one for the calling code's reference). If so, the code will proceed to call detach_tree on the vfsmount. This call updates the base count on the namespace's group (which would go down by one due to the reference to the unmounted filesystem leaving it), and would mark the detached vfsmount with MNT_ISBASE with a ->mnt_group._count == 1. When the umount syscall them drops the locks and calls mntput on the given mountpoint, both it's mnt_count == 0 and mnt_group._count == 0. The later invokes a call to __mntgroupput which walks the group (which only has the one vfsmount) and calls __mntput on it to let it release it's resource, decrement sb->count and so on.

One of the interesting side effects of this is that now when we do a lazy umount of a tree of filesystems, we no longer need to break apart each element of the tree. It also allows us to implement a new interface that allows for having sub-trees of mountpoints that aren't associated with any namespace. This would allow for process-specific sandboxes of filesystems that are mounted yet not available in the process' namespace (implemented in a later patch using mountpoint file descriptors).

One of the downsides to this implementation is that we grab a read on a rwlock on _every_ mntget/mntput. I looked into using a brlock, but apparently those have been removed from the kernel a while ago. I've thought about how to do the same kind of synchronization using RCU, but haven't yet figured out how to apply it to this kind of data-structure. This patch hasn't been tested for scalability in any sense.

---

 fs/namespace.c        |  276 +++++++++++++++++++++++++++++++++++++++-----------
 fs/super.c            |    1 
 include/linux/mount.h |   46 +++++++-
 3 files changed, 263 insertions(+), 60 deletions(-)
fs/namespace.c +220 -56
Index: linux-2.6.9-quilt/fs/namespace.c
===================================================================
@@ -38,6 +38,15 @@ static inline int sysfs_init(void)
 /* spinlock for vfsmount related operations, inplace of dcache_lock */
 spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
+/*
+ * spinlock for mnt_group entries.  A write lock indicates that a sub-tree is
+ * being attached/detached and all reference count changes must wait until this
+ * is done.  As such, any vfsmount reference count changes must hold a read
+ * lock on vfsmountref_lock.
+ */
+rwlock_t vfsmountref_lock __cacheline_aligned_in_smp = RW_LOCK_UNLOCKED;
+EXPORT_SYMBOL(vfsmountref_lock);
+
 static struct list_head *mount_hashtable;
 static int hash_mask, hash_bits;
 static kmem_cache_t *mnt_cache; 
@@ -62,7 +71,9 @@ struct vfsmount *alloc_vfsmnt(const char
 	struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL); 
 	if (mnt) {
 		memset(mnt, 0, sizeof(struct vfsmount));
+		mnt->mnt_flags = MNT_ISBASE;
 		atomic_set(&mnt->mnt_count,1);
+		atomic_set(&mnt->mnt_group.count, 1);
 		INIT_LIST_HEAD(&mnt->mnt_hash);
 		INIT_LIST_HEAD(&mnt->mnt_child);
 		INIT_LIST_HEAD(&mnt->mnt_mounts);
@@ -137,24 +148,141 @@ static struct vfsmount *next_mnt(struct 
 	return list_entry(next, struct vfsmount, mnt_child);
 }
 
+/*
+ * detach_mnt breaks apart mnt from its parent.  Caller is expected to call this
+ * with a reference held on mnt, as well as an empty nameidata struct.  Once mnt
+ * is detached, the caller is responsible for dropping the references in
+ * nameidata as well as a reference to mnt, on top of the caller-owned
+ * reference to mnt.
+ *
+ * Called with namespace->sem held and vfsmount_lock held (if appropriate)
+ */
 static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
 {
+	struct vfsmount *p;
+	long nmounts = 0, nrefs = 0;
+	long basecount;
+
+
+	/* save copy old mountpoint for delayed release */
+	old_nd->mnt    = mnt->mnt_parent;
 	old_nd->dentry = mnt->mnt_mountpoint;
-	old_nd->mnt = mnt->mnt_parent;
-	mnt->mnt_parent = mnt;
-	mnt->mnt_mountpoint = mnt->mnt_root;
+
+	/* undo mountpoint stitching */
 	list_del_init(&mnt->mnt_child);
 	list_del_init(&mnt->mnt_hash);
-	old_nd->dentry->d_mounted--;
+	mnt->mnt_parent = mnt;
+	mnt->mnt_mountpoint->d_mounted--;
+
+	/* messing with mount groups requires a write lock */
+	write_lock(&vfsmountref_lock);
+
+	/* mnt is now the root of it's own sub-tree */
+	mnt->mnt_flags |= MNT_ISBASE;
+
+	/* dissassociate sub-tree with parent namespace */
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		list_del_init(&p->mnt_list);
+		p->mnt_namespace = NULL;
+
+		/* point to mnt as the base of their sub-tree */
+		p->mnt_group.base = mnt;
+
+		/* count the number of mounts in the detached sub-tree */
+		nmounts++;
+
+		/* count the total number of refcounts in the sub-tree */
+		nrefs += atomic_read(&p->mnt_count);
+	}
+
+	/*
+	 * calculate the number of group-refs in the base sub-tree.
+	 *
+	 * Lemma: within a tree, 2 references are used for stitching
+	 * parent->child and child->parent for each child.
+	 * Therefore, the total count of references used for stitching in a tree
+	 * is the number of children times 2.  nmounts here represents the total
+	 * count of mountpoints, so nmounts - 1 is number of children within the
+	 * tree.
+	 * We subtract this value from nrefs to get the total number of refs not
+	 * used for stitching.
+	 */
+	basecount = nrefs - ((nmounts - 1) * 2);
+
+	/* set the new base counts in the child tree */
+	memset(&mnt->mnt_group.count, 0, sizeof(mnt->mnt_group.count));
+	atomic_set(&mnt->mnt_group.count, basecount);
+
+	/*
+	 * - the parent tree gets +1 because old_nd->mnt is the callers
+	 *   responsiblity to free.
+	 * - the parent tree gets another +1 because mnt is the callers
+	 *   responsibility to free as well.
+	 * These stem from the fact that we can't mntput the references
+	 * previously used for stitching because we are under spinlocks.
+	 */
+	atomic_sub(basecount - 2 , &vfsmnt_base(old_nd->mnt)->mnt_group.count);
+
+	write_unlock(&vfsmountref_lock);
 }
 
-static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
+/*
+ * attach_mnt attaches mountpoint mnt at the location given by nd.
+ * This should be called with a reference to mnt, as well as a reference to
+ * nd->mnt/dentry.  The reference to mnt is consumed by this call, however the
+ * references in nd are _not_ consumed.
+ *
+ * called with namespace->sem and vfsmount_lock held if appropriate
+ */
+static void attach_mnt (struct vfsmount *mnt, struct nameidata *nd)
 {
-	mnt->mnt_parent = mntget(nd->mnt);
+	struct vfsmount *p;
+	struct vfsmount *base;
+	struct namespace *namespace = nd->mnt->mnt_namespace;
+	long basecount;
+
+	/* do the stitching */
+	nd->dentry->d_mounted++;
+	mnt->mnt_parent     = mntget(nd->mnt);
 	mnt->mnt_mountpoint = dget(nd->dentry);
-	list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
 	list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);
-	nd->dentry->d_mounted++;
+	list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
+
+	/* messing with mount groups requires a write lock */
+	write_lock(&vfsmountref_lock);
+
+	/* get a reference to the base of the subtree */
+	/* (protected by vfsmountref_lock) */
+	base = vfsmnt_base(nd->mnt);
+
+	/* mnt is no longer the base of its own subtree */
+	mnt->mnt_flags &= ~MNT_ISBASE;
+
+	/*
+	 * calculate the new base count.
+	 *
+	 * -1 because mnt is consumed by stitching
+	 * -1 because nd->mnt is consumed by stitching
+	 */
+	basecount = atomic_read(&base->mnt_group.count)
+	          + atomic_read(&mnt->mnt_group.count)
+		  - 2;
+
+
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+
+		/* update the base pointers */
+		p->mnt_group.base = base;
+
+		/* chain in the namespace of the parenting subtree */
+		p->mnt_namespace = namespace;
+		if (namespace)
+			list_add_tail(&p->mnt_list, &namespace->list);
+	}
+
+	atomic_set(&base->mnt_group.count, basecount);
+
+	write_unlock(&vfsmountref_lock);
 }
 
 /* this expects the caller to hold vfsmount_lock */
@@ -194,12 +322,13 @@ clone_mnt(struct vfsmount *old, struct d
 		} else {
 			mnt->mnt_flags = old->mnt_flags & ~MNT_CHILDEXPIRE;
 		}
+		mnt->mnt_flags |= MNT_ISBASE;
 		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;
+		mnt->mnt_namespace = NULL;
 	}
 	return mnt;
 }
@@ -212,7 +341,49 @@ void __mntput(struct vfsmount *mnt)
 	deactivate_super(sb);
 }
 
-EXPORT_SYMBOL(__mntput);
+void __mntgroupput(struct vfsmount *mnt)
+{
+	struct vfsmount *p;
+	LIST_HEAD(kill);
+
+
+	/*
+	 * We don't need to grab vfsmount_lock here because we are assured
+	 * that no-one holds a reference to us.
+	 * The kill list ordering depends on next_mnt doing a pre-order
+	 * traversal.
+	 */
+	for (p = mnt; p; p = next_mnt(p, mnt)) {
+		p->mnt_namespace = NULL;
+		list_del(&p->mnt_list);
+		list_add(&p->mnt_list, &kill);
+	}
+
+	while (!list_empty(&kill)) {
+		struct nameidata old_nd;
+
+
+		mnt = list_entry(kill.next, struct vfsmount, mnt_list);
+		WARN_ON(!list_empty(&mnt->mnt_mounts));
+
+		if (mnt == mnt->mnt_parent) {
+			WARN_ON(atomic_read(&mnt->mnt_count) != 0);
+			list_del_init(&mnt->mnt_list);
+			__mntput(mnt);
+		} else {
+			/* detach_mnt will clear mnt's mnt_list, however, we are
+			 * working on a depth_first pass, so this is okay */
+			detach_mnt(mnt, &old_nd);
+			/* we don't mntput because that would cause recursion */
+			atomic_dec(&old_nd.mnt->mnt_count);
+			dput(old_nd.dentry);
+			atomic_dec(&mnt->mnt_count);
+			WARN_ON(atomic_read(&mnt->mnt_count) != 0);
+			__mntput(mnt);
+		}
+	}
+}
+EXPORT_SYMBOL(__mntgroupput);
 
 /* iterator */
 static void *m_start(struct seq_file *m, loff_t *pos)
@@ -417,31 +588,18 @@ static void clear_expire(struct vfsmount
 	}
 }
 
+/* called with vfsmount_lock held */
 static void umount_tree(struct vfsmount *mnt)
 {
-	struct vfsmount *p;
-	LIST_HEAD(kill);
-
-	for (p = mnt; p; p = next_mnt(p, mnt)) {
-		list_del(&p->mnt_list);
-		list_add(&p->mnt_list, &kill);
-	}
-
-	while (!list_empty(&kill)) {
-		mnt = list_entry(kill.next, struct vfsmount, mnt_list);
-		list_del_init(&mnt->mnt_list);
-		list_del_init(&mnt->mnt_expire);
-		if (mnt->mnt_parent == mnt) {
-			spin_unlock(&vfsmount_lock);
-		} else {
-			struct nameidata old_nd;
-			detach_mnt(mnt, &old_nd);
-			spin_unlock(&vfsmount_lock);
-			path_release_on_umount(&old_nd);
-		}
-		_mntput(mnt);
-		spin_lock(&vfsmount_lock);
-	}
+	struct nameidata old_nd;
+	
+	list_del_init(&mnt->mnt_expire);
+	clear_childexpire(mnt);
+	detach_mnt(mnt, &old_nd);
+	spin_unlock(&vfsmount_lock);
+	path_release(&old_nd);	
+	mntput(mnt);
+	spin_lock(&vfsmount_lock);
 }
 
 static int do_umount(struct vfsmount *mnt, int flags)
@@ -506,14 +664,16 @@ static int do_umount(struct vfsmount *mn
 		security_sb_umount_close(mnt);
 		spin_lock(&vfsmount_lock);
 	}
+	retval = -EEXIST;
+	if (flags & MNT_DETACH && mnt->mnt_parent == mnt)
+		goto out;
 	retval = -EBUSY;
 	if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
-		if (!list_empty(&mnt->mnt_list)) {
-			clear_expire(mnt);
-			umount_tree(mnt);
-		}
+		clear_expire(mnt);
+		umount_tree(mnt);
 		retval = 0;
 	}
+out:
 	spin_unlock(&vfsmount_lock);
 	if (retval)
 		security_sb_umount_busy(mnt);
@@ -662,14 +822,23 @@ static int graft_tree(struct vfsmount *m
 	if (err)
 		goto out_unlock;
 
-	err = -ENOENT;
 	spin_lock(&vfsmount_lock);
+	err = -EEXIST;
+	if (mnt->mnt_parent != mnt) {
+		spin_unlock(&vfsmount_lock);
+		goto out_unlock;
+	}
+	read_lock(&vfsmountref_lock);
+	err = -ELOOP;
+	if (vfsmnt_base(nd->mnt) == mnt) {
+		read_unlock(&vfsmountref_lock);
+		spin_unlock(&vfsmount_lock);
+		goto out_unlock;
+	}
+	read_unlock(&vfsmountref_lock);
+	err = -ENOENT;
 	if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) {
-		struct list_head head;
-
 		attach_mnt(mnt, nd);
-		list_add_tail(&head, &mnt->mnt_list);
-		list_splice(&head, current->namespace->list.prev);
 		mntget(mnt);
 		err = 0;
 	}
@@ -753,7 +922,7 @@ static int do_remount(struct nameidata *
 	err = do_remount_sb(sb, flags, data, 0);
 	if (!err)
 		nd->mnt->mnt_flags = mnt_flags | 
-		    (nd->mnt->mnt_flags & MNT_CHILDEXPIRE);
+		    (nd->mnt->mnt_flags & (MNT_CHILDEXPIRE | MNT_ISBASE));
 	up_write(&sb->s_umount);
 	if (!err)
 		security_sb_post_remount(nd->mnt, flags, data);
@@ -843,7 +1012,7 @@ static int do_add_mount(struct nameidata
 	if (IS_ERR(mnt))
 		return PTR_ERR(mnt);
 
-	mnt->mnt_flags = mnt_flags;
+	mnt->mnt_flags = mnt_flags | MNT_ISBASE;
 	return do_graft_mount(mnt, nd);
 }
 
@@ -874,8 +1043,6 @@ int do_graft_mount(struct vfsmount *newm
 		goto unlock;
 
 	err = graft_tree(newmnt, nd);
-
-
 unlock:
 	up_write(&current->namespace->sem);
 	mntput(newmnt);
@@ -1213,9 +1380,9 @@ int copy_namespace(int flags, struct tas
 		kfree(new_ns);
 		goto out;
 	}
-	spin_lock(&vfsmount_lock);
 	list_add_tail(&new_ns->list, &new_ns->root->mnt_list);
-	spin_unlock(&vfsmount_lock);
+	list_for_each_entry(p, &new_ns->list, mnt_list)
+		p->mnt_namespace = new_ns;
 
 	/*
 	 * Second pass: switch the tsk->fs->* elements and mark new vfsmounts
@@ -1454,6 +1621,8 @@ asmlinkage long sys_pivot_root(const cha
 			goto out3;
 	} else if (!is_subdir(old_nd.dentry, new_nd.dentry))
 		goto out3;
+	mntget(user_nd.mnt);
+	mntget(new_nd.mnt);
 	detach_mnt(new_nd.mnt, &parent_nd);
 	detach_mnt(user_nd.mnt, &root_parent);
 	attach_mnt(user_nd.mnt, &old_nd);
@@ -1564,21 +1733,16 @@ void __init mnt_init(unsigned long mempa
 
 void __put_namespace(struct namespace *namespace)
 {
-	struct vfsmount *mnt;
-
 	down(&expiry_sem);
 	down_write(&namespace->sem);
 	spin_lock(&vfsmount_lock);
 
-	list_for_each_entry(mnt, &namespace->list, mnt_list) {
-		mnt->mnt_namespace = NULL;
-		mnt->mnt_flags &= ~MNT_CHILDEXPIRE;
-		list_del_init(&mnt->mnt_expire);
-	}
+	clear_expire(namespace->root);
 
-	umount_tree(namespace->root);
 	spin_unlock(&vfsmount_lock);
 	up_write(&namespace->sem);
 	up(&expiry_sem);
+
+	mntput(namespace->root);
 	kfree(namespace);
 }
include/linux/mount.h +43 -3

74 lines not on this page · raw message

fs/super.c +0 -1
Index: linux-2.6.9-quilt/fs/super.c
===================================================================
@@ -793,7 +793,6 @@ do_kern_mount(const char *fstype, int fl
 	mnt->mnt_root = dget(sb->s_root);
 	mnt->mnt_mountpoint = sb->s_root;
 	mnt->mnt_parent = mnt;
-	mnt->mnt_namespace = current->namespace;
 	up_write(&sb->s_umount);
 	put_filesystem(type);
 	return mnt;
#20 lore raw

Re: [PATCH 12/28] VFS: Remove (now bogus) check_mnt

In reply to Mike Waychison
Quoted 6 lines · Mike Waychison

check_mnt used to be used to see if a mountpoint was actually grafted or not to a namespace. This was done because we didn't support mountpoints being attached to one another if they weren't associated with a namespace. We now support this, so all check_mnt calls are bogus. The only exception is that pivot_root still requires all participants to exist within the same namespace.

did you audit the namespace code that it doesn't allow attachign to other namespaces than the current?

#21 lore raw

Re: [PATCH 12/28] VFS: Remove (now bogus) check_mnt

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 10 lines · Christoph Hellwig
Quoted 6 lines · Mike Waychison

check_mnt used to be used to see if a mountpoint was actually grafted or not to a namespace. This was done because we didn't support mountpoints being attached to one another if they weren't associated with a namespace. We now support this, so all check_mnt calls are bogus. The only exception is that pivot_root still requires all participants to exist within the same namespace.

Quoted 3 lines

did you audit the namespace code that it doesn't allow attachign to other namespaces than the current?

So, I don't see how that is possible, other than through relative resolution from a cwd in the other namespace. Arguably, you aren't buying any security by denying the mountpoint if you already let other processes in your namespace.

Auditting the original code, it appeared that doing such a thing was a no-no only because the locking semantics of current->namespace->sem made this difficult.

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfRiVdQs4kOxk3/MRAmC2AJ93Dqcf1hNFjmjKESxsfuBeUqZ+nQCffEZX Ej3a3wyhQAwTg+amwHqn1v0=
=se6H
-----END PGP SIGNATURE-----

#22 lore raw

Re: [PATCH 8/28] VFS: Remove MNT_EXPIRE support

In reply to Mike Waychison
Quoted 2 lines · Mike Waychison

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

umm, this is a user API, you can't simply drop it.

#23 lore raw

Re: [PATCH 8/28] VFS: Remove MNT_EXPIRE support

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 8 lines · Christoph Hellwig
Quoted 2 lines · Mike Waychison

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

umm, this is a user API, you can't simply drop it.

Is anybody using it though?

Hmm. I'll think about it a while to figure out how to map this functionality to the new expire semantics. Any suggestions?

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfRfAdQs4kOxk3/MRAr3JAJ0U7qwTU1bnmcQy6R6BasFABNtoGQCZAdNZ wYQ8HClGafH+Tv1PKSJRnzM=
=sfq+
-----END PGP SIGNATURE-----

#24 lore raw

Re: [PATCH 8/28] VFS: Remove MNT_EXPIRE support

Quoted 6 lines · Mike Waychison

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 2 lines · Christoph Hellwig

On Mon, Oct 25, 2004 at 10:42:32AM -0400, Mike Waychison wrote:

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

umm, this is a user API, you can't simply drop it.

Is anybody using it though?

doesn't matter much. Maybe Sun likes deliberately breaking user ABIs in Solaris, but in Linux we certainly don't.

Quoted 2 lines

Hmm. I'll think about it a while to figure out how to map this functionality to the new expire semantics. Any suggestions?

Hey, it's you who wants the new semantics. And you didn't even explain them in detail.

#25 lore raw

Re: [PATCH 8/28] VFS: Remove MNT_EXPIRE support

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 14 lines · Christoph Hellwig
Quoted 12 lines · Mike Waychison

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 7 lines · Christoph Hellwig
Quoted 2 lines · Mike Waychison

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

umm, this is a user API, you can't simply drop it.

Is anybody using it though?

doesn't matter much. Maybe Sun likes deliberately breaking user ABIs in Solaris, but in Linux we certainly don't.

I wouldn't know, I only play with Linux ;)

Quoted 9 lines
Quoted 4 lines

Hmm. I'll think about it a while to figure out how to map this functionality to the new expire semantics. Any suggestions?

Hey, it's you who wants the new semantics. And you didn't even explain them in detail.

Hmm.. I think some bits of the series bounced. Let me look.

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfRv+dQs4kOxk3/MRAru8AJ9EPceeBwWKUzCAKoAcqkGxcU79gACfROFU uLiWsbIGSl0AVX3rMf9sdeI=
=dXgV
-----END PGP SIGNATURE-----

#26 lore raw

Re: [PATCH 8/28] VFS: Remove MNT_EXPIRE support

In reply to Christoph Hellwig

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 8 lines · Christoph Hellwig
Quoted 2 lines · Mike Waychison

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

umm, this is a user API, you can't simply drop it.

I also wanted to add that given the current interface that is found in mainline, there is no way for userspace to even set a mountpoint as expiring. The only consumer is still AFS which handles the mark_mounts_for_expiry stuff itself.

So even if userspace wanted to use MNT_EXPIRE, it couldn't.

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfTTUdQs4kOxk3/MRAghsAJ41gA73Qov2lS6nHGcC+A3zsIc+DQCeIdCB 4XisV0zx/CvTDQpQfSfLY04=
=Qj2Q
-----END PGP SIGNATURE-----

#27 lore raw

Re: [PATCH 8/28] VFS: Remove MNT_EXPIRE support

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 13 lines · Mike Waychison
Quoted 9 lines · Christoph Hellwig
Quoted 9 lines · Mike Waychison
Quoted 4 lines

Drop support for MNT_EXPIRE (flag to umount(2)). Nobody was using it and it didn't fit into the new expiry framework.

umm, this is a user API, you can't simply drop it.

Quoted 7 lines

I also wanted to add that given the current interface that is found in mainline, there is no way for userspace to even set a mountpoint as expiring. The only consumer is still AFS which handles the mark_mounts_for_expiry stuff itself.

So even if userspace wanted to use MNT_EXPIRE, it couldn't.

Gah, nevermind. I'm an idiot.

MNT_EXPIRE allows userspace to do the countdown itself. For some reason I assumed the mount in question had to be on a mnt_fslink list.

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBfTfndQs4kOxk3/MRAo5/AJ42AGwgKaRoEoA1Pto55DClNF3ZFwCfQna8 02yxxTF3wB30aguYzaumWII=
=oTA1
-----END PGP SIGNATURE-----

#28 lore raw

Re: [PATCH 6/28] VFS: Make expiry recursive

In reply to Mike Waychison
Quoted 5 lines · Mike Waychison

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.

Quoted 8 lines

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.

so add some constant for this, ala

#define MNT_EXPIRE_RECURSIVE (-1)

#29 lore raw

Re: [PATCH 4/28] VFS: Stat shouldn't stop expire

In reply to Mike Waychison
Quoted 22 lines · Mike Waychison

This patch fixes the problem where if you have a mountpoint that is going to expire, it fails to expire before somebody keeps stat(2)ing the root of it's filesystem. For example, consider the case where a user has his home
directory automounted on /home/mikew. Some other user can keep the
filesystem mounted forever by simply calling ls(1) in /home, because the stat action resets the marker on each call.

---

 namei.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletion(-)

Index: linux-2.6.9-quilt/fs/namei.c
===================================================================

fs/namei.c +0 -1
@@ -275,7 +275,16 @@ int deny_write_access(struct file * file
 void path_release(struct nameidata *nd)
 {
 	dput(nd->dentry);
-	mntput(nd->mnt);

fs/namei.c int deny_write_access(struct file * file · line 287

Quoted 10 lines
fs/namei.c +10 -0
+	/*
+	 * In order to ensure that access to an automounted filesystems'
+	 * root does not reset it's expire counter, we check to see if the path
+	 * being released here is a mountpoint itself.  If it is, then we call
+	 * _mntput which leaves the expire counter alone.
+	 */
+	if (nd->mnt && nd->mnt->mnt_root == nd->dentry)
+		_mntput(nd->mnt);
+	else  
+		mntput(nd->mnt);

Why only for the root dentry not any on stat() This seems highly inconsistant. Also while you're at it please give _mntput a more sensible name, e.g. mntput_no_expire (yes, I know that name isn't your fault)

#30 lore raw

Re: [PATCH 4/28] VFS: Stat shouldn't stop expire

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 29 lines · Christoph Hellwig
Quoted 27 lines · Mike Waychison

This patch fixes the problem where if you have a mountpoint that is going to expire, it fails to expire before somebody keeps stat(2)ing the root of it's filesystem. For example, consider the case where a user has his home
directory automounted on /home/mikew. Some other user can keep the
filesystem mounted forever by simply calling ls(1) in /home, because the stat action resets the marker on each call.

---

namei.c | 11 ++++++++++-

1 files changed, 10 insertions(+), 1 deletion(-)

Index: linux-2.6.9-quilt/fs/namei.c
===================================================================

fs/namei.c +5 -1
@@ -275,7 +275,16 @@ int deny_write_access(struct file * file
 void path_release(struct nameidata *nd)
 {
 	dput(nd->dentry);
-	mntput(nd->mnt);
+	/*
+	 * In order to ensure that access to an automounted filesystems'
+	 * root does not reset it's expire counter, we check to see if the path
+	 * being released here is a mountpoint itself.  If it is, then we call
+	 * _mntput which leaves the expire counter alone.
Quoted 8 lines

fs/namei.c int deny_write_access(struct file * file · line 287

Quoted 5 lines
fs/namei.c +5 -0
+	 */
+	if (nd->mnt && nd->mnt->mnt_root == nd->dentry)
+		_mntput(nd->mnt);
+	else  
+		mntput(nd->mnt);

Why only for the root dentry not any on stat() This seems highly inconsistant.

I'm not sure. I need help in understanding the different cases of
path_release: (please add any others/discrepencies you see)

1) path walk (across a mountpoint)
2) open / close (of a mountpoint)
3) stat / xattr (of a mountpoint)

The first case, a path walk, will always touch a non-mountpoint root dentry. As such, the above snippet will always reset the expire counter
as some other dentry got path_released.

Case 2, opening of a mountpoint happens when you readdir a the base of the mounted filesystem. In this case, you aren't path_releasing on close, but are doing an explicit mntput(filp->f_vfsmnt).

Case 3, you are accessing meta information of the root of the mountpoint. Assuming the call is made from 'outside' the mounted filesystem, the expire counter is currently ticking. Access to grab meta information of the base directory of the mountpoint shouldn't reset the counter, and in all cases I looked at (minimal), they called path_release when done.

The above hack^Wsnippet was intended to deal with case #3, but it may make more sense to look into the matter further and perform the special case in vfs_l?stat themselves.

Thoughts?

Quoted 3 lines

Also while you're at it please give _mntput a more sensible name, e.g. mntput_no_expire (yes, I know that name isn't your fault)

Will do.

- --
Mike Waychison
Sun Microsystems, Inc.
1 (650) 352-5299 voice
1 (416) 202-8336 voice

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTICE: The opinions expressed in this email are held by me, and may not represent the views of Sun Microsystems, Inc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD4DBQFBf+q2dQs4kOxk3/MRAnlQAJ413gDuQLqF5HgOKSQ/S7LrtlaZqQCYix9y ogHTca+0B+7+HIuSJsY9kQ==
=Qw5B
-----END PGP SIGNATURE-----

#31 lore raw

Re: [PATCH 3/28] VFS: Move expiry into vfs

In reply to Mike Waychison
Quoted 11 lines · Mike Waychison

This patch moves the recently added expiry functionality directly into the VFS layer. Doing this gives us a couple advantages:

- Allows for configurable timeouts using a single consolidated timer
- Keeps filesystems from having to each implement their own expiry logic
- Provides a generic interface that can be used for _any_ filesystem, as desired by user applications and/or the system admninistrator.

This patch implements expiry by having the VFS recursively register work to do. Checks are done for expiry every 1 second, so expiry is configurable to that granularity.

The expiry timer should only run as long as there are filesystems registered for expiry.