diff --git a/sys/fs/nfs/nfs_var.h.nodeall b/sys/fs/nfs/nfs_var.h --- a/sys/fs/nfs/nfs_var.h.nodeall +++ b/sys/fs/nfs/nfs_var.h @@ -551,6 +551,8 @@ void nfscl_freenfsclds(struct nfsclds *); int nfsrpc_allocate(vnode_t, off_t, off_t, struct nfsvattr *, int *, struct ucred *, NFSPROC_T *, void *); +int nfsrpc_deallocate(vnode_t, off_t, off_t, struct nfsvattr *, int *, + struct ucred *, NFSPROC_T *, void *); int nfsrpc_copy_file_range(vnode_t, off_t *, vnode_t, off_t *, size_t *, unsigned int, int *, struct nfsvattr *, int *, struct nfsvattr *, struct ucred *, bool, bool *); diff --git a/sys/fs/nfsclient/nfs_clrpcops.c.nodeall b/sys/fs/nfsclient/nfs_clrpcops.c --- a/sys/fs/nfsclient/nfs_clrpcops.c.nodeall +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -132,6 +132,8 @@ static int nfsrpc_writerpc(vnode_t , struct uio *, int *, int *, struct ucred *, nfsv4stateid_t *, NFSPROC_T *, struct nfsvattr *, int *, void *); +static int nfsrpc_deallocaterpc(vnode_t, off_t, off_t, nfsv4stateid_t *, + struct nfsvattr *, int *, struct ucred *, NFSPROC_T *, void *); static int nfsrpc_createv23(vnode_t , char *, int, struct vattr *, nfsquad_t, int, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *, struct nfsfh **, int *, int *, void *); @@ -2085,6 +2087,116 @@ m_freem(nd->nd_mrep); *iomode = committed; if (nd->nd_repstat && !error) + error = nd->nd_repstat; + return (error); +} + +/* + * Do an nfs deallocate operation. + */ +int +nfsrpc_deallocate(vnode_t vp, off_t offs, off_t len, struct nfsvattr *nap, + int *attrflagp, struct ucred *cred, NFSPROC_T *p, void *stuff) +{ + int error, expireret = 0, openerr, retrycnt; + uint32_t clidrev = 0; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); + struct nfsfh *nfhp; + nfsv4stateid_t stateid; + void *lckp; + + if (nmp->nm_clp != NULL) + clidrev = nmp->nm_clp->nfsc_clientidrev; + retrycnt = 0; + do { + lckp = NULL; + openerr = 1; + nfhp = VTONFS(vp)->n_fhp; + error = nfscl_getstateid(vp, nfhp->nfh_fh, nfhp->nfh_len, + NFSV4OPEN_ACCESSWRITE, 0, cred, p, &stateid, &lckp); + if (error != 0) { + /* + * No Open stateid, so try and open the file + * now. + */ + openerr = nfsrpc_open(vp, FWRITE, cred, p); + if (openerr == 0) + nfscl_getstateid(vp, nfhp->nfh_fh, + nfhp->nfh_len, NFSV4OPEN_ACCESSWRITE, 0, + cred, p, &stateid, &lckp); + } + error = nfsrpc_deallocaterpc(vp, offs, len, &stateid, nap, + attrflagp, cred, p, stuff); + if (error == NFSERR_STALESTATEID) + nfscl_initiate_recovery(nmp->nm_clp); + if (lckp != NULL) + nfscl_lockderef(lckp); + if (openerr == 0) + nfsrpc_close(vp, 0, p); + if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY || + error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) { + (void) nfs_catnap(PZERO, error, "nfs_deallocate"); + } else if ((error == NFSERR_EXPIRED || + error == NFSERR_BADSTATEID) && clidrev != 0) { + expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, p); + } + retrycnt++; + } while (error == NFSERR_GRACE || error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY || + error == NFSERR_BADSESSION || + (error == NFSERR_OLDSTATEID && retrycnt < 20) || + ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) && + expireret == 0 && clidrev != 0 && retrycnt < 4)); + if (error && retrycnt >= 4) + error = EIO; + return (error); +} + +/* + * The actual deallocate RPC. + */ +static int +nfsrpc_deallocaterpc(vnode_t vp, off_t offs, off_t len, + nfsv4stateid_t *stateidp, struct nfsvattr *nap, int *attrflagp, + struct ucred *cred, NFSPROC_T *p, void *stuff) +{ + uint32_t *tl; + struct nfsnode *np = VTONFS(vp); + int error, wccflag; + struct nfsrv_descript nfsd; + struct nfsrv_descript *nd = &nfsd; + nfsattrbit_t attrbits; + + *attrflagp = 0; + NFSCL_REQSTART(nd, NFSPROC_DEALLOCATE, vp); + nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID); + NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER); + txdr_hyper(offs, tl); + tl += 2; + txdr_hyper(len, tl); + NFSWRITEGETATTR_ATTRBIT(&attrbits); + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_GETATTR); + nfsrv_putattrbit(nd, &attrbits); + error = nfscl_request(nd, vp, p, cred, stuff); + if (error != 0) + return (error); + wccflag = 0; + error = nfscl_wcc_data(nd, vp, nap, attrflagp, &wccflag, stuff); + if (error != 0) + goto nfsmout; + if (nd->nd_repstat == 0) { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + error = nfsm_loadattr(nd, nap); + if (error != 0) + goto nfsmout; + *attrflagp = NFS_LATTR_NOSHRINK; + } + NFSWRITERPC_SETTIME(wccflag, np, nap, 1); +nfsmout: + m_freem(nd->nd_mrep); + if (nd->nd_repstat != 0 && error == 0) error = nd->nd_repstat; return (error); } diff --git a/sys/fs/nfsclient/nfs_clvnops.c.nodeall b/sys/fs/nfsclient/nfs_clvnops.c --- a/sys/fs/nfsclient/nfs_clvnops.c.nodeall +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -146,6 +146,7 @@ static vop_setacl_t nfs_setacl; static vop_advise_t nfs_advise; static vop_allocate_t nfs_allocate; +static vop_deallocate_t nfs_deallocate; static vop_copy_file_range_t nfs_copy_file_range; static vop_ioctl_t nfs_ioctl; static vop_getextattr_t nfs_getextattr; @@ -193,6 +194,7 @@ .vop_setacl = nfs_setacl, .vop_advise = nfs_advise, .vop_allocate = nfs_allocate, + .vop_deallocate = nfs_deallocate, .vop_copy_file_range = nfs_copy_file_range, .vop_ioctl = nfs_ioctl, .vop_getextattr = nfs_getextattr, @@ -3671,6 +3673,81 @@ */ if (error != 0) error = vop_stdallocate(ap); + if (attrflag != 0) { + ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); + if (error == 0 && ret != 0) + error = ret; + } + if (error != 0) + error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); + return (error); +} + +/* + * nfs deallocate call + */ +static int +nfs_deallocate(struct vop_deallocate_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct thread *td = curthread; + struct nfsvattr nfsva; + struct nfsmount *nmp; + off_t tlen; + int attrflag, error, ret; + + error = 0; + attrflag = 0; + nmp = VFSTONFS(vp->v_mount); + mtx_lock(&nmp->nm_mtx); + if (NFSHASNFSV4(nmp) && nmp->nm_minorvers >= NFSV42_MINORVERSION && + (nmp->nm_privflag & NFSMNTP_NODEALLOCATE) == 0) { + mtx_unlock(&nmp->nm_mtx); + tlen = omin(OFF_MAX - *ap->a_offset, *ap->a_len); + NFSCL_DEBUG(4, "dealloc: off=%jd len=%jd maxfilesize=%ju\n", + (intmax_t)*ap->a_offset, (intmax_t)tlen, + (uintmax_t)nmp->nm_maxfilesize); + if ((uint64_t)*ap->a_offset >= nmp->nm_maxfilesize) + error = EFBIG; + else if ((uint64_t)*ap->a_offset + tlen > nmp->nm_maxfilesize) + tlen = nmp->nm_maxfilesize - *ap->a_offset; + if (error == 0) + error = ncl_vinvalbuf(vp, V_SAVE, td, 1); + if (error == 0) { + vnode_pager_purge_range(vp, *ap->a_offset, + *ap->a_offset + tlen); + error = nfsrpc_deallocate(vp, *ap->a_offset, tlen, + &nfsva, &attrflag, ap->a_cred, td, NULL); + NFSCL_DEBUG(4, "dealloc: rpc=%d\n", error); + } + if (error == 0) { + NFSCL_DEBUG(4, "dealloc: attrflag=%d na_size=%ju\n", + attrflag, (uintmax_t)nfsva.na_size); + if (attrflag != 0) { + if ((uint64_t)*ap->a_offset + tlen >= + nfsva.na_size) + *ap->a_offset = nfsva.na_size; + else + *ap->a_offset += tlen; + } + *ap->a_len = 0; + } else if (error == NFSERR_NOTSUPP) { + mtx_lock(&nmp->nm_mtx); + nmp->nm_privflag |= NFSMNTP_NODEALLOCATE; + mtx_unlock(&nmp->nm_mtx); + } + } else { + mtx_unlock(&nmp->nm_mtx); + error = EIO; + } + /* + * If the NFS server cannot perform the Deallocate operation, just call + * vop_stddeallocate() to perform it. + */ + if (error != 0 && error != NFSERR_FBIG && error != NFSERR_INVAL) { + error = vop_stddeallocate(ap); + NFSCL_DEBUG(4, "dealloc: stddeallocate=%d\n", error); + } if (attrflag != 0) { ret = nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); if (error == 0 && ret != 0) diff --git a/sys/fs/nfsclient/nfsmount.h.nodeall b/sys/fs/nfsclient/nfsmount.h --- a/sys/fs/nfsclient/nfsmount.h.nodeall +++ b/sys/fs/nfsclient/nfsmount.h @@ -124,6 +124,7 @@ #define NFSMNTP_NOADVISE 0x00000100 #define NFSMNTP_NOALLOCATE 0x00000200 #define NFSMNTP_DELEGISSUED 0x00000400 +#define NFSMNTP_NODEALLOCATE 0x00000800 /* New mount flags only used by the kernel via nmount(2). */ #define NFSMNT_TLS 0x00000001 diff --git a/sys/kern/vfs_default.c.nodeall b/sys/kern/vfs_default.c --- a/sys/kern/vfs_default.c.nodeall +++ b/sys/kern/vfs_default.c @@ -93,7 +93,6 @@ static int vop_stdread_pgcache(struct vop_read_pgcache_args *ap); static int vop_stdstat(struct vop_stat_args *ap); static int vop_stdvput_pair(struct vop_vput_pair_args *ap); -static int vop_stddeallocate(struct vop_deallocate_args *ap); /* * This vnode table stores what we want to do if the filesystem doesn't @@ -1126,7 +1125,7 @@ return (error); } -static int +int vop_stddeallocate(struct vop_deallocate_args *ap) { struct vnode *vp; diff --git a/sys/sys/vnode.h.nodeall b/sys/sys/vnode.h --- a/sys/sys/vnode.h.nodeall +++ b/sys/sys/vnode.h @@ -861,6 +861,7 @@ int vop_stdadvlockasync(struct vop_advlockasync_args *ap); int vop_stdadvlockpurge(struct vop_advlockpurge_args *ap); int vop_stdallocate(struct vop_allocate_args *ap); +int vop_stddeallocate(struct vop_deallocate_args *ap); int vop_stdset_text(struct vop_set_text_args *ap); int vop_stdpathconf(struct vop_pathconf_args *); int vop_stdpoll(struct vop_poll_args *);