summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2014-08-03 03:27:54 +0000
committerkib <kib@FreeBSD.org>2014-08-03 03:27:54 +0000
commit17b754a0261fb1acd7f0974416bde5667c726f31 (patch)
treee40a48ea7e5645477aaebf277b2cb119f5320d8b
parentbbb1622644fb979f6001f31721adcb7f51785f4e (diff)
downloadFreeBSD-src-17b754a0261fb1acd7f0974416bde5667c726f31.zip
FreeBSD-src-17b754a0261fb1acd7f0974416bde5667c726f31.tar.gz
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
-rw-r--r--sys/kern/vfs_init.c93
-rw-r--r--sys/kern/vfs_mount.c28
-rw-r--r--sys/kern/vfs_subr.c31
-rw-r--r--sys/sys/mount.h6
4 files changed, 93 insertions, 65 deletions
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c
index 66a6b00..fb95417 100644
--- a/sys/kern/vfs_init.c
+++ b/sys/kern/vfs_init.c
@@ -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);
}
/*
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 8b764f7..674e526 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -463,9 +463,9 @@ vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp, const char *fspath,
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);
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 12a9cb3..9289e6a 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -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
/*
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index ca7e55c..ca3bc43 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -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
OpenPOWER on IntegriCloud