diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -601,7 +601,8 @@ if (error) goto error; vp = nd.ni_vp; - if (vp != NULL || vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vp != NULL || vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | + V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1386,7 +1386,7 @@ panic("kern_mknod: invalid mode"); } } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | V_PCATCH)) != 0) @@ -1473,7 +1473,7 @@ vrele(nd.ni_vp); return (EEXIST); } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | V_PCATCH)) != 0) @@ -1654,7 +1654,7 @@ NDFREE_PNBUF(&nd); return (error); } - error = vn_start_write(vp, &mp, V_NOWAIT); + error = vn_start_write(vp, &mp, V_NOWAIT | V_FORXSLEEP); if (error != 0) { vput(vp); vput(nd.ni_dvp); @@ -1752,7 +1752,7 @@ error = EEXIST; goto out; } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | V_PCATCH)) != 0) @@ -1817,7 +1817,7 @@ vrele(nd.ni_vp); return (EEXIST); } - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | V_PCATCH)) != 0) @@ -1945,7 +1945,8 @@ error = EBUSY; } if (error == 0) { - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | + V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if (vp == nd.ni_dvp) @@ -3699,7 +3700,7 @@ } tdvp = tond.ni_dvp; tvp = tond.ni_vp; - error = vn_start_write(fvp, &mp, V_NOWAIT); + error = vn_start_write(fvp, &mp, V_NOWAIT | V_FORXSLEEP); if (error != 0) { NDFREE_PNBUF(&fromnd); NDFREE_PNBUF(&tond); @@ -3828,7 +3829,7 @@ segflg, path, fd, &cap_mkdirat_rights); if ((error = namei(&nd)) != 0) return (error); - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | V_PCATCH)) != 0) @@ -3933,7 +3934,7 @@ if (error != 0) goto out; #endif - if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(vp); if (nd.ni_dvp == vp) diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -267,7 +267,8 @@ vap->va_mode = cmode; if (fmode & O_EXCL) vap->va_vaflags |= VA_EXCLUSIVE; - if (vn_start_write(ndp->ni_dvp, &mp, V_NOWAIT) != 0) { + if (vn_start_write(ndp->ni_dvp, &mp, V_NOWAIT | + V_FORXSLEEP) != 0) { NDFREE_PNBUF(ndp); vput(ndp->ni_dvp); if ((error = vn_start_write(NULL, &mp, @@ -1848,11 +1849,13 @@ * suspension is over, and then proceed. */ static int -vn_start_write_refed(struct mount *mp, int flags, bool mplocked) +vn_start_write_refed(struct mount **mpp, int flags, bool mplocked) { struct mount_pcpu *mpcpu; + struct mount *mp; int error, mflags; + mp = *mpp; if (__predict_true(!mplocked) && (flags & V_XSLEEP) == 0 && vfs_op_thread_enter(mp, mpcpu)) { MPASS((mp->mnt_kern_flag & MNTK_SUSPEND) == 0); @@ -1880,14 +1883,18 @@ } mflags |= (PUSER - 1); while ((mp->mnt_kern_flag & MNTK_SUSPEND) != 0) { - if (flags & V_NOWAIT) { + if ((flags & V_NOWAIT) != 0) { + if ((flags & V_FORXSLEEP) == 0) + *mpp = NULL; error = EWOULDBLOCK; goto unlock; } error = msleep(&mp->mnt_flag, MNT_MTX(mp), mflags, "suspfs", 0); - if (error) + if (error != 0) { + *mpp = NULL; goto unlock; + } } } if (flags & V_XSLEEP) @@ -1908,6 +1915,7 @@ KASSERT((flags & ~V_VALID_FLAGS) == 0, ("%s: invalid flags passed %d\n", __func__, flags)); + MPASS((flags & (V_FORXSLEEP | V_NOWAIT)) != V_FORXSLEEP); error = 0; /* @@ -1935,7 +1943,7 @@ if (vp == NULL) vfs_ref(mp); - return (vn_start_write_refed(mp, flags, false)); + return (vn_start_write_refed(mpp, flags, false)); } /* @@ -1951,7 +1959,7 @@ struct mount *mp; int error, mflags; - KASSERT((flags & ~V_VALID_FLAGS) == 0, + KASSERT((flags & (~V_VALID_FLAGS | V_XSLEEP | V_FORXSLEEP)) == 0, ("%s: invalid flags passed %d\n", __func__, flags)); retry: @@ -1989,6 +1997,7 @@ if (flags & V_NOWAIT) { MNT_REL(mp); MNT_IUNLOCK(mp); + *mpp = NULL; return (EWOULDBLOCK); } /* @@ -2004,6 +2013,7 @@ vfs_rel(mp); if (error == 0) goto retry; + *mpp = NULL; return (error); } @@ -2140,7 +2150,7 @@ vfs_op_exit(mp); } else if ((flags & VR_START_WRITE) != 0) { MNT_REF(mp); - vn_start_write_refed(mp, 0, true); + vn_start_write_refed(&mp, 0, true); } else { MNT_IUNLOCK(mp); } diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -409,7 +409,8 @@ #define V_NOWAIT 0x0002 /* vn_start_write: don't sleep for suspend */ #define V_XSLEEP 0x0004 /* vn_start_write: just return after sleep */ #define V_PCATCH 0x0008 /* vn_start_write: make the sleep interruptible */ -#define V_VALID_FLAGS (V_WAIT | V_NOWAIT | V_XSLEEP | V_PCATCH) +#define V_FORXSLEEP 0x0010 /* vn_start_write: keep *mpp for V_XSLEEP */ +#define V_VALID_FLAGS (V_WAIT | V_NOWAIT | V_XSLEEP | V_PCATCH | V_FORXSLEEP) #define VR_START_WRITE 0x0001 /* vfs_write_resume: start write atomically */ #define VR_NO_SUSPCLR 0x0002 /* vfs_write_resume: do not clear suspension */ diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c --- a/sys/ufs/ffs/ffs_snapshot.c +++ b/sys/ufs/ffs/ffs_snapshot.c @@ -273,7 +273,7 @@ if (wrtmp != mp) panic("ffs_snapshot: mount mismatch"); vfs_rel(wrtmp); - if (vn_start_write(NULL, &wrtmp, V_NOWAIT) != 0) { + if (vn_start_write(NULL, &wrtmp, V_NOWAIT | V_FORXSLEEP) != 0) { NDFREE_PNBUF(&nd); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &wrtmp,