svn commit: r308618 - in head/sys: kern ufs/ffs
Konstantin Belousov
kib at FreeBSD.org
Sun Nov 13 21:49:52 UTC 2016
Author: kib
Date: Sun Nov 13 21:49:51 2016
New Revision: 308618
URL: https://svnweb.freebsd.org/changeset/base/308618
Log:
Provide simple mutual exclusion between mount point update and unmount.
Currently mount update keeps vfs_busy(9) reference on the mount point
during MNT_UPDATE VFS_MOUNT() vfsops call. This already provides the
exclusion, but is problematic for filesystems which need to perform
namei(9) during VFS_MOUNT(MNT_UPDATE) operations, e.g. to refresh
mnt_from path, because namei(9) must not be called while the
vfs_busy(9) reference is owned.
Check for MNT_UPDATE flag before setting MNTK_UNMOUNT, and for
MNTK_UNMOUNT before entering innards of vfs_domount_update(), failing
syscalls with EBUSY if conflict is detected. Keep vfs_busy(9)
reference around VFS_MOUNT(MNT_UPDATE) calls still to not change VFS
KPI.
In the update path in ffs_mount(), drop vfs_busy() reference around
namei(), which is now safe due to unmount never executing in parallel
with VFS_MOUNT(MNT_UPDATE), and which avoids the deadlock.
Reported and tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Modified:
head/sys/kern/vfs_mount.c
head/sys/ufs/ffs/ffs_vfsops.c
Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c Sun Nov 13 21:39:55 2016 (r308617)
+++ head/sys/kern/vfs_mount.c Sun Nov 13 21:49:51 2016 (r308618)
@@ -940,6 +940,11 @@ vfs_domount_update(
VOP_UNLOCK(vp, 0);
MNT_ILOCK(mp);
+ if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0) {
+ MNT_IUNLOCK(mp);
+ error = EBUSY;
+ goto end;
+ }
mp->mnt_flag &= ~MNT_UPDATEMASK;
mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE |
MNT_SNAPSHOT | MNT_ROOTFS | MNT_UPDATEMASK | MNT_RDONLY);
@@ -1299,6 +1304,7 @@ dounmount(struct mount *mp, int flags, s
vn_start_write(NULL, &mp, V_WAIT | V_MNTREF);
MNT_ILOCK(mp);
if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0 ||
+ (mp->mnt_flag & MNT_UPDATE) != 0 ||
!TAILQ_EMPTY(&mp->mnt_uppers)) {
dounmount_cleanup(mp, coveredvp, 0);
return (EBUSY);
Modified: head/sys/ufs/ffs/ffs_vfsops.c
==============================================================================
--- head/sys/ufs/ffs/ffs_vfsops.c Sun Nov 13 21:39:55 2016 (r308617)
+++ head/sys/ufs/ffs/ffs_vfsops.c Sun Nov 13 21:49:51 2016 (r308618)
@@ -147,7 +147,7 @@ ffs_mount(struct mount *mp)
struct ufsmount *ump = NULL;
struct fs *fs;
pid_t fsckpid = 0;
- int error, flags;
+ int error, error1, flags;
uint64_t mntorflags;
accmode_t accmode;
struct nameidata ndp;
@@ -453,6 +453,11 @@ ffs_mount(struct mount *mp)
*/
if (mp->mnt_flag & MNT_SNAPSHOT)
return (ffs_snapshot(mp, fspec));
+
+ /*
+ * Must not call namei() while owning busy ref.
+ */
+ vfs_unbusy(mp);
}
/*
@@ -460,7 +465,18 @@ ffs_mount(struct mount *mp)
* and verify that it refers to a sensible disk device.
*/
NDINIT(&ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td);
- if ((error = namei(&ndp)) != 0)
+ error = namei(&ndp);
+ if ((mp->mnt_flag & MNT_UPDATE) != 0) {
+ /*
+ * Unmount does not start if MNT_UPDATE is set. Mount
+ * update busies mp before setting MNT_UPDATE. We
+ * must be able to retain our busy ref succesfully,
+ * without sleep.
+ */
+ error1 = vfs_busy(mp, MBF_NOWAIT);
+ MPASS(error1 == 0);
+ }
+ if (error != 0)
return (error);
NDFREE(&ndp, NDF_ONLY_PNBUF);
devvp = ndp.ni_vp;
More information about the svn-src-all
mailing list