git: 08b9cc316a31 - main - nfscl: Add a VOP_DEALLOCATE() for the NFSv4.2 client

Rick Macklem rmacklem at FreeBSD.org
Sat Aug 28 01:35:29 UTC 2021


The branch main has been updated by rmacklem:

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

commit 08b9cc316a319fba95af8bb13b262fe0d5526ec3
Author:     Rick Macklem <rmacklem at FreeBSD.org>
AuthorDate: 2021-08-28 01:31:36 +0000
Commit:     Rick Macklem <rmacklem at FreeBSD.org>
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


More information about the dev-commits-src-all mailing list