git: 3ab7f15d9116 - stable/13 - nfscl: Handle a Getattr failure with NFSERR_DELAY following Open

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Wed, 22 Nov 2023 00:04:32 UTC
The branch stable/13 has been updated by rmacklem:

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

commit 3ab7f15d91160b4ac3369f6c3bc696ba9bf88afc
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2023-10-22 01:33:33 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2023-11-22 00:03:11 +0000

    nfscl: Handle a Getattr failure with NFSERR_DELAY following Open
    
    During testing at a recent IETF NFSv4 Bakeathon, a non-FreeBSD
    server was rebooted.  After the reboot, the FreeBSD client sent
    an Open/Claim_previous with a Getattr after the Open in the same
    compound.  The Open/Claim_previous was done to recover the Open
    and a Delegation for for a file.  The Open succeeded, but the
    Getattr after the Open failed with NFSERR_DELAY.  This resulted
    in the FreeBSD client retrying the entire RPC over and over again,
    until the server's recovery grace period ended.  Since the Open
    succeeded, there was no need to retry the entire RPC.
    
    This patch modifies the NFSv4 client side recovery Open/Claim_previous
    RPC reply handling to deal with this case.  With this patch, the
    Getattr reply of NFSERR_DELAY is ignored and the successful Open
    reply is processed.
    
    This bug will not normally affect users, since this non-FreeBSD
    server is not widely used (it may not even have shipped to any
    customers).
    
    (cherry picked from commit 14bbf4fe5abb20f1126168e66b03127ae920f78e)
---
 sys/fs/nfsclient/nfs_clrpcops.c | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index be702e703c91..569132aee43c 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -569,7 +569,8 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
 	if (error)
 		return (error);
 	NFSCL_INCRSEQID(op->nfso_own->nfsow_seqid, nd);
-	if (!nd->nd_repstat) {
+	if (nd->nd_repstat == 0 || (nd->nd_repstat == NFSERR_DELAY &&
+	    reclaim != 0 && (nd->nd_flag & ND_NOMOREDATA) == 0)) {
 		NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
 		    6 * NFSX_UNSIGNED);
 		op->nfso_stateid.seqid = *tl++;
@@ -641,16 +642,29 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
 			goto nfsmout;
 		}
 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
-		error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
-		    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
-		    NULL, NULL, NULL, p, cred);
-		if (error)
-			goto nfsmout;
+		/* If the 2nd element == NFS_OK, the Getattr succeeded. */
+		if (*++tl == 0) {
+			KASSERT(nd->nd_repstat == 0,
+			    ("nfsrpc_openrpc: Getattr repstat"));
+			error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
+			    NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
+			    NULL, NULL, NULL, p, cred);
+			if (error)
+				goto nfsmout;
+		}
 		if (ndp != NULL) {
-			ndp->nfsdl_change = nfsva.na_filerev;
-			ndp->nfsdl_modtime = nfsva.na_mtime;
-			ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
+			if (reclaim != 0 && dp != NULL) {
+				ndp->nfsdl_change = dp->nfsdl_change;
+				ndp->nfsdl_modtime = dp->nfsdl_modtime;
+				ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
+			} else if (nd->nd_repstat == 0) {
+				ndp->nfsdl_change = nfsva.na_filerev;
+				ndp->nfsdl_modtime = nfsva.na_mtime;
+				ndp->nfsdl_flags |= NFSCLDL_MODTIMESET;
+			} else
+				ndp->nfsdl_flags |= NFSCLDL_RECALL;
 		}
+		nd->nd_repstat = 0;
 		if (!reclaim && (rflags & NFSV4OPEN_RESULTCONFIRM)) {
 		    do {
 			ret = nfsrpc_openconfirm(vp, newfhp, newfhlen, op,