Diff below implements most of the missing locking goo for NFS without enabling it. It does the following:
- Add a missing PDIRUNLOCK in nfs_lookup() - Change vrele(9) into vput(9) where necessary. nfs_nget() will in future return a locked NFSnode. Just like ffs_vget() returns a locked vnode. So every time it is called we need a corresponding vput(9). - Make sure VN_KNOTE() is always called with a valid reference, before vrele(9) or vput(9). - Substitute an XXX by an assert in nfs_removeit(). However as long as nfs_lock/unlock are noops, this diff should not introduce any change in behavior. But please do test this diff :) Ok? Index: nfs/nfs_vnops.c =================================================================== RCS file: /cvs/src/sys/nfs/nfs_vnops.c,v retrieving revision 1.171 diff -u -p -r1.171 nfs_vnops.c --- nfs/nfs_vnops.c 22 Feb 2017 11:42:46 -0000 1.171 +++ nfs/nfs_vnops.c 9 Apr 2018 14:00:09 -0000 @@ -769,6 +769,7 @@ nfs_lookup(void *v) flags = cnp->cn_flags; *vpp = NULLVP; + newvp = NULLVP; if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); @@ -836,8 +837,10 @@ nfs_lookup(void *v) if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((!lockparent || !(flags & ISLASTCN)) && - newvp != dvp) + newvp != dvp) { VOP_UNLOCK(dvp, p); + cnp->cn_flags |= PDIRUNLOCK; + } return (0); } cache_purge(newvp); @@ -1318,10 +1321,6 @@ nfs_mknodrpc(struct vnode *dvp, struct v if (!error) { nfsm_mtofh(dvp, newvp, info.nmi_v3, gotvp); if (!gotvp) { - if (newvp) { - vrele(newvp); - newvp = NULL; - } error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); if (!error) @@ -1335,7 +1334,7 @@ nfs_mknodrpc(struct vnode *dvp, struct v nfsmout: if (error) { if (newvp) - vrele(newvp); + vput(newvp); } else { if (cnp->cn_flags & MAKEENTRY) nfs_cache_enter(dvp, newvp, cnp); @@ -1345,7 +1344,7 @@ nfsmout: VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); - vrele(dvp); + vput(dvp); return (error); } @@ -1362,7 +1361,7 @@ nfs_mknod(void *v) error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap); if (!error) - vrele(newvp); + vput(newvp); VN_KNOTE(ap->a_dvp, NOTE_WRITE); @@ -1430,10 +1429,6 @@ again: if (!error) { nfsm_mtofh(dvp, newvp, info.nmi_v3, gotvp); if (!gotvp) { - if (newvp) { - vrele(newvp); - newvp = NULL; - } error = nfs_lookitup(dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); if (!error) @@ -1451,7 +1446,7 @@ nfsmout: goto again; } if (newvp) - vrele(newvp); + vput(newvp); } else if (info.nmi_v3 && (fmode & O_EXCL)) error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc); if (!error) { @@ -1464,7 +1459,7 @@ nfsmout: if (!wccflag) NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); VN_KNOTE(ap->a_dvp, NOTE_WRITE); - vrele(dvp); + vput(dvp); return (error); } @@ -1530,12 +1525,13 @@ nfs_remove(void *v) error = nfs_sillyrename(dvp, vp, cnp); pool_put(&namei_pool, cnp->cn_pnbuf); NFS_INVALIDATE_ATTRCACHE(np); - vrele(dvp); - vrele(vp); - VN_KNOTE(vp, NOTE_DELETE); VN_KNOTE(dvp, NOTE_WRITE); - + if (dvp == vp) + vrele(vp); + else + vput(vp); + vput(dvp); return (error); } @@ -1545,9 +1541,9 @@ nfs_remove(void *v) int nfs_removeit(struct sillyrename *sp) { + KASSERT(VOP_ISLOCKED(sp->s_dvp)); /* * Make sure that the directory vnode is still valid. - * XXX we should lock sp->s_dvp here. * * NFS can potentially try to nuke a silly *after* the directory * has already been pushed out on a forced unmount. Since the silly @@ -1628,7 +1624,7 @@ nfs_rename(void *v) if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename && tvp->v_type != VDIR && !nfs_sillyrename(tdvp, tvp, tcnp)) { VN_KNOTE(tvp, NOTE_DELETE); - vrele(tvp); + vput(tvp); tvp = NULL; } @@ -1827,13 +1823,13 @@ nfs_symlink(void *v) nfsmout: if (newvp) - vrele(newvp); + vput(newvp); pool_put(&namei_pool, cnp->cn_pnbuf); VTONFS(dvp)->n_flag |= NMODIFIED; if (!wccflag) NFS_INVALIDATE_ATTRCACHE(VTONFS(dvp)); VN_KNOTE(dvp, NOTE_WRITE); - vrele(dvp); + vput(dvp); return (error); } @@ -1904,7 +1900,7 @@ nfsmout: } if (error) { if (newvp) - vrele(newvp); + vput(newvp); } else { VN_KNOTE(dvp, NOTE_WRITE|NOTE_LINK); if (cnp->cn_flags & MAKEENTRY) @@ -1912,7 +1908,7 @@ nfsmout: *ap->a_vpp = newvp; } pool_put(&namei_pool, cnp->cn_pnbuf); - vrele(dvp); + vput(dvp); return (error); } @@ -1936,7 +1932,7 @@ nfs_rmdir(void *v) if (dvp == vp) { vrele(dvp); - vrele(dvp); + vput(dvp); pool_put(&namei_pool, cnp->cn_pnbuf); return (EINVAL); } @@ -1964,8 +1960,8 @@ nfsmout: VN_KNOTE(vp, NOTE_DELETE); cache_purge(vp); - vrele(vp); - vrele(dvp); + vput(vp); + vput(dvp); /* * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. */ @@ -2492,7 +2488,10 @@ nfs_readdirplusrpc(struct vnode *vp, str nfsm_adv(nfsm_rndup(i)); } if (newvp != NULLVP) { - vrele(newvp); + if (newvp == vp) + vrele(newvp); + else + vput(newvp); newvp = NULLVP; } nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED); @@ -2533,8 +2532,12 @@ nfs_readdirplusrpc(struct vnode *vp, str } nfsmout: - if (newvp != NULLVP) - vrele(newvp); + if (newvp != NULLVP) { + if (newvp == vp) + vrele(newvp); + else + vput(newvp); + } return (error); } @@ -2659,7 +2662,7 @@ nfs_lookitup(struct vnode *dvp, char *na nfsm_postop_attr(newvp, attrflag); if (!attrflag && *npp == NULL) { m_freem(info.nmi_mrep); - vrele(newvp); + vput(newvp); return (ENOENT); } } else @@ -2670,7 +2673,7 @@ nfsmout: if (npp && *npp == NULL) { if (error) { if (newvp) - vrele(newvp); + vput(newvp); } else *npp = np; }