Index: sys/fs/nfs/nfsport.h =================================================================== --- sys/fs/nfs/nfsport.h +++ sys/fs/nfs/nfsport.h @@ -878,6 +878,7 @@ int nfscl_loadattrcache(struct vnode **, struct nfsvattr *, void *, void *, int, int); int newnfs_realign(struct mbuf **, int); +bool ncl_pager_setsize(struct vnode *vp, u_quad_t *nsizep); /* * If the port runs on an SMP box that can enforce Atomic ops with low Index: sys/fs/nfsclient/nfs_clport.c =================================================================== --- sys/fs/nfsclient/nfs_clport.c +++ sys/fs/nfsclient/nfs_clport.c @@ -414,10 +414,7 @@ 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; error = 0; @@ -565,27 +562,42 @@ if (np->n_attrstamp != 0) KDTRACE_NFS_ATTRCACHE_LOAD_DONE(vp, vap, error); #endif + (void)ncl_pager_setsize(vp, NULL); + return (error); +} + +bool +ncl_pager_setsize(struct vnode *vp, u_quad_t *nsizep) +{ + struct nfsnode *np; + vm_object_t object; + struct vattr *vap; + u_quad_t nsize; + bool setnsize; + + np = VTONFS(vp); + NFSASSERTNODE(np); + + vap = &np->n_vattr.na_vattr; 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. - */ + + if (object != NULL && nsize != object->un_pager.vnp.vnp_size) { + if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) setnsize = true; - } else { + else + np->n_flag |= NVNSETSZSKIP; + } + if (nsizep == NULL) { + NFSUNLOCKNODE(np); + if (setnsize) vnode_pager_setsize(vp, nsize); - } + setnsize = false; + } else { + *nsizep = nsize; } - NFSUNLOCKNODE(np); - if (setnsize) - vnode_pager_setsize(vp, nsize); - return (error); + return (setnsize); } /* Index: sys/fs/nfsclient/nfs_clsubs.c =================================================================== --- sys/fs/nfsclient/nfs_clsubs.c +++ sys/fs/nfsclient/nfs_clsubs.c @@ -185,6 +185,8 @@ struct vattr *vap; struct nfsmount *nmp; int timeo, mustflush; + u_quad_t nsize; + bool setnsize; np = VTONFS(vp); vap = &np->n_vattr.na_vattr; @@ -230,6 +232,7 @@ return( ENOENT); } nfsstatsv1.attrcache_hits++; + setnsize = false; if (vap->va_size != np->n_size) { if (vap->va_type == VREG) { if (np->n_flag & NMODIFIED) { @@ -240,7 +243,7 @@ } else { np->n_size = vap->va_size; } - vnode_pager_setsize(vp, np->n_size); + setnsize = ncl_pager_setsize(vp, &nsize); } else { np->n_size = vap->va_size; } @@ -253,6 +256,8 @@ vaper->va_mtime = np->n_mtim; } NFSUNLOCKNODE(np); + if (setnsize) + vnode_pager_setsize(vp, nsize); KDTRACE_NFS_ATTRCACHE_GET_HIT(vp, vap); return (0); } Index: sys/fs/nfsclient/nfs_clvnops.c =================================================================== --- sys/fs/nfsclient/nfs_clvnops.c +++ sys/fs/nfsclient/nfs_clvnops.c @@ -142,6 +142,7 @@ static vop_advlockasync_t nfs_advlockasync; static vop_getacl_t nfs_getacl; static vop_setacl_t nfs_setacl; +static vop_lock1_t nfs_lock; /* * Global vfs data structures for nfs @@ -160,6 +161,7 @@ .vop_putpages = ncl_putpages, .vop_inactive = ncl_inactive, .vop_link = nfs_link, + .vop_lock1 = nfs_lock, .vop_lookup = nfs_lookup, .vop_mkdir = nfs_mkdir, .vop_mknod = nfs_mknod, @@ -295,6 +297,73 @@ * rep->r_mtx : Protects the fields in an nfsreq. */ +static int +nfs_lock(struct vop_lock1_args *ap) +{ + struct vnode *vp; + struct nfsnode *np; + u_quad_t nsize; + int error, lktype; + bool onfault; + + vp = ap->a_vp; + lktype = ap->a_flags & LK_TYPE_MASK; + error = VOP_LOCK1_APV(&default_vnodeops, ap); + if (error != 0 || vp->v_op != &newnfs_vnodeops) + return (error); + np = VTONFS(vp); + NFSLOCKNODE(np); + if ((np->n_flag & NVNSETSZSKIP) == 0 || (lktype != LK_SHARED && + lktype != LK_EXCLUSIVE && lktype != LK_UPGRADE && + lktype != LK_TRYUPGRADE)) { + NFSUNLOCKNODE(np); + return (0); + } + onfault = (ap->a_flags & LK_EATTR_MASK) == LK_NOWAIT && + (ap->a_flags & LK_INIT_MASK) == LK_CANRECURSE && + (lktype == LK_SHARED || lktype == LK_EXCLUSIVE); + if (onfault && vp->v_vnlock->lk_recurse == 0) { + /* + * Force retry in vm_fault(), to make the lock request + * sleepable, which allows us to piggy-back the + * sleepable call to vnode_pager_setsize(). + */ + NFSUNLOCKNODE(np); + VOP_UNLOCK(vp, 0); + return (EBUSY); + } + if ((ap->a_flags & LK_NOWAIT) != 0 || + (lktype == LK_SHARED && vp->v_vnlock->lk_recurse > 0)) { + NFSUNLOCKNODE(np); + return (0); + } + if (lktype == LK_SHARED) { + NFSUNLOCKNODE(np); + VOP_UNLOCK(vp, 0); + ap->a_flags &= ~(LK_TYPE_MASK | LK_INTERLOCK); + ap->a_flags |= LK_EXCLUSIVE; + error = VOP_LOCK1_APV(&default_vnodeops, ap); + if (error != 0 || vp->v_op != &newnfs_vnodeops) + return (error); + NFSLOCKNODE(np); + if ((np->n_flag & NVNSETSZSKIP) == 0) { + NFSUNLOCKNODE(np); + goto downgrade; + } + } + np->n_flag &= ~NVNSETSZSKIP; + nsize = np->n_size; + NFSUNLOCKNODE(np); + vnode_pager_setsize(vp, nsize); +downgrade: + if (lktype == LK_SHARED) { + ap->a_flags &= ~(LK_TYPE_MASK | LK_INTERLOCK); + ap->a_flags |= LK_DOWNGRADE; + (void)VOP_LOCK1_APV(&default_vnodeops, ap); + } + return (0); +} + static int nfs34_access_otw(struct vnode *vp, int wmode, struct thread *td, struct ucred *cred, u_int32_t *retmode) Index: sys/fs/nfsclient/nfsnode.h =================================================================== --- sys/fs/nfsclient/nfsnode.h +++ sys/fs/nfsclient/nfsnode.h @@ -163,6 +163,7 @@ #define NWRITEOPENED 0x00040000 /* Has been opened for writing */ #define NHASBEENLOCKED 0x00080000 /* Has been file locked. */ #define NDSCOMMIT 0x00100000 /* Commit is done via the DS. */ +#define NVNSETSZSKIP 0x00200000 /* Skipped vnode_pager_setsize() */ /* * Convert between nfsnode pointers and vnode pointers Index: sys/vm/vm_fault.c =================================================================== --- sys/vm/vm_fault.c +++ sys/vm/vm_fault.c @@ -839,7 +839,12 @@ */ if (fs.object->type != OBJT_DEFAULT || fs.object == fs.first_object) { - if (fs.pindex >= fs.object->size) { + /* + * The vnode case is handled later, after the + * vnode is locked and just before pagein. + */ + if (fs.object->type != OBJT_VNODE && + fs.pindex >= fs.object->size) { unlock_and_deallocate(&fs); return (KERN_OUT_OF_BOUNDS); } @@ -1031,6 +1036,11 @@ } fs.vp = vp; } + if (fs.object->type == OBJT_VNODE && + fs.pindex >= fs.object->size) { + unlock_and_deallocate(&fs); + return (KERN_OUT_OF_BOUNDS); + } KASSERT(fs.vp == NULL || !fs.map->system_map, ("vm_fault: vnode-backed object mapped by system map")); Index: sys/vm/vnode_pager.c =================================================================== --- sys/vm/vnode_pager.c +++ sys/vm/vnode_pager.c @@ -440,7 +440,7 @@ if ((object = vp->v_object) == NULL) return; -/* ASSERT_VOP_ELOCKED(vp, "vnode_pager_setsize and not locked vnode"); */ + ASSERT_VOP_ELOCKED(vp, "vnode_pager_setsize and not locked vnode"); VM_OBJECT_WLOCK(object); if (object->type == OBJT_DEAD) { VM_OBJECT_WUNLOCK(object);