The branch main has been updated by rmacklem:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=08b9cc316a319fba95af8bb13b262fe0d5526ec3

commit 08b9cc316a319fba95af8bb13b262fe0d5526ec3
Author:     Rick Macklem <[email protected]>
AuthorDate: 2021-08-28 01:31:36 +0000
Commit:     Rick Macklem <[email protected]>
CommitDate: 2021-08-28 01:31:36 +0000

    nfscl: Add a VOP_DEALLOCATE() for the NFSv4.2 client
    
    This patch adds a VOP_DEALLOCATE() to the NFS client.
    For NFSv4.2 servers that support the Deallocate operation,
    it is used. Otherwise, it falls back on calling
    vop_stddeallocate().
    
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D31640
---
 sys/fs/nfs/nfs_var.h            |   2 +
 sys/fs/nfsclient/nfs_clrpcops.c | 112 ++++++++++++++++++++++++++++++++++++++++
 sys/fs/nfsclient/nfs_clvnops.c  |  79 ++++++++++++++++++++++++++++
 sys/fs/nfsclient/nfsmount.h     |   1 +
 4 files changed, 194 insertions(+)

diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 5ddae345f906..bae4affa72e2 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -553,6 +553,8 @@ int nfscl_findlayoutforio(struct nfscllayout *, uint64_t, 
uint32_t,
 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 b/sys/fs/nfsclient/nfs_clrpcops.c
index de4030876bfe..f9636bc28365 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -132,6 +132,8 @@ static int nfsrpc_readrpc(vnode_t , struct uio *, struct 
ucred *,
 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 *);
@@ -2089,6 +2091,116 @@ nfsmout:
        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);
+}
+
 /*
  * nfs mknod rpc
  * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index 39340692563f..bfd77d50bc91 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -146,6 +146,7 @@ static vop_getacl_t nfs_getacl;
 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 @@ static struct vop_vector newnfs_vnodeops_nosig = {
        .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,
@@ -3681,6 +3683,83 @@ nfs_allocate(struct vop_allocate_args *ap)
        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) {
+                       /* Avoid EFBIG error return from the NFSv4.2 server. */
+                       *ap->a_len = 0;
+                       return (0);
+               }
+               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 < nfsva.na_size)
+                                       *ap->a_offset += omin((off_t)
+                                           nfsva.na_size - *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)
+                       error = ret;
+       }
+       if (error != 0)
+               error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
+       return (error);
+}
+
 /*
  * nfs copy_file_range call
  */
diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h
index a5997e474be9..d5771f157034 100644
--- a/sys/fs/nfsclient/nfsmount.h
+++ b/sys/fs/nfsclient/nfsmount.h
@@ -124,6 +124,7 @@ struct      nfsmount {
 #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
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to