git: 0948d2a9cfea - stable/13 - nfsd: Avoid acquiring a vnode for some NFSv4 Readdir operations

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Thu, 16 Nov 2023 23:56:08 UTC
The branch stable/13 has been updated by rmacklem:

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

commit 0948d2a9cfea5a469a9505fb314a6f9af38bb6fa
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2023-10-17 20:55:48 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2023-11-16 23:54:20 +0000

    nfsd: Avoid acquiring a vnode for some NFSv4 Readdir operations
    
    Without this patch, a NFSv4 Readdir operation acquires the vnode for
    each entry in the directory.  If only the Type, Fileid, Mounted_on_fileid
    and ReaddirError attributes are requested by a client, acquiring the vnode
    is not necessary for non-directories.  Directory vnodes must be acquired
    to check for server file system mount points.
    
    This patch avoids acquiring the vnode, as above, resulting in a 3-8%
    improvement in Readdir RPC RTT for some simple tests I did.
    
    Note that only non-rdirplus NFSv4 mounts will benefit from this change.
    
    Tested during a recent IETF NFSv4 Bakeathon testing event.
    
    (cherry picked from commit cd5edc7db261fb228be4044e6fdd38850eb4e9c4)
---
 sys/fs/nfsserver/nfs_nfsdport.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index e2a3ff7e3b93..05cebdd13f7d 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -117,6 +117,11 @@ extern int nfsrv_issuedelegs;
 extern int nfsrv_dolocallocks;
 extern struct nfsdevicehead nfsrv_devidhead;
 
+/* Map d_type to vnode type. */
+static uint8_t dtype_to_vnode[DT_WHT + 1] = { VNON, VFIFO, VCHR, VNON, VDIR,
+    VNON, VBLK, VNON, VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON };
+#define	NFS_DTYPETOVTYPE(t)	((t) <= DT_WHT ? dtype_to_vnode[(t)] : VNON)
+
 static int nfsrv_createiovec(int, struct mbuf **, struct mbuf **,
     struct iovec **);
 static int nfsrv_createiovec_extpgs(int, int, struct mbuf **,
@@ -2319,7 +2324,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
 	caddr_t bpos0, bpos1;
 	u_int64_t off, toff, verf;
 	u_long *cookies = NULL, *cookiep;
-	nfsattrbit_t attrbits, rderrbits, savbits;
+	nfsattrbit_t attrbits, rderrbits, savbits, refbits;
 	struct uio io;
 	struct iovec iv;
 	struct componentname cn;
@@ -2370,9 +2375,20 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
 		if (error)
 			goto nfsmout;
 		NFSSET_ATTRBIT(&savbits, &attrbits);
+		NFSSET_ATTRBIT(&refbits, &attrbits);
 		NFSCLRNOTFILLABLE_ATTRBIT(&attrbits, nd);
 		NFSZERO_ATTRBIT(&rderrbits);
 		NFSSETBIT_ATTRBIT(&rderrbits, NFSATTRBIT_RDATTRERROR);
+		/*
+		 * If these 4 bits are the only attributes requested by the
+		 * client, they can be satisfied without acquiring the vnode
+		 * for the file object unless it is a directory.
+		 * This will be indicated by savbits being all 0s.
+		 */
+		NFSCLRBIT_ATTRBIT(&savbits, NFSATTRBIT_TYPE);
+		NFSCLRBIT_ATTRBIT(&savbits, NFSATTRBIT_FILEID);
+		NFSCLRBIT_ATTRBIT(&savbits, NFSATTRBIT_MOUNTEDONFILEID);
+		NFSCLRBIT_ATTRBIT(&savbits, NFSATTRBIT_RDATTRERROR);
 	} else {
 		NFSZERO_ATTRBIT(&attrbits);
 	}
@@ -2616,7 +2632,10 @@ again:
 			new_mp = mp;
 			mounted_on_fileno = (uint64_t)dp->d_fileno;
 			if ((nd->nd_flag & ND_NFSV3) ||
-			    NFSNONZERO_ATTRBIT(&savbits)) {
+			    NFSNONZERO_ATTRBIT(&savbits) ||
+			    dp->d_type == DT_UNKNOWN ||
+			    (dp->d_type == DT_DIR &&
+			     nfsrv_enable_crossmntpt != 0)) {
 				if (nd->nd_flag & ND_NFSV4)
 					refp = nfsv4root_getreferral(NULL,
 					    vp, dp->d_fileno);
@@ -2754,6 +2773,11 @@ again:
 						break;
 					}
 				}
+			} else if (NFSNONZERO_ATTRBIT(&attrbits)) {
+				/* Only need Type and/or Fileid. */
+				VATTR_NULL(&nvap->na_vattr);
+				nvap->na_fileid = dp->d_fileno;
+				nvap->na_type = NFS_DTYPETOVTYPE(dp->d_type);
 			}
 
 			/*
@@ -2785,7 +2809,7 @@ again:
 					supports_nfsv4acls = 0;
 				if (refp != NULL) {
 					dirlen += nfsrv_putreferralattr(nd,
-					    &savbits, refp, 0,
+					    &refbits, refp, 0,
 					    &nd->nd_repstat);
 					if (nd->nd_repstat) {
 						if (nvp != NULL)