Index: fs/nfsclient/nfs_clbio.c =================================================================== --- fs/nfsclient/nfs_clbio.c +++ fs/nfsclient/nfs_clbio.c @@ -877,6 +877,8 @@ int bp_cached, n, on, error = 0, error1, wouldcommit; size_t orig_resid, local_resid; off_t orig_size, tmp_off; + u_quad_t nsize; + int ret; KASSERT(uio->uio_rw == UIO_WRITE, ("ncl_write mode")); KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread, @@ -1044,8 +1046,18 @@ NFSLOCKNODE(np); np->n_size = uio->uio_offset + n; np->n_flag |= NMODIFIED; - vnode_pager_setsize(vp, np->n_size); + nsize = np->n_size; + /* + * The size should always be increasing, so + * vnode_pager_setsize_nonblock() should + * always succeed. + */ + ret = vnode_pager_setsize_nonblock(vp, nsize); NFSUNLOCKNODE(np); + if (ret == EWOULDBLOCK) { + vnode_pager_setsize(vp, nsize); + printf("ncl_write: size decreased\n"); + } save = bp->b_flags & B_CACHE; bcount = on + n; @@ -1069,13 +1081,24 @@ } NFSUNLOCKNODE(np); bp = nfs_getcacheblk(vp, lbn, bcount, td); + ret = 0; NFSLOCKNODE(np); if (uio->uio_offset + n > np->n_size) { np->n_size = uio->uio_offset + n; np->n_flag |= NMODIFIED; - vnode_pager_setsize(vp, np->n_size); + nsize = np->n_size; + /* + * The size should always be increasing, so + * vnode_pager_setsize_nonblock() should + * always succeed. + */ + ret = vnode_pager_setsize_nonblock(vp, nsize); } NFSUNLOCKNODE(np); + if (ret == EWOULDBLOCK) { + vnode_pager_setsize(vp, nsize); + printf("ncl_write: size decreased2\n"); + } } if (!bp) { Index: fs/nfsclient/nfs_clport.c =================================================================== --- fs/nfsclient/nfs_clport.c +++ fs/nfsclient/nfs_clport.c @@ -414,12 +414,12 @@ struct nfsnode *np; struct nfsmount *nmp; struct timespec mtime_save; - vm_object_t object; u_quad_t nsize; - int error, force_fid_err; + int error, force_fid_err, ret; bool setnsize; error = 0; + setnsize = false; /* * If v_type == VNON it is a new node, so fill in the v_type, @@ -529,6 +529,7 @@ np->n_size = vap->va_size; np->n_flag |= NSIZECHANGED; } + setnsize = true; } else { np->n_size = vap->va_size; } @@ -566,24 +567,11 @@ 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); + ret = 0; if (setnsize) + ret = vnode_pager_setsize_nonblock(vp, nsize); + NFSUNLOCKNODE(np); + if (ret == EWOULDBLOCK) vnode_pager_setsize(vp, nsize); return (error); } Index: fs/nfsclient/nfs_clsubs.c =================================================================== --- fs/nfsclient/nfs_clsubs.c +++ fs/nfsclient/nfs_clsubs.c @@ -184,8 +184,10 @@ struct nfsnode *np; struct vattr *vap; struct nfsmount *nmp; - int timeo, mustflush; + int timeo, mustflush, ret; + u_quad_t nsize; + ret = 0; np = VTONFS(vp); vap = &np->n_vattr.na_vattr; nmp = VFSTONFS(vp->v_mount); @@ -240,7 +242,8 @@ } else { np->n_size = vap->va_size; } - vnode_pager_setsize(vp, np->n_size); + nsize = np->n_size; + ret = vnode_pager_setsize_nonblock(vp, nsize); } else { np->n_size = vap->va_size; } @@ -253,6 +256,8 @@ vaper->va_mtime = np->n_mtim; } NFSUNLOCKNODE(np); + if (ret == EWOULDBLOCK) + vnode_pager_setsize(vp, nsize); 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 @@ -926,7 +926,7 @@ struct nfsnode *np = VTONFS(vp); struct thread *td = curthread; /* XXX */ struct vattr *vap = ap->a_vap; - int error = 0; + int error = 0, ret; u_quad_t tsize; #ifndef nolint @@ -1022,8 +1022,18 @@ if (error && vap->va_size != VNOVAL) { NFSLOCKNODE(np); np->n_size = np->n_vattr.na_size = tsize; - vnode_pager_setsize(vp, tsize); + /* + * The above ncl_meta_setsize() call should have gotten rid + * of all pages beyond the new size, so that calling + * vnode_pager_setsize() here should be ok, but call + * vnode_pager_setsize_nonblock() to play it safe and delay the + * call until the NFS node is unlocked for the case where + * the file is shrinking. + */ + ret = vnode_pager_setsize_nonblock(vp, tsize); NFSUNLOCKNODE(np); + if (ret == EWOULDBLOCK) + vnode_pager_setsize(vp, tsize); } return (error); } Index: vm/vm_extern.h =================================================================== --- vm/vm_extern.h +++ vm/vm_extern.h @@ -118,7 +118,7 @@ void vmspace_free(struct vmspace *); void vmspace_exitfree(struct proc *); void vmspace_switch_aio(struct vmspace *); -void vnode_pager_setsize(struct vnode *, vm_ooffset_t); +int vnode_pager_setsize_mightsleep(struct vnode *, vm_ooffset_t, bool); int vslock(void *, size_t); void vsunlock(void *, size_t); struct sf_buf *vm_imgact_map_page(vm_object_t object, vm_ooffset_t offset); @@ -129,5 +129,10 @@ u_int vm_inactive_count(void); u_int vm_laundry_count(void); u_int vm_wait_count(void); + +#define vnode_pager_setsize(v, o) \ + (void)vnode_pager_setsize_mightsleep((v), (o), true) +#define vnode_pager_setsize_nonblock(v, o) \ + vnode_pager_setsize_mightsleep((v), (o), false) #endif /* _KERNEL */ #endif /* !_VM_EXTERN_H_ */ Index: vm/vnode_pager.c =================================================================== --- vm/vnode_pager.c +++ vm/vnode_pager.c @@ -430,21 +430,26 @@ * * Note: this routine may be invoked as a result of a pager put * operation (possibly at object termination time), so we must be careful. + * If can_sleep == false, this function can be called while a mutex or rw + * lock is held. In this case, EWOULDBLOCK will be returned if the operation + * cannot be completed. + * Otherwise, a return of 0 indicates success. */ -void -vnode_pager_setsize(struct vnode *vp, vm_ooffset_t nsize) +int +vnode_pager_setsize_mightsleep(struct vnode *vp, vm_ooffset_t nsize, + bool can_sleep) { vm_object_t object; vm_page_t m; vm_pindex_t nobjsize; if ((object = vp->v_object) == NULL) - return; + return (0); /* ASSERT_VOP_ELOCKED(vp, "vnode_pager_setsize and not locked vnode"); */ VM_OBJECT_WLOCK(object); if (object->type == OBJT_DEAD) { VM_OBJECT_WUNLOCK(object); - return; + return (0); } KASSERT(object->type == OBJT_VNODE, ("not vnode-backed object %p", object)); @@ -453,10 +458,15 @@ * Hasn't changed size */ VM_OBJECT_WUNLOCK(object); - return; + return (0); } nobjsize = OFF_TO_IDX(nsize + PAGE_MASK); if (nsize < object->un_pager.vnp.vnp_size) { + /* If can_sleep == false, return EWOULDBLOCK. */ + if (!can_sleep) { + VM_OBJECT_WUNLOCK(object); + return (EWOULDBLOCK); + } /* * File has shrunk. Toss any cached pages beyond the new EOF. */ @@ -510,6 +520,7 @@ object->un_pager.vnp.vnp_size = nsize; object->size = nobjsize; VM_OBJECT_WUNLOCK(object); + return (0); } /*