git: d3db309653aa - stable/13 - nfsd: Add a sysctl to limit NFSv4.2 Copy RPC size

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Sat, 30 Mar 2024 01:06:32 UTC
The branch stable/13 has been updated by rmacklem:

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

commit d3db309653aa5eb263220296757fdf514efb55c2
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2024-03-16 01:04:37 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2024-03-30 01:05:31 +0000

    nfsd: Add a sysctl to limit NFSv4.2 Copy RPC size
    
    NFSv4.2 supports a Copy operation, which avoids file data being
    read to the client and then written back to the server, if both
    input and output files are on the same NFSv4.2 mount for
    copy_file_range(2).
    
    Unfortunately, this Copy operation can take a long time under
    certain circumstances.  If this occurs concurrently with a RPC
    that requires an exclusive lock on the nfsd such as ExchangeID
    done for a new mount, the result can be an nfsd "stall" until
    the Copy completes.
    
    This patch adds a sysctl that can be set to limit the size of
    a Copy operation or, if set to 0, disable Copy operations.
    
    The use of this sysctl and other ways to avoid Copy operations
    taking too long will be documented in the nfsd.4 man page by
    a separate commit.
    
    (cherry picked from commit 748f56c53f4286e0b140c1b779ff8ade1cf4fec9)
---
 sys/fs/nfsserver/nfs_nfsdserv.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 7020053be330..c1ba36347b4c 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -97,6 +97,9 @@ static bool	nfsrv_doallocate = false;
 SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, enable_v42allocate, CTLFLAG_RW,
     &nfsrv_doallocate, 0,
     "Enable NFSv4.2 Allocate operation");
+static uint64_t nfsrv_maxcopyrange = SSIZE_MAX;
+SYSCTL_U64(_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.
@@ -5475,10 +5478,11 @@ nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
 	void *rl_rcookie, *rl_wcookie;
 
 	rl_rcookie = rl_wcookie = NULL;
-	if (nfsrv_devidcnt > 0) {
+	if (nfsrv_maxcopyrange == 0 || nfsrv_devidcnt > 0) {
 		/*
 		 * For a pNFS server, reply NFSERR_NOTSUPP so that the client
 		 * will do the copy via I/O on the DS(s).
+		 * If vfs.nfsd.maxcopyrange set to 0, disable Copy.
 		 */
 		nd->nd_repstat = NFSERR_NOTSUPP;
 		goto nfsmout;
@@ -5641,7 +5645,15 @@ nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
 			nd->nd_repstat = error;
 	}
 
-	xfer = len;
+	/*
+	 * Do the actual copy to an upper limit of vfs.nfsd.maxcopyrange.
+	 * This size limit can be set to limit the time a copy RPC will
+	 * take.
+	 */
+	if (len > nfsrv_maxcopyrange)
+		xfer = nfsrv_maxcopyrange;
+	else
+		xfer = len;
 	if (nd->nd_repstat == 0) {
 		nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
 		    &xfer, COPY_FILE_RANGE_TIMEO1SEC, nd->nd_cred, nd->nd_cred,