git: 9e1bbfb88e98 - main - nfs_clstate.c: Fix CB_RECALL handling for NFSv4.1/4.2

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Sat, 27 Jun 2026 23:37:13 UTC
The branch main has been updated by rmacklem:

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

commit 9e1bbfb88e986b209709ea765189a3ebb6581bcd
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-06-27 23:35:20 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-06-27 23:35:20 +0000

    nfs_clstate.c: Fix CB_RECALL handling for NFSv4.1/4.2
    
    Recent testing with a modified NFSv4.1/4.2 client that
    sometimes ignored CB_RECALL callbacks, identified a few
    problems when handling the unusual case of CB_RECALL not
    be performed by the client.
    - The csa_cachethis argument to CB_SEQUENCE was being ignored.
    - The CB_SEQUENCE operation would reply NFSERR_DELAY
      after the first CB_RECALL attempt, making retries
      ineffective.
    - The code could return NFSERR_RESOURCE, which is a
      NFSv4.0 specific error code.
    
    This patch fixes the above three problems.
    
    The patch only affects the NFSv4.1/4.2 client when
    delegations are being issued and the client somehow
    fails to handle a CB_RECALL callback of a delegation,
    which is an unusual case.
    
    MFC after:      2 weeks
---
 sys/fs/nfsclient/nfs_clstate.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 1931964ed62e..e906de603c28 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -3585,7 +3585,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
 	mount_t mp;
 	nfsattrbit_t attrbits, rattrbits;
 	nfsv4stateid_t stateid;
-	uint32_t seqid, slotid = 0, highslot, cachethis __unused;
+	uint32_t seqid, slotid = 0, highslot, cachethis;
 	uint8_t sessionid[NFSX_V4SESSIONID];
 	struct mbuf *rep;
 	struct nfscllayout *lyp;
@@ -3596,6 +3596,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
 	struct nfsclsession *tsep;
 
 	gotseq_ok = 0;
+	cachethis = 0;
 	nfsrvd_rephead(nd);
 	NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
 	taglen = fxdr_unsigned(int, *tl);
@@ -3922,6 +3923,9 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
 				error = nfsv4_seqsession(seqid, slotid,
 				    highslot, tsep->nfsess_cbslots, &rep,
 				    tsep->nfsess_backslots);
+				/* For callbacks, do not reply NFSERR_DELAY. */
+				if (error == NFSERR_DELAY)
+					error = 0;
 			}
 			NFSUNLOCKCLSTATE();
 			if (error == 0 || error == NFSERR_REPLYFROMCACHE) {
@@ -4065,8 +4069,9 @@ nfsmout:
 	}
 	*nd->nd_errp = nfscl_errmap(nd, minorvers);
 out:
-	if (gotseq_ok != 0) {
+	if (gotseq_ok != 0 && cachethis != 0) {
 		rep = m_copym(nd->nd_mreq, 0, M_COPYALL, M_WAITOK);
+		NFSCL_DEBUG(4, "Got reply for cbcache=%p\n", rep);
 		NFSLOCKCLSTATE();
 		clp = nfscl_getclntsess(sessionid);
 		if (clp != NULL) {
@@ -5283,6 +5288,8 @@ nfscl_errmap(struct nfsrv_descript *nd, u_int32_t minorvers)
 	while (*++errp)
 		if (*errp == (short)nd->nd_repstat)
 			return (txdr_unsigned(nd->nd_repstat));
+	if (minorvers > NFSV4_MINORVERSION && *defaulterrp == NFSERR_RESOURCE)
+		return (txdr_unsigned(NFSERR_SERVERFAULT));
 	return (txdr_unsigned(*defaulterrp));
 }