svn commit: r350337 - in projects/nfsv42/sys/fs: nfs nfsclient nfsserver

Rick Macklem rmacklem at FreeBSD.org
Fri Jul 26 00:27:49 UTC 2019


Author: rmacklem
Date: Fri Jul 26 00:27:47 2019
New Revision: 350337
URL: https://svnweb.freebsd.org/changeset/base/350337

Log:
  Add support for Copy (the intra-server case) to the NFSv4.2 client and server.
  
  The NFSv4.2 server now has a method for VOP_COPY_FILE_RANGE() that will do
  the Copy intra-server operation on an NFSv4.2 server. If this operation fails
  with EACCES, it will be retried with the other credentials, since an NFS
  RPC only takes one set of credentials.
  If the operation fails due to lack of support (or both attempts fail with
  EACCES), then it falls back on vn_generic_copy_file_range() using read/write.
  The NFSv4.2 server uses vn_generic_copy_file_range() to perform the Copy
  operation.

Modified:
  projects/nfsv42/sys/fs/nfs/nfs_commonsubs.c
  projects/nfsv42/sys/fs/nfs/nfs_var.h
  projects/nfsv42/sys/fs/nfs/nfsport.h
  projects/nfsv42/sys/fs/nfs/nfsproto.h
  projects/nfsv42/sys/fs/nfsclient/nfs_clrpcops.c
  projects/nfsv42/sys/fs/nfsclient/nfs_clvnops.c
  projects/nfsv42/sys/fs/nfsclient/nfsmount.h
  projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c
  projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsocket.c
  projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsubs.c

Modified: projects/nfsv42/sys/fs/nfs/nfs_commonsubs.c
==============================================================================
--- projects/nfsv42/sys/fs/nfs/nfs_commonsubs.c	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfs/nfs_commonsubs.c	Fri Jul 26 00:27:47 2019	(r350337)
@@ -168,7 +168,7 @@ struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS] = {
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 },		/* Destroy ClientID */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* Reclaim Complete */
 	{ 0, 1, 1, 1, LK_EXCLUSIVE, 1, 0 },		/* Allocate */
-	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Copy */
+	{ 2, 1, 1, 0, LK_SHARED, 1, 0 },		/* Copy */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Copy Notify */
 	{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Deallocate */
 	{ 0, 1, 0, 1, LK_SHARED, 1, 0 },		/* IO Advise */
@@ -206,7 +206,7 @@ static struct nfsrv_lughash	*nfsgroupnamehash;
  */
 static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
 
 /* local functions */
 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
@@ -282,6 +282,7 @@ static struct {
 	{ NFSV4OP_OPEN, 8, "CreateLayGet", 12, },
 	{ NFSV4OP_IOADVISE, 1, "Advise", 6, },
 	{ NFSV4OP_ALLOCATE, 2, "Allocate", 8, },
+	{ NFSV4OP_SAVEFH, 5, "Copy", 4, },
 };
 
 /*
@@ -290,7 +291,7 @@ static struct {
 static int nfs_bigrequest[NFSV42_NPROCS] = {
 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0
+	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
 };
 
 /*

Modified: projects/nfsv42/sys/fs/nfs/nfs_var.h
==============================================================================
--- projects/nfsv42/sys/fs/nfs/nfs_var.h	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfs/nfs_var.h	Fri Jul 26 00:27:47 2019	(r350337)
@@ -279,6 +279,8 @@ int nfsrvd_teststateid(struct nfsrv_descript *, int,
     vnode_t, struct nfsexstuff *);
 int nfsrvd_allocate(struct nfsrv_descript *, int,
     vnode_t, struct nfsexstuff *);
+int nfsrvd_copy_file_range(struct nfsrv_descript *, int,
+    vnode_t, vnode_t, struct nfsexstuff *, struct nfsexstuff *);
 int nfsrvd_notsupp(struct nfsrv_descript *, int,
     vnode_t, struct nfsexstuff *);
 
@@ -535,6 +537,9 @@ int nfscl_findlayoutforio(struct nfscllayout *, uint64
 void nfscl_freenfsclds(struct nfsclds *);
 int nfsrpc_allocate(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 *);
 
 /* nfs_clstate.c */
 int nfscl_open(vnode_t, u_int8_t *, int, u_int32_t, int,

Modified: projects/nfsv42/sys/fs/nfs/nfsport.h
==============================================================================
--- projects/nfsv42/sys/fs/nfs/nfsport.h	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfs/nfsport.h	Fri Jul 26 00:27:47 2019	(r350337)
@@ -396,11 +396,12 @@
 /* Additional procedures for NFSv4.2. */
 #define	NFSPROC_IOADVISE	56
 #define	NFSPROC_ALLOCATE	57
+#define	NFSPROC_COPY		58
 
 /*
  * Must be defined as one higher than the last NFSv4.2 Proc# above.
  */
-#define	NFSV42_NPROCS		58
+#define	NFSV42_NPROCS		59
 
 #endif	/* NFS_V3NPROCS */
 
@@ -429,7 +430,7 @@ struct nfsstatsv1 {
 	uint64_t	readlink_bios;
 	uint64_t	biocache_readdirs;
 	uint64_t	readdir_bios;
-	uint64_t	rpccnt[NFSV42_NPROCS + 11];
+	uint64_t	rpccnt[NFSV42_NPROCS + 10];
 	uint64_t	rpcretries;
 	uint64_t	srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS];
 	uint64_t	srvrpc_errs;

Modified: projects/nfsv42/sys/fs/nfs/nfsproto.h
==============================================================================
--- projects/nfsv42/sys/fs/nfs/nfsproto.h	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfs/nfsproto.h	Fri Jul 26 00:27:47 2019	(r350337)
@@ -215,6 +215,18 @@
 #define	NFSERR_RETURNCONFLICT	10086
 #define	NFSERR_DELEGREVOKED	10087
 
+/* NFSv4.2 specific errors. */
+#define	NFSERR_PARTNERNOTSUPP	10088
+#define	NFSERR_PARTNERNOAUTH	10089
+#define	NFSERR_UNIONNOTSUPP	10090
+#define	NFSERR_OFFLOADDENIED	10091
+#define	NFSERR_WRONGLFS		10092
+#define	NFSERR_BADLABEL		10093
+#define	NFSERR_OFFLOADNOREQS	10094
+
+/* Maximum value of all the NFS error values. */
+#define	NFSERR_MAXERRVAL	NFSERR_OFFLOADNOREQS
+
 #define	NFSERR_STALEWRITEVERF	30001	/* Fake return for nfs_commit() */
 #define	NFSERR_DONTREPLY	30003	/* Don't process request */
 #define	NFSERR_RETVOID		30004	/* Return void, not error */
@@ -368,11 +380,12 @@
 /* Additional procedures for NFSv4.2. */
 #define	NFSPROC_IOADVISE	56
 #define	NFSPROC_ALLOCATE	57
+#define	NFSPROC_COPY		58
 
 /*
  * Must be defined as one higher than the last NFSv4.2 Proc# above.
  */
-#define	NFSV42_NPROCS		58
+#define	NFSV42_NPROCS		59
 
 #endif	/* NFS_V3NPROCS */
 

Modified: projects/nfsv42/sys/fs/nfsclient/nfs_clrpcops.c
==============================================================================
--- projects/nfsv42/sys/fs/nfsclient/nfs_clrpcops.c	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfsclient/nfs_clrpcops.c	Fri Jul 26 00:27:47 2019	(r350337)
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
 #include "opt_inet6.h"
 
 #include <fs/nfs/nfsport.h>
+#include <fs/nfsclient/nfs.h>
 #include <sys/sysctl.h>
 #include <sys/taskqueue.h>
 
@@ -212,6 +213,9 @@ static int nfsrpc_layoutget(struct nfsmount *, uint8_t
 static int nfsrpc_layoutgetres(struct nfsmount *, vnode_t, uint8_t *,
     int, nfsv4stateid_t *, int, uint32_t *, struct nfscllayout **,
     struct nfsclflayouthead *, int, int, int *, struct ucred *, NFSPROC_T *);
+static int nfsrpc_copyrpc(vnode_t, off_t, vnode_t, off_t, size_t *,
+    nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *,
+    struct nfsvattr *, int *, bool, int *, struct ucred *, NFSPROC_T *);
 
 int nfs_pnfsio(task_fn_t *, void *);
 
@@ -7946,5 +7950,194 @@ out:
 			*islockedp = 1;
 	}
 	return (laystat);
+}
+
+/*
+ * nfs copy_file_range operation.
+ */
+APPLESTATIC int
+nfsrpc_copy_file_range(vnode_t invp, off_t *inoffp, vnode_t outvp,
+    off_t *outoffp, size_t *lenp, unsigned int flags, int *inattrflagp,
+    struct nfsvattr *innap, int *outattrflagp, struct nfsvattr *outnap,
+    struct ucred *cred, bool consecutive, bool *must_commitp)
+{
+	int commit, error, expireret = 0, retrycnt;
+	u_int32_t clidrev = 0;
+	struct nfsmount *nmp = VFSTONFS(vnode_mount(invp));
+	struct nfsfh *innfhp = NULL, *outnfhp = NULL;
+	nfsv4stateid_t instateid, outstateid;
+	void *inlckp, *outlckp;
+
+	if (nmp->nm_clp != NULL)
+		clidrev = nmp->nm_clp->nfsc_clientidrev;
+	innfhp = VTONFS(invp)->n_fhp;
+	outnfhp = VTONFS(outvp)->n_fhp;
+	retrycnt = 0;
+	do {
+		/* Get both stateids. */
+		inlckp = NULL;
+		nfscl_getstateid(invp, innfhp->nfh_fh, innfhp->nfh_len,
+		    NFSV4OPEN_ACCESSREAD, 0, NULL, curthread, &instateid,
+		    &inlckp);
+		outlckp = NULL;
+		nfscl_getstateid(outvp, outnfhp->nfh_fh, outnfhp->nfh_len,
+		    NFSV4OPEN_ACCESSWRITE, 0, NULL, curthread, &outstateid,
+		    &outlckp);
+
+		error = nfsrpc_copyrpc(invp, *inoffp, outvp, *outoffp, lenp,
+		    &instateid, &outstateid, innap, inattrflagp, outnap,
+		    outattrflagp, consecutive, &commit, cred, curthread);
+		if (error == 0) {
+			if (commit != NFSWRITE_FILESYNC)
+				*must_commitp = true;
+			*inoffp += *lenp;
+			*outoffp += *lenp;
+		} else if (error == NFSERR_STALESTATEID)
+			nfscl_initiate_recovery(nmp->nm_clp);
+		if (inlckp != NULL)
+			nfscl_lockderef(inlckp);
+		if (outlckp != NULL)
+			nfscl_lockderef(outlckp);
+		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_cfr");
+		} else if ((error == NFSERR_EXPIRED ||
+		    error == NFSERR_BADSTATEID) && clidrev != 0) {
+			expireret = nfscl_hasexpired(nmp->nm_clp, clidrev,
+			    curthread);
+		}
+		retrycnt++;
+	} while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
+	    error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
+	      error == NFSERR_STALEDONTRECOVER ||
+	    (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
+	    ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
+	     expireret == 0 && clidrev != 0 && retrycnt < 4));
+	if (error != 0 && (retrycnt >= 4 ||
+	    error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
+	      error == NFSERR_STALEDONTRECOVER))
+		error = EIO;
+	return (error);
+}
+
+/*
+ * The copy RPC.
+ */
+static int
+nfsrpc_copyrpc(vnode_t invp, off_t inoff, vnode_t outvp, off_t outoff,
+    size_t *lenp, nfsv4stateid_t *instateidp, nfsv4stateid_t *outstateidp,
+    struct nfsvattr *innap, int *inattrflagp, struct nfsvattr *outnap,
+    int *outattrflagp, bool consecutive, int *commitp, struct ucred *cred,
+    NFSPROC_T *p)
+{
+	uint32_t *tl;
+	int error;
+	struct nfsrv_descript nfsd;
+	struct nfsrv_descript *nd = &nfsd;
+	struct nfsmount *nmp;
+	nfsattrbit_t attrbits;
+	uint64_t len;
+
+	nmp = VFSTONFS(outvp->v_mount);
+	*inattrflagp = *outattrflagp = 0;
+	*commitp = NFSWRITE_UNSTABLE;
+	len = *lenp;
+	*lenp = 0;
+	NFSCL_REQSTART(nd, NFSPROC_COPY, invp);
+	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_GETATTR);
+	NFSGETATTR_ATTRBIT(&attrbits);
+	nfsrv_putattrbit(nd, &attrbits);
+	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_PUTFH);
+	nfsm_fhtom(nd, VTONFS(outvp)->n_fhp->nfh_fh,
+	    VTONFS(outvp)->n_fhp->nfh_len, 0);
+	NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+	*tl = txdr_unsigned(NFSV4OP_COPY);
+	nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID);
+	nfsm_stateidtom(nd, outstateidp, NFSSTATEID_PUTSTATEID);
+	NFSM_BUILD(tl, uint32_t *, 3 * NFSX_HYPER + 4 * NFSX_UNSIGNED);
+	txdr_hyper(inoff, tl); tl += 2;
+	txdr_hyper(outoff, tl); tl += 2;
+	txdr_hyper(len, tl); tl += 2;
+	if (consecutive)
+		*tl++ = newnfs_true;
+	else
+		*tl++ = newnfs_false;
+	*tl++ = newnfs_true;
+	*tl++ = 0;
+	*tl = txdr_unsigned(NFSV4OP_GETATTR);
+	NFSWRITEGETATTR_ATTRBIT(&attrbits);
+	nfsrv_putattrbit(nd, &attrbits);
+	error = nfscl_request(nd, invp, p, cred, NULL);
+	if (error != 0)
+		return (error);
+	if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+		/* Get the input file's attributes. */
+		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+		if (*(tl + 1) == 0) {
+			error = nfsm_loadattr(nd, innap);
+			if (error != 0)
+				goto nfsmout;
+			*inattrflagp = 1;
+		} else
+			nd->nd_flag |= ND_NOMOREDATA;
+	}
+	/* Skip over return stat for PutFH. */
+	if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+		if (*++tl != 0)
+			nd->nd_flag |= ND_NOMOREDATA;
+	}
+	/* Skip over return stat for Copy. */
+	if ((nd->nd_flag & ND_NOMOREDATA) == 0)
+		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+	if (nd->nd_repstat == 0) {
+		NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+		if (*tl != 0) {
+			/* There should be no callback ids. */
+			error = NFSERR_BADXDR;
+			goto nfsmout;
+		}
+		NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 3 * NFSX_UNSIGNED +
+		    NFSX_VERF);
+		len = fxdr_hyper(tl); tl += 2;
+		*commitp = fxdr_unsigned(int, *tl++);
+		NFSLOCKMNT(nmp);
+		if (!NFSHASWRITEVERF(nmp)) {
+			NFSBCOPY(tl, nmp->nm_verf, NFSX_VERF);
+			NFSSETWRITEVERF(nmp);
+		}
+		NFSUNLOCKMNT(nmp);
+		tl += (NFSX_VERF / NFSX_UNSIGNED);
+		if (nd->nd_repstat == 0 && *++tl != newnfs_true)
+			/* Must be a synchronous copy. */
+			nd->nd_repstat = NFSERR_NOTSUPP;
+		NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+		error = nfsm_loadattr(nd, outnap);
+		if (error == 0)
+			*outattrflagp = NFS_LATTR_NOSHRINK;
+		if (nd->nd_repstat == 0)
+			*lenp = len;
+	} else if (nd->nd_repstat == NFSERR_OFFLOADNOREQS) {
+		/*
+		 * For the case where consecutive is not supported, but
+		 * synchronous is supported, we can try consecutive == false
+		 * by returning this error.  Otherwise, return NFSERR_NOTSUPP,
+		 * since Copy cannot be done.
+		 */
+		if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+			NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+			if (!consecutive || *++tl == newnfs_false)
+				nd->nd_repstat = NFSERR_NOTSUPP;
+		} else
+			nd->nd_repstat = NFSERR_BADXDR;
+	}
+	if (error == 0)
+		error = nd->nd_repstat;
+nfsmout:
+	mbuf_freem(nd->nd_mrep);
+	return (error);
 }
 

Modified: projects/nfsv42/sys/fs/nfsclient/nfs_clvnops.c
==============================================================================
--- projects/nfsv42/sys/fs/nfsclient/nfs_clvnops.c	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfsclient/nfs_clvnops.c	Fri Jul 26 00:27:47 2019	(r350337)
@@ -144,6 +144,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_copy_file_range_t nfs_copy_file_range;
 
 /*
  * Global vfs data structures for nfs
@@ -183,6 +184,7 @@ static struct vop_vector newnfs_vnodeops_nosig = {
 	.vop_setacl =		nfs_setacl,
 	.vop_advise =		nfs_advise,
 	.vop_allocate =		nfs_allocate,
+	.vop_copy_file_range =	nfs_copy_file_range,
 };
 
 static int
@@ -3509,6 +3511,162 @@ nfs_allocate(struct vop_allocate_args *ap)
 	}
 	if (error != 0)
 		error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
+	return (error);
+}
+
+/*
+ * nfs copy_file_range call
+ */
+static int
+nfs_copy_file_range(struct vop_copy_file_range_args *ap)
+{
+	struct vnode *invp = ap->a_invp;
+	struct vnode *outvp = ap->a_outvp;
+	struct mount *mp;
+	struct nfsvattr innfsva, outnfsva;
+	struct uio io;
+	struct nfsmount *nmp;
+	size_t len, len2;
+	int error, inattrflag, outattrflag, ret;
+	off_t inoff, outoff;
+	bool consecutive, must_commit, tryoutcred;
+
+	nmp = VFSTONFS(invp->v_mount);
+	mtx_lock(&nmp->nm_mtx);
+	if (!NFSHASNFSV4(nmp) || nmp->nm_minorvers < NFSV42_MINORVERSION ||
+	    (nmp->nm_privflag & NFSMNTP_NOCOPY) != 0) {
+		mtx_unlock(&nmp->nm_mtx);
+		error = vn_generic_copy_file_range(ap->a_invp, ap->a_inoffp,
+		    ap->a_outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags,
+		    ap->a_incred, ap->a_outcred, ap->a_fsizetd);
+		return (error);
+	}
+	mtx_unlock(&nmp->nm_mtx);
+
+	/* Lock both vnodes, avoiding risk of deadlock. */
+	do {
+		mp = NULL;
+		error = vn_start_write(outvp, &mp, V_WAIT);
+		if (error == 0) {
+			error = vn_lock(outvp, LK_EXCLUSIVE);
+			if (error == 0) {
+				error = vn_lock(invp, LK_SHARED | LK_NOWAIT);
+				if (error == 0)
+					break;
+				VOP_UNLOCK(outvp, 0);
+				if (mp != NULL)
+					vn_finished_write(mp);
+				mp = NULL;
+				error = vn_lock(invp, LK_SHARED);
+				if (error == 0)
+					VOP_UNLOCK(invp, 0);
+			}
+		}
+		if (mp != NULL)
+			vn_finished_write(mp);
+	} while (error == 0);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Do the vn_rlimit_fsize() check.  Should this be above the VOP layer?
+	 */
+	io.uio_offset = *ap->a_outoffp;
+	io.uio_resid = *ap->a_lenp;
+	error = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd);
+	/* Do the actual NFSv4.2 RPC. */
+	len = *ap->a_lenp;
+	mtx_lock(&nmp->nm_mtx);
+	if ((nmp->nm_privflag & NFSMNTP_NOCONSECUTIVE) == 0)
+		consecutive = true;
+	else
+		consecutive = false;
+	mtx_unlock(&nmp->nm_mtx);
+	inoff = *ap->a_inoffp;
+	outoff = *ap->a_outoffp;
+	tryoutcred = true;
+	while (len > 0 && error == 0) {
+		inattrflag = outattrflag = 0;
+		must_commit = false;
+		len2 = len;
+		if (tryoutcred)
+			error = nfsrpc_copy_file_range(invp, ap->a_inoffp,
+			    outvp, ap->a_outoffp, &len2, ap->a_flags,
+			    &inattrflag, &innfsva, &outattrflag, &outnfsva,
+			    ap->a_outcred, consecutive, &must_commit);
+		else
+			error = nfsrpc_copy_file_range(invp, ap->a_inoffp,
+			    outvp, ap->a_outoffp, &len2, ap->a_flags,
+			    &inattrflag, &innfsva, &outattrflag, &outnfsva,
+			    ap->a_incred, consecutive, &must_commit);
+		if (inattrflag != 0) {
+			ret = nfscl_loadattrcache(&invp, &innfsva, NULL, NULL,
+			    0, 1);
+			if (error == 0 && ret != 0)
+				error = ret;
+		}
+		if (outattrflag != 0) {
+			ret = nfscl_loadattrcache(&outvp, &outnfsva, NULL, NULL,
+			    1, 1);
+			if (error == 0 && ret != 0)
+				error = ret;
+		}
+		if (error == 0) {
+			if (consecutive == false) {
+				if (len2 == len) {
+					mtx_lock(&nmp->nm_mtx);
+					nmp->nm_privflag |=
+					    NFSMNTP_NOCONSECUTIVE;
+					mtx_unlock(&nmp->nm_mtx);
+				} else
+					error = NFSERR_OFFLOADNOREQS;
+			}
+			len -= len2;
+		} else if (error == NFSERR_OFFLOADNOREQS && consecutive) {
+			/*
+			 * Try consecutive == false, which is ok only if all
+			 * bytes are copied.
+			 */
+			consecutive = false;
+			error = 0;
+		} else if (error == NFSERR_ACCES && tryoutcred) {
+			/* Try again with incred. */
+			tryoutcred = false;
+			error = 0;
+		}
+	}
+	if (must_commit && error == 0)
+		error = ncl_commit(outvp, outoff, *ap->a_lenp, ap->a_outcred,
+		    curthread);
+	VOP_UNLOCK(invp, 0);
+	VOP_UNLOCK(outvp, 0);
+	if (mp != NULL)
+		vn_finished_write(mp);
+	if (error == NFSERR_NOTSUPP || error == NFSERR_OFFLOADNOREQS ||
+	    error == NFSERR_ACCES) {
+		/*
+		 * Unlike the NFSv4.2 Copy, vn_generic_copy_file_range() can
+		 * use a_incred for the read and a_outcred for the write, so
+		 * try this for NFSERR_ACCES failures for the Copy.
+		 * For NFSERR_NOTSUPP and NFSERR_OFFLOADNOREQS, the Copy can
+		 * never succeed, so disable it.
+		 */
+		if (error != NFSERR_ACCES) {
+			/* Can never do Copy on this mount. */
+			mtx_lock(&nmp->nm_mtx);
+			nmp->nm_privflag |= NFSMNTP_NOCOPY;
+			mtx_unlock(&nmp->nm_mtx);
+		}
+		*ap->a_inoffp = inoff;
+		*ap->a_outoffp = outoff;
+		error = vn_generic_copy_file_range(ap->a_invp, ap->a_inoffp,
+		    ap->a_outvp, ap->a_outoffp, ap->a_lenp, ap->a_flags,
+		    ap->a_incred, ap->a_outcred, ap->a_fsizetd);
+	} else if (error != 0)
+		*ap->a_lenp = 0;
+
+	if (error != 0)
+		error = nfscl_maperr(curthread, error, (uid_t)0, (gid_t)0);
 	return (error);
 }
 

Modified: projects/nfsv42/sys/fs/nfsclient/nfsmount.h
==============================================================================
--- projects/nfsv42/sys/fs/nfsclient/nfsmount.h	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfsclient/nfsmount.h	Fri Jul 26 00:27:47 2019	(r350337)
@@ -106,6 +106,8 @@ struct	nfsmount {
 #define	NFSMNTP_FORCEDISM	0x00000001
 #define	NFSMNTP_CANCELRPCS	0x00000002
 #define	NFSMNTP_IOADVISETHRUMDS	0x00000004
+#define	NFSMNTP_NOCOPY		0x00000008
+#define	NFSMNTP_NOCONSECUTIVE	0x00000010
 
 #define	NFSMNT_DIRPATH(m)	(&((m)->nm_name[(m)->nm_krbnamelen + 1]))
 #define	NFSMNT_SRVKRBNAME(m)						\

Modified: projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c
==============================================================================
--- projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfsserver/nfs_nfsdserv.c	Fri Jul 26 00:27:47 2019	(r350337)
@@ -74,6 +74,9 @@ SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nf
 extern int	nfsrv_doflexfile;
 SYSCTL_INT(_vfs_nfsd, OID_AUTO, default_flexfile, CTLFLAG_RW,
     &nfsrv_doflexfile, 0, "Make Flex File Layout the default for pNFS");
+static int	nfsrv_maxcopyrange = 10 * 1024 * 1024;
+SYSCTL_INT(_vfs_nfsd, OID_AUTO, maxcopyrange, CTLFLAG_RW,
+    &nfsrv_maxcopyrange, 0, "Max size of a Copy so RPC times reasonable");
 
 /*
  * This list defines the GSS mechanisms supported.
@@ -5179,6 +5182,214 @@ nfsrvd_allocate(struct nfsrv_descript *nd, __unused in
 	return (0);
 nfsmout:
 	vput(vp);
+	NFSEXITCODE2(error, nd);
+	return (error);
+}
+
+/*
+ * nfs copy service
+ */
+APPLESTATIC int
+nfsrvd_copy_file_range(struct nfsrv_descript *nd, int isdgram,
+    vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
+{
+	uint32_t *tl;
+	struct nfsvattr at;
+	int cnt, error = 0, ret;
+	off_t inoff, outoff;
+	uint64_t len;
+	size_t xfer;
+	struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
+	struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
+	nfsquad_t clientid;
+	nfsv4stateid_t stateid;
+	nfsattrbit_t attrbits;
+	void *rl_rcookie, *rl_wcookie;
+
+	rl_rcookie = rl_wcookie = NULL;
+	if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) {
+		nd->nd_repstat = NFSERR_WRONGSEC;
+		goto nfsmout;
+	}
+	NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER +
+	    3 * NFSX_UNSIGNED);
+	instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
+	inlop->lo_flags = NFSLCK_READ;
+	instp->ls_ownerlen = 0;
+	instp->ls_op = NULL;
+	instp->ls_uid = nd->nd_cred->cr_uid;
+	instp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+	clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
+	clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
+	if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
+		clientid.qval = nd->nd_clientid.qval;
+	instp->ls_stateid.other[2] = *tl++;
+	outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
+	outlop->lo_flags = NFSLCK_WRITE;
+	outstp->ls_ownerlen = 0;
+	outstp->ls_op = NULL;
+	outstp->ls_uid = nd->nd_cred->cr_uid;
+	outstp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+	outstp->ls_stateid.other[0] = *tl++;
+	outstp->ls_stateid.other[1] = *tl++;
+	outstp->ls_stateid.other[2] = *tl++;
+	inoff = fxdr_hyper(tl); tl += 2;
+	inlop->lo_first = inoff;
+	outoff = fxdr_hyper(tl); tl += 2;
+	outlop->lo_first = outoff;
+	len = fxdr_hyper(tl); tl += 2;
+	if (len == 0) {
+		/* len == 0 means to EOF. */
+		inlop->lo_end = OFF_MAX;
+		outlop->lo_end = OFF_MAX;
+	} else {
+		inlop->lo_end = inlop->lo_first + len;
+		outlop->lo_end = outlop->lo_first + len;
+	}
+
+	/* Only supports synchronous Copy. */
+	if (*++tl != newnfs_true) {
+		nd->nd_repstat = NFSERR_OFFLOADNOREQS;
+		NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+		*tl++ = newnfs_true;
+		*tl = newnfs_true;
+		goto nfsmout;
+	}
+	cnt = fxdr_unsigned(int, *++tl);
+	if ((nd->nd_flag & ND_DSSERVER) != 0 || cnt != 0)
+		nd->nd_repstat = NFSERR_NOTSUPP;
+	if (nd->nd_repstat == 0 && (inoff > OFF_MAX || outoff > OFF_MAX ||
+	    inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
+	    inlop->lo_end < inlop->lo_first || outlop->lo_end <
+	    outlop->lo_first))
+		nd->nd_repstat = NFSERR_INVAL;
+
+	if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
+		nd->nd_repstat = NFSERR_WRONGTYPE;
+
+	/* Check permissions for the input file. */
+	NFSZERO_ATTRBIT(&attrbits);
+	NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
+	ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
+	if (nd->nd_repstat == 0)
+		nd->nd_repstat = ret;
+	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
+	     NFSVNO_EXSTRICTACCESS(exp)))
+		nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
+		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
+		    NULL);
+	if (nd->nd_repstat == 0)
+		nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
+		    clientid, &stateid, exp, nd, curthread);
+	NFSVOPUNLOCK(vp, 0);
+	if (nd->nd_repstat != 0)
+		goto out;
+
+	error = NFSVOPLOCK(tovp, LK_SHARED);
+	if (error != 0)
+		goto out;
+	if (vnode_vtype(tovp) != VREG)
+		nd->nd_repstat = NFSERR_WRONGTYPE;
+
+	/* For the output file, we only need the Owner attribute. */
+	ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
+	if (nd->nd_repstat == 0)
+		nd->nd_repstat = ret;
+	if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
+	     NFSVNO_EXSTRICTACCESS(exp)))
+		nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
+		    curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
+		    NULL);
+	if (nd->nd_repstat == 0)
+		nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
+		    clientid, &stateid, toexp, nd, curthread);
+	NFSVOPUNLOCK(tovp, 0);
+
+	/* Range lock the byte ranges for both invp and outvp. */
+	if (nd->nd_repstat == 0) {
+		for (;;) {
+			if (len == 0) {
+				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
+				    OFF_MAX);
+				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
+				    OFF_MAX);
+			} else {
+				rl_wcookie = vn_rangelock_wlock(tovp, outoff,
+				    outoff + len);
+				rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
+				    inoff + len);
+			}
+			if (rl_rcookie != NULL)
+				break;
+			vn_rangelock_unlock(tovp, rl_wcookie);
+			if (len == 0)
+				rl_rcookie = vn_rangelock_rlock(vp, inoff,
+				    OFF_MAX);
+			else
+				rl_rcookie = vn_rangelock_rlock(vp, inoff,
+				    inoff + len);
+			vn_rangelock_unlock(vp, rl_rcookie);
+		}
+
+		/*
+		 * If len == 0, set it based on invp's size.  Since it is range
+		 * locked, it should not change in size.
+		 */
+		if (len == 0) {
+			error = NFSVOPLOCK(vp, LK_SHARED);
+			if (error == 0) {
+				ret = nfsvno_getattr(vp, &at, nd, curthread, 1,
+				    NULL);
+				/* If offset past EOF, just leave len == 0. */
+				if (ret == 0 && at.na_size > inoff)
+					len = at.na_size - inoff;
+				NFSVOPUNLOCK(vp, 0);
+				if (ret != 0 && nd->nd_repstat == 0)
+					nd->nd_repstat = ret;
+			} else if (nd->nd_repstat == 0)
+				nd->nd_repstat = error;
+		}
+	}
+
+	/*
+	 * Do the actual copy to an upper limit of vfs.nfsd.maxcopyrange.
+	 * This limit is applied to ensure that the RPC replies in a
+	 * reasonable time.
+	 */
+	if (len > nfsrv_maxcopyrange)
+		xfer = nfsrv_maxcopyrange;
+	else
+		xfer = len;
+	nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff, &xfer, 0,
+	    nd->nd_cred, nd->nd_cred, NULL);
+	if (nd->nd_repstat == 0)
+		len = xfer;
+
+	/* Unlock the ranges. */
+	if (rl_rcookie != NULL)
+		vn_rangelock_unlock(vp, rl_rcookie);
+	if (rl_wcookie != NULL)
+		vn_rangelock_unlock(tovp, rl_wcookie);
+
+	if (nd->nd_repstat == 0) {
+		NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_HYPER +
+		    NFSX_VERF);
+		*tl++ = txdr_unsigned(0);	/* No callback ids. */
+		txdr_hyper(len, tl); tl += 2;
+		*tl++ = txdr_unsigned(NFSWRITE_UNSTABLE);
+		*tl++ = txdr_unsigned(nfsboottime.tv_sec);
+		*tl++ = txdr_unsigned(nfsboottime.tv_usec);
+		*tl++ = newnfs_true;
+		*tl = newnfs_true;
+	}
+out:
+	vrele(vp);
+	vrele(tovp);
+	NFSEXITCODE2(error, nd);
+	return (error);
+nfsmout:
+	vput(vp);
+	vrele(tovp);
 	NFSEXITCODE2(error, nd);
 	return (error);
 }

Modified: projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsocket.c
==============================================================================
--- projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsocket.c	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsocket.c	Fri Jul 26 00:27:47 2019	(r350337)
@@ -197,9 +197,9 @@ int (*nfsrv4_ops0[NFSV42_NOPS])(struct nfsrv_descript 
 	nfsrvd_destroyclientid,
 	nfsrvd_reclaimcomplete,
 	nfsrvd_allocate,
+	(int (*)(struct nfsrv_descript *, int, vnode_t , struct nfsexstuff *))0,
 	nfsrvd_notsupp,
 	nfsrvd_notsupp,
-	nfsrvd_notsupp,
 	nfsrvd_ioadvise,
 	nfsrvd_layouterror,
 	nfsrvd_layoutstats,
@@ -349,7 +349,7 @@ int (*nfsrv4_ops2[NFSV42_NOPS])(struct nfsrv_descript 
 	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
 	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
 	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
-	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
+	nfsrvd_copy_file_range,
 	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
 	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
 	(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,

Modified: projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsubs.c
==============================================================================
--- projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsubs.c	Thu Jul 25 22:23:34 2019	(r350336)
+++ projects/nfsv42/sys/fs/nfsserver/nfs_nfsdsubs.c	Fri Jul 26 00:27:47 2019	(r350337)
@@ -1544,7 +1544,7 @@ nfsrv_isannfserr(u_int32_t errval)
 
 	if (errval == NFSERR_OK)
 		return (errval);
-	if (errval >= NFSERR_BADHANDLE && errval <= NFSERR_DELEGREVOKED)
+	if (errval >= NFSERR_BADHANDLE && errval <= NFSERR_MAXERRVAL)
 		return (errval);
 	if (errval > 0 && errval <= NFSERR_REMOTE)
 		return (nfsrv_v2errmap[errval - 1]);


More information about the svn-src-projects mailing list