svn commit: r269457 - in head/sys: kern sys

Konstantin Belousov kib at FreeBSD.org
Sun Aug 3 03:27:55 UTC 2014


Author: kib
Date: Sun Aug  3 03:27:54 2014
New Revision: 269457
URL: http://svnweb.freebsd.org/changeset/base/269457

Log:
  Remove Giant acquisition from the mount and unmount pathes.
  
  It could be claimed that two things were reasonable protected by
  Giant.  One is vfsconf list links, which is converted to the new
  dedicated sx vfsconf_sx.  Another is vfsconf.vfc_refcount, which is
  now updated with atomics.
  
  Note that vfc_refcount still has the same races now as it has under
  the Giant, the unload of filesystem modules can happen while the
  module is still in use.
  
  Tested by:	pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	2 weeks

Modified:
  head/sys/kern/vfs_init.c
  head/sys/kern/vfs_mount.c
  head/sys/kern/vfs_subr.c
  head/sys/sys/mount.h

Modified: head/sys/kern/vfs_init.c
==============================================================================
--- head/sys/kern/vfs_init.c	Sun Aug  3 03:06:00 2014	(r269456)
+++ head/sys/kern/vfs_init.c	Sun Aug  3 03:27:54 2014	(r269457)
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/linker.h>
 #include <sys/mount.h>
 #include <sys/proc.h>
+#include <sys/sx.h>
 #include <sys/syscallsubr.h>
 #include <sys/sysctl.h>
 #include <sys/vnode.h>
@@ -64,6 +65,8 @@ int maxvfsconf = VFS_GENERIC + 1;
  * New entries are added/deleted by vfs_register()/vfs_unregister()
  */
 struct vfsconfhead vfsconf = TAILQ_HEAD_INITIALIZER(vfsconf);
+struct sx vfsconf_sx;
+SX_SYSINIT(vfsconf, &vfsconf_sx, "vfsconf");
 
 /*
  * Loader.conf variable vfs.typenumhash enables setting vfc_typenum using a hash
@@ -104,20 +107,33 @@ struct vattr va_null;
  * Routines having to do with the management of the vnode table.
  */
 
-struct vfsconf *
-vfs_byname(const char *name)
+static struct vfsconf *
+vfs_byname_locked(const char *name)
 {
 	struct vfsconf *vfsp;
 
+	sx_assert(&vfsconf_sx, SA_LOCKED);
 	if (!strcmp(name, "ffs"))
 		name = "ufs";
-	TAILQ_FOREACH(vfsp, &vfsconf, vfc_list)
+	TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) {
 		if (!strcmp(name, vfsp->vfc_name))
 			return (vfsp);
+	}
 	return (NULL);
 }
 
 struct vfsconf *
+vfs_byname(const char *name)
+{
+	struct vfsconf *vfsp;
+
+	vfsconf_slock();
+	vfsp = vfs_byname_locked(name);
+	vfsconf_sunlock();
+	return (vfsp);
+}
+
+struct vfsconf *
 vfs_byname_kld(const char *fstype, struct thread *td, int *error)
 {
 	struct vfsconf *vfsp;
@@ -168,8 +184,11 @@ vfs_register(struct vfsconf *vfc)
 		    vfc->vfc_name, vfc->vfc_version);
 		return (EINVAL);
 	}
-	if (vfs_byname(vfc->vfc_name) != NULL)
+	vfsconf_lock();
+	if (vfs_byname_locked(vfc->vfc_name) != NULL) {
+		vfsconf_unlock();
 		return (EEXIST);
+	}
 
 	if (vfs_typenumhash != 0) {
 		/*
@@ -202,26 +221,6 @@ vfs_register(struct vfsconf *vfc)
 	TAILQ_INSERT_TAIL(&vfsconf, vfc, vfc_list);
 
 	/*
-	 * If this filesystem has a sysctl node under vfs
-	 * (i.e. vfs.xxfs), then change the oid number of that node to 
-	 * match the filesystem's type number.  This allows user code
-	 * which uses the type number to read sysctl variables defined
-	 * by the filesystem to continue working. Since the oids are
-	 * in a sorted list, we need to make sure the order is
-	 * preserved by re-registering the oid after modifying its
-	 * number.
-	 */
-	sysctl_lock();
-	SLIST_FOREACH(oidp, SYSCTL_CHILDREN(&sysctl___vfs), oid_link)
-		if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
-			sysctl_unregister_oid(oidp);
-			oidp->oid_number = vfc->vfc_typenum;
-			sysctl_register_oid(oidp);
-			break;
-		}
-	sysctl_unlock();
-
-	/*
 	 * Initialise unused ``struct vfsops'' fields, to use
 	 * the vfs_std*() functions.  Note, we need the mount
 	 * and unmount operations, at the least.  The check
@@ -280,8 +279,30 @@ vfs_register(struct vfsconf *vfc)
 	 * Call init function for this VFS...
 	 */
 	(*(vfc->vfc_vfsops->vfs_init))(vfc);
+	vfsconf_unlock();
 
-	return 0;
+	/*
+	 * If this filesystem has a sysctl node under vfs
+	 * (i.e. vfs.xxfs), then change the oid number of that node to
+	 * match the filesystem's type number.  This allows user code
+	 * which uses the type number to read sysctl variables defined
+	 * by the filesystem to continue working. Since the oids are
+	 * in a sorted list, we need to make sure the order is
+	 * preserved by re-registering the oid after modifying its
+	 * number.
+	 */
+	sysctl_lock();
+	SLIST_FOREACH(oidp, SYSCTL_CHILDREN(&sysctl___vfs), oid_link) {
+		if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
+			sysctl_unregister_oid(oidp);
+			oidp->oid_number = vfc->vfc_typenum;
+			sysctl_register_oid(oidp);
+			break;
+		}
+	}
+	sysctl_unlock();
+
+	return (0);
 }
 
 
@@ -294,15 +315,22 @@ vfs_unregister(struct vfsconf *vfc)
 
 	i = vfc->vfc_typenum;
 
-	vfsp = vfs_byname(vfc->vfc_name);
-	if (vfsp == NULL)
-		return EINVAL;
-	if (vfsp->vfc_refcount)
-		return EBUSY;
+	vfsconf_lock();
+	vfsp = vfs_byname_locked(vfc->vfc_name);
+	if (vfsp == NULL) {
+		vfsconf_unlock();
+		return (EINVAL);
+	}
+	if (vfsp->vfc_refcount != 0) {
+		vfsconf_unlock();
+		return (EBUSY);
+	}
 	if (vfc->vfc_vfsops->vfs_uninit != NULL) {
 		error = (*vfc->vfc_vfsops->vfs_uninit)(vfsp);
-		if (error)
+		if (error != 0) {
+			vfsconf_unlock();
 			return (error);
+		}
 	}
 	TAILQ_REMOVE(&vfsconf, vfsp, vfc_list);
 	maxtypenum = VFS_GENERIC;
@@ -310,7 +338,8 @@ vfs_unregister(struct vfsconf *vfc)
 		if (maxtypenum < vfsp->vfc_typenum)
 			maxtypenum = vfsp->vfc_typenum;
 	maxvfsconf = maxtypenum + 1;
-	return 0;
+	vfsconf_unlock();
+	return (0);
 }
 
 /*

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c	Sun Aug  3 03:06:00 2014	(r269456)
+++ head/sys/kern/vfs_mount.c	Sun Aug  3 03:27:54 2014	(r269457)
@@ -463,9 +463,9 @@ vfs_mount_alloc(struct vnode *vp, struct
 	mp->mnt_activevnodelistsize = 0;
 	mp->mnt_ref = 0;
 	(void) vfs_busy(mp, MBF_NOWAIT);
+	atomic_add_acq_int(&vfsp->vfc_refcount, 1);
 	mp->mnt_op = vfsp->vfc_vfsops;
 	mp->mnt_vfc = vfsp;
-	vfsp->vfc_refcount++;	/* XXX Unlocked */
 	mp->mnt_stat.f_type = vfsp->vfc_typenum;
 	mp->mnt_gen++;
 	strlcpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
@@ -505,7 +505,7 @@ vfs_mount_destroy(struct mount *mp)
 		panic("vfs_mount_destroy: nonzero writeopcount");
 	if (mp->mnt_secondary_writes != 0)
 		panic("vfs_mount_destroy: nonzero secondary_writes");
-	mp->mnt_vfc->vfc_refcount--;
+	atomic_subtract_rel_int(&mp->mnt_vfc->vfc_refcount, 1);
 	if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) {
 		struct vnode *vp;
 
@@ -736,17 +736,12 @@ sys_mount(td, uap)
 	}
 
 	AUDIT_ARG_TEXT(fstype);
-	mtx_lock(&Giant);
 	vfsp = vfs_byname_kld(fstype, td, &error);
 	free(fstype, M_TEMP);
-	if (vfsp == NULL) {
-		mtx_unlock(&Giant);
+	if (vfsp == NULL)
 		return (ENOENT);
-	}
-	if (vfsp->vfc_vfsops->vfs_cmount == NULL) {
-		mtx_unlock(&Giant);
+	if (vfsp->vfc_vfsops->vfs_cmount == NULL)
 		return (EOPNOTSUPP);
-	}
 
 	ma = mount_argsu(ma, "fstype", uap->type, MFSNAMELEN);
 	ma = mount_argsu(ma, "fspath", uap->path, MNAMELEN);
@@ -755,7 +750,6 @@ sys_mount(td, uap)
 	ma = mount_argb(ma, !(flags & MNT_NOEXEC), "noexec");
 
 	error = vfsp->vfc_vfsops->vfs_cmount(ma, uap->data, flags);
-	mtx_unlock(&Giant);
 	return (error);
 }
 
@@ -777,7 +771,6 @@ vfs_domount_first(
 	struct vnode *newdp;
 	int error;
 
-	mtx_assert(&Giant, MA_OWNED);
 	ASSERT_VOP_ELOCKED(vp, __func__);
 	KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here"));
 
@@ -889,7 +882,6 @@ vfs_domount_update(
 	int error, export_error;
 	uint64_t flag;
 
-	mtx_assert(&Giant, MA_OWNED);
 	ASSERT_VOP_ELOCKED(vp, __func__);
 	KASSERT((fsflags & MNT_UPDATE) != 0, ("MNT_UPDATE should be here"));
 
@@ -1091,7 +1083,6 @@ vfs_domount(
 	error = namei(&nd);
 	if (error != 0)
 		return (error);
-	mtx_lock(&Giant);
 	NDFREE(&nd, NDF_ONLY_PNBUF);
 	vp = nd.ni_vp;
 	if ((fsflags & MNT_UPDATE) == 0) {
@@ -1106,7 +1097,6 @@ vfs_domount(
 		free(pathbuf, M_TEMP);
 	} else
 		error = vfs_domount_update(td, vp, fsflags, optlist);
-	mtx_unlock(&Giant);
 
 	ASSERT_VI_UNLOCKED(vp, __func__);
 	ASSERT_VOP_UNLOCKED(vp, __func__);
@@ -1153,12 +1143,10 @@ sys_unmount(td, uap)
 		free(pathbuf, M_TEMP);
 		return (error);
 	}
-	mtx_lock(&Giant);
 	if (uap->flags & MNT_BYFSID) {
 		AUDIT_ARG_TEXT(pathbuf);
 		/* Decode the filesystem ID. */
 		if (sscanf(pathbuf, "FSID:%d:%d", &id0, &id1) != 2) {
-			mtx_unlock(&Giant);
 			free(pathbuf, M_TEMP);
 			return (EINVAL);
 		}
@@ -1198,19 +1186,15 @@ sys_unmount(td, uap)
 		 * now, so in the !MNT_BYFSID case return the more likely
 		 * EINVAL for compatibility.
 		 */
-		mtx_unlock(&Giant);
 		return ((uap->flags & MNT_BYFSID) ? ENOENT : EINVAL);
 	}
 
 	/*
 	 * Don't allow unmounting the root filesystem.
 	 */
-	if (mp->mnt_flag & MNT_ROOTFS) {
-		mtx_unlock(&Giant);
+	if (mp->mnt_flag & MNT_ROOTFS)
 		return (EINVAL);
-	}
 	error = dounmount(mp, uap->flags, td);
-	mtx_unlock(&Giant);
 	return (error);
 }
 
@@ -1228,8 +1212,6 @@ dounmount(mp, flags, td)
 	uint64_t async_flag;
 	int mnt_gen_r;
 
-	mtx_assert(&Giant, MA_OWNED);
-
 	if ((coveredvp = mp->mnt_vnodecovered) != NULL) {
 		mnt_gen_r = mp->mnt_gen;
 		VI_LOCK(coveredvp);

Modified: head/sys/kern/vfs_subr.c
==============================================================================
--- head/sys/kern/vfs_subr.c	Sun Aug  3 03:06:00 2014	(r269456)
+++ head/sys/kern/vfs_subr.c	Sun Aug  3 03:27:54 2014	(r269457)
@@ -3233,6 +3233,7 @@ sysctl_vfs_conflist(SYSCTL_HANDLER_ARGS)
 	int error;
 
 	error = 0;
+	vfsconf_slock();
 	TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) {
 #ifdef COMPAT_FREEBSD32
 		if (req->flags & SCTL_MASK32)
@@ -3243,11 +3244,12 @@ sysctl_vfs_conflist(SYSCTL_HANDLER_ARGS)
 		if (error)
 			break;
 	}
+	vfsconf_sunlock();
 	return (error);
 }
 
-SYSCTL_PROC(_vfs, OID_AUTO, conflist, CTLTYPE_OPAQUE | CTLFLAG_RD,
-    NULL, 0, sysctl_vfs_conflist,
+SYSCTL_PROC(_vfs, OID_AUTO, conflist, CTLTYPE_OPAQUE | CTLFLAG_RD |
+    CTLFLAG_MPSAFE, NULL, 0, sysctl_vfs_conflist,
     "S,xvfsconf", "List of all configured filesystems");
 
 #ifndef BURN_BRIDGES
@@ -3277,9 +3279,12 @@ vfs_sysctl(SYSCTL_HANDLER_ARGS)
 	case VFS_CONF:
 		if (namelen != 3)
 			return (ENOTDIR);	/* overloaded */
-		TAILQ_FOREACH(vfsp, &vfsconf, vfc_list)
+		vfsconf_slock();
+		TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) {
 			if (vfsp->vfc_typenum == name[2])
 				break;
+		}
+		vfsconf_sunlock();
 		if (vfsp == NULL)
 			return (EOPNOTSUPP);
 #ifdef COMPAT_FREEBSD32
@@ -3292,8 +3297,9 @@ vfs_sysctl(SYSCTL_HANDLER_ARGS)
 	return (EOPNOTSUPP);
 }
 
-static SYSCTL_NODE(_vfs, VFS_GENERIC, generic, CTLFLAG_RD | CTLFLAG_SKIP,
-    vfs_sysctl, "Generic filesystem");
+static SYSCTL_NODE(_vfs, VFS_GENERIC, generic, CTLFLAG_RD | CTLFLAG_SKIP |
+    CTLFLAG_MPSAFE, vfs_sysctl,
+    "Generic filesystem");
 
 #if 1 || defined(COMPAT_PRELITE2)
 
@@ -3304,6 +3310,7 @@ sysctl_ovfs_conf(SYSCTL_HANDLER_ARGS)
 	struct vfsconf *vfsp;
 	struct ovfsconf ovfs;
 
+	vfsconf_slock();
 	TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) {
 		bzero(&ovfs, sizeof(ovfs));
 		ovfs.vfc_vfsops = vfsp->vfc_vfsops;	/* XXX used as flag */
@@ -3312,10 +3319,13 @@ sysctl_ovfs_conf(SYSCTL_HANDLER_ARGS)
 		ovfs.vfc_refcount = vfsp->vfc_refcount;
 		ovfs.vfc_flags = vfsp->vfc_flags;
 		error = SYSCTL_OUT(req, &ovfs, sizeof ovfs);
-		if (error)
-			return error;
+		if (error != 0) {
+			vfsconf_sunlock();
+			return (error);
+		}
 	}
-	return 0;
+	vfsconf_sunlock();
+	return (0);
 }
 
 #endif /* 1 || COMPAT_PRELITE2 */
@@ -3413,8 +3423,9 @@ sysctl_vnode(SYSCTL_HANDLER_ARGS)
 	return (error);
 }
 
-SYSCTL_PROC(_kern, KERN_VNODE, vnode, CTLTYPE_OPAQUE|CTLFLAG_RD,
-    0, 0, sysctl_vnode, "S,xvnode", "");
+SYSCTL_PROC(_kern, KERN_VNODE, vnode, CTLTYPE_OPAQUE | CTLFLAG_RD |
+    CTLFLAG_MPSAFE, 0, 0, sysctl_vnode, "S,xvnode",
+    "");
 #endif
 
 /*

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h	Sun Aug  3 03:06:00 2014	(r269456)
+++ head/sys/sys/mount.h	Sun Aug  3 03:27:54 2014	(r269457)
@@ -39,6 +39,7 @@
 #include <sys/lock.h>
 #include <sys/lockmgr.h>
 #include <sys/_mutex.h>
+#include <sys/_sx.h>
 #endif
 
 /*
@@ -889,6 +890,11 @@ void	vfs_unmountall(void);
 extern	TAILQ_HEAD(mntlist, mount) mountlist;	/* mounted filesystem list */
 extern	struct mtx mountlist_mtx;
 extern	struct nfs_public nfs_pub;
+extern	struct sx vfsconf_sx;
+#define	vfsconf_lock()		sx_xlock(&vfsconf_sx)
+#define	vfsconf_unlock()	sx_xunlock(&vfsconf_sx)
+#define	vfsconf_slock()		sx_slock(&vfsconf_sx)
+#define	vfsconf_sunlock()	sx_sunlock(&vfsconf_sx)
 
 /*
  * Declarations for these vfs default operations are located in


More information about the svn-src-head mailing list