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
This commit is contained in:
parent
7a4fd2a8e7
commit
aef0061353
@ -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, struct thread *td)
|
||||
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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user