Index: fs/nfsclient/nfs_clbio.c =================================================================== --- fs/nfsclient/nfs_clbio.c +++ fs/nfsclient/nfs_clbio.c @@ -101,9 +101,9 @@ int biosize, bcount; np = VTONFS(vp); - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); nsize = np->n_size; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); biosize = vp->v_bufobj.bo_bsize; bcount = biosize; @@ -301,6 +301,7 @@ } else mtx_unlock(&nmp->nm_mtx); + sx_xlock(&np->n_slock); mtx_lock(&np->n_mtx); if (newnfs_directio_enable && !newnfs_directio_allow_mmap && (np->n_flag & NNONCACHE) && (vp->v_type == VREG)) { @@ -317,6 +318,7 @@ count = 0; } mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); for (i = 0; i < npages; i++) rtvals[i] = VM_PAGER_ERROR; @@ -474,9 +476,9 @@ do { u_quad_t nsize; - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); nsize = np->n_size; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); switch (vp->v_type) { case VREG: @@ -929,9 +931,9 @@ } orig_resid = uio->uio_resid; - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); orig_size = np->n_size; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); /* * If IO_APPEND then load uio_offset. We restart here if we cannot @@ -943,9 +945,9 @@ error = VOP_GETATTR(vp, &vattr, cred); if (error) return (error); - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); uio->uio_offset = np->n_size; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); } if (uio->uio_offset < 0) @@ -1018,6 +1020,7 @@ * Handle direct append and file extension cases, calculate * unaligned buffer size. */ + sx_xlock(&np->n_slock); mtx_lock(&np->n_mtx); if ((np->n_flag & NHASBEENLOCKED) == 0 && (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0) @@ -1029,6 +1032,7 @@ lbn == (np->n_size / biosize) && uio->uio_offset + n > np->n_size)) && n) { mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); /* * Get the buffer (in its pre-append state to maintain * B_CACHE if it was previously set). Resize the @@ -1041,11 +1045,13 @@ if (bp != NULL) { long save; + sx_xlock(&np->n_slock); mtx_lock(&np->n_mtx); np->n_size = uio->uio_offset + n; np->n_flag |= NMODIFIED; - vnode_pager_setsize(vp, np->n_size); mtx_unlock(&np->n_mtx); + vnode_pager_setsize(vp, np->n_size); + sx_xunlock(&np->n_slock); save = bp->b_flags & B_CACHE; bcount = on + n; @@ -1068,14 +1074,18 @@ bcount = np->n_size - (off_t)lbn * biosize; } mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); bp = nfs_getcacheblk(vp, lbn, bcount, td); + sx_xlock(&np->n_slock); mtx_lock(&np->n_mtx); if (uio->uio_offset + n > np->n_size) { np->n_size = uio->uio_offset + n; np->n_flag |= NMODIFIED; + mtx_unlock(&np->n_mtx); vnode_pager_setsize(vp, np->n_size); - } - mtx_unlock(&np->n_mtx); + } else + mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); } if (!bp) { @@ -1706,10 +1716,10 @@ /* * Setup for actual write */ - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); if ((off_t)bp->b_blkno * DEV_BSIZE + bp->b_dirtyend > np->n_size) bp->b_dirtyend = np->n_size - (off_t)bp->b_blkno * DEV_BSIZE; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend @@ -1839,10 +1849,10 @@ int biosize = vp->v_bufobj.bo_bsize; int error = 0; - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); tsize = np->n_size; np->n_size = nsize; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); if (nsize < tsize) { struct buf *bp; Index: fs/nfsclient/nfs_clnode.c =================================================================== --- fs/nfsclient/nfs_clnode.c +++ fs/nfsclient/nfs_clnode.c @@ -143,6 +143,7 @@ * happened to return an error no special casing is needed). */ mtx_init(&np->n_mtx, "NEWNFSnode lock", NULL, MTX_DEF | MTX_DUPOK); + sx_init(&np->n_slock, "NEWNFSnode slock"); lockinit(&np->n_excl, PVFS, "nfsupg", VLKTIMEOUT, LK_NOSHARE | LK_CANRECURSE); @@ -172,6 +173,7 @@ *npp = NULL; free(np->n_fhp, M_NFSFH); mtx_destroy(&np->n_mtx); + sx_destroy(&np->n_slock); lockdestroy(&np->n_excl); uma_zfree(newnfsnode_zone, np); return (error); @@ -333,6 +335,7 @@ if (np->n_v4 != NULL) free(np->n_v4, M_NFSV4NODE); mtx_destroy(&np->n_mtx); + sx_destroy(&np->n_slock); lockdestroy(&np->n_excl); uma_zfree(newnfsnode_zone, vp->v_data); vp->v_data = NULL; Index: fs/nfsclient/nfs_clport.c =================================================================== --- fs/nfsclient/nfs_clport.c +++ fs/nfsclient/nfs_clport.c @@ -233,6 +233,7 @@ * happened to return an error no special casing is needed). */ mtx_init(&np->n_mtx, "NEWNFSnode lock", NULL, MTX_DEF | MTX_DUPOK); + sx_init(&np->n_slock, "NEWNFSnode slock"); lockinit(&np->n_excl, PVFS, "nfsupg", VLKTIMEOUT, LK_NOSHARE | LK_CANRECURSE); @@ -276,6 +277,7 @@ if (error != 0) { *npp = NULL; mtx_destroy(&np->n_mtx); + sx_destroy(&np->n_slock); lockdestroy(&np->n_excl); free(nfhp, M_NFSFH); if (np->n_v4 != NULL) @@ -414,10 +416,8 @@ struct nfsnode *np; struct nfsmount *nmp; struct timespec mtime_save; - vm_object_t object; - u_quad_t nsize; int error, force_fid_err; - bool setnsize; + bool unlocked; error = 0; @@ -429,7 +429,8 @@ * information. */ np = VTONFS(vp); - NFSLOCKNODE(np); + sx_xlock(&np->n_slock); + mtx_lock(&np->n_mtx); if (vp->v_type != nvap->va_type) { vp->v_type = nvap->va_type; if (vp->v_type == VFIFO) @@ -503,6 +504,7 @@ } else vn_fsid(vp, vap); np->n_attrstamp = time_second; + unlocked = false; if (vap->va_size != np->n_size) { if (vap->va_type == VREG) { if (dontshrink && vap->va_size < np->n_size) { @@ -513,7 +515,10 @@ */ vap->va_size = np->n_size; np->n_attrstamp = 0; + unlocked = true; + mtx_unlock(&np->n_mtx); KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp); + vnode_pager_setsize(vp, np->n_size); } else if (np->n_flag & NMODIFIED) { /* * We've modified the file: Use the larger @@ -525,14 +530,22 @@ np->n_size = vap->va_size; np->n_flag |= NSIZECHANGED; } + unlocked = true; + mtx_unlock(&np->n_mtx); + vnode_pager_setsize(vp, np->n_size); } else { np->n_size = vap->va_size; np->n_flag |= NSIZECHANGED; + unlocked = true; + mtx_unlock(&np->n_mtx); + vnode_pager_setsize(vp, np->n_size); } } else { np->n_size = vap->va_size; } } + if (unlocked) + mtx_lock(&np->n_mtx); /* * The following checks are added to prevent a race between (say) * a READDIR+ and a WRITE. @@ -565,26 +578,8 @@ if (np->n_attrstamp != 0) KDTRACE_NFS_ATTRCACHE_LOAD_DONE(vp, vap, error); #endif - nsize = vap->va_size; - object = vp->v_object; - setnsize = false; - if (object != NULL) { - if (OFF_TO_IDX(nsize + PAGE_MASK) < object->size) { - /* - * When shrinking the size, the call to - * vnode_pager_setsize() cannot be done with - * the mutex held, because we might need to - * wait for a busy page. Delay it until after - * the node is unlocked. - */ - setnsize = true; - } else { - vnode_pager_setsize(vp, nsize); - } - } - NFSUNLOCKNODE(np); - if (setnsize) - vnode_pager_setsize(vp, nsize); + mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); return (error); } Index: fs/nfsclient/nfs_clrpcops.c =================================================================== --- fs/nfsclient/nfs_clrpcops.c +++ fs/nfsclient/nfs_clrpcops.c @@ -1440,12 +1440,12 @@ * more link data than it should? */ if (len == NFS_MAXPATHLEN) { - NFSLOCKNODE(np); + sx_xlock(&np->n_slock); if (np->n_size > 0 && np->n_size < NFS_MAXPATHLEN) { len = np->n_size; cangetattr = 0; } - NFSUNLOCKNODE(np); + sx_xunlock(&np->n_slock); } error = nfsm_mbufuio(nd, uiop, len); if ((nd->nd_flag & ND_NFSV4) && !error && cangetattr) Index: fs/nfsclient/nfs_clstate.c =================================================================== --- fs/nfsclient/nfs_clstate.c +++ fs/nfsclient/nfs_clstate.c @@ -1357,7 +1357,9 @@ off = fl->l_start; break; case SEEK_END: + sx_xlock(&np->n_slock); off = np->n_size + fl->l_start; + sx_xunlock(&np->n_slock); break; default: return (1); Index: fs/nfsclient/nfs_clsubs.c =================================================================== --- fs/nfsclient/nfs_clsubs.c +++ fs/nfsclient/nfs_clsubs.c @@ -185,11 +185,13 @@ struct vattr *vap; struct nfsmount *nmp; int timeo, mustflush; + bool unlocked; np = VTONFS(vp); vap = &np->n_vattr.na_vattr; nmp = VFSTONFS(vp->v_mount); mustflush = nfscl_mustflush(vp); /* must be before mtx_lock() */ + sx_xlock(&np->n_slock); mtx_lock(&np->n_mtx); /* XXX n_mtime doesn't seem to be updated on a miss-and-reload */ timeo = (time_second - np->n_mtime.tv_sec) / 10; @@ -226,10 +228,12 @@ (mustflush != 0 || np->n_attrstamp == 0)) { nfsstatsv1.attrcache_misses++; mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); KDTRACE_NFS_ATTRCACHE_GET_MISS(vp); return( ENOENT); } nfsstatsv1.attrcache_hits++; + unlocked = false; if (vap->va_size != np->n_size) { if (vap->va_type == VREG) { if (np->n_flag & NMODIFIED) { @@ -240,11 +244,15 @@ } else { np->n_size = vap->va_size; } + unlocked = true; + mtx_unlock(&np->n_mtx); vnode_pager_setsize(vp, np->n_size); } else { np->n_size = vap->va_size; } } + if (unlocked) + mtx_lock(&np->n_mtx); bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr)); if (np->n_flag & NCHG) { if (np->n_flag & NACC) @@ -253,6 +261,7 @@ vaper->va_mtime = np->n_mtim; } mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); KDTRACE_NFS_ATTRCACHE_GET_HIT(vp, vap); return (0); } Index: fs/nfsclient/nfs_clvnops.c =================================================================== --- fs/nfsclient/nfs_clvnops.c +++ fs/nfsclient/nfs_clvnops.c @@ -465,14 +465,14 @@ * After calling nfsspec_access, we should have the correct * file size cached. */ - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); if (ap->a_cred->cr_uid == 0 && (ap->a_accmode & VREAD) && VTONFS(vp)->n_size > 0) { struct iovec aiov; struct uio auio; char buf[1]; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); aiov.iov_base = buf; aiov.iov_len = 1; auio.uio_iov = &aiov; @@ -498,7 +498,7 @@ else error = EACCES; } else - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); return (error); } } @@ -976,14 +976,16 @@ * V_SAVE races that might setsize a lower * value. */ - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); tsize = np->n_size; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); error = ncl_meta_setsize(vp, td, vap->va_size); + sx_xlock(&np->n_slock); mtx_lock(&np->n_mtx); if (np->n_flag & NMODIFIED) { tsize = np->n_size; mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); error = ncl_vinvalbuf(vp, vap->va_size == 0 ? 0 : V_SAVE, td, 1); if (error != 0) { @@ -995,17 +997,19 @@ * locally, as required. */ nfscl_delegmodtime(vp); - } else + } else { mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); + } /* * np->n_size has already been set to vap->va_size * in ncl_meta_setsize(). We must set it again since * nfs_loadattrcache() could be called through * ncl_meta_setsize() and could modify np->n_size. */ - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); np->n_vattr.na_size = np->n_size = vap->va_size; - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); } } else { mtx_lock(&np->n_mtx); @@ -1020,10 +1024,10 @@ } error = nfs_setattrrpc(vp, vap, ap->a_cred, td); if (error && vap->va_size != VNOVAL) { - mtx_lock(&np->n_mtx); + sx_xlock(&np->n_slock); np->n_size = np->n_vattr.na_size = tsize; vnode_pager_setsize(vp, tsize); - mtx_unlock(&np->n_mtx); + sx_xunlock(&np->n_slock); } return (error); } Index: fs/nfsclient/nfsnode.h =================================================================== --- fs/nfsclient/nfsnode.h +++ fs/nfsclient/nfsnode.h @@ -91,9 +91,13 @@ * changing the definition in nfsproto.h of NFS_SMALLFH.) * NB: Hopefully the current order of the fields is such that everything will * be well aligned and, therefore, tightly packed. + * n_mtx - is used to protect all fields except n_size. + * n_slock - is used to protect the n_size field. This is done so that + * functions such as vnode_pager_setsize() may be called with this lock. */ struct nfsnode { - struct mtx n_mtx; /* Protects all of these members */ + struct mtx n_mtx; /* Protects all except n_size */ + struct sx n_slock; /* Protects n_size */ struct lock n_excl; /* Exclusive helper for shared vnode lock */ u_quad_t n_size; /* Current size of file */