git: 016570c4463d - main - nfs_clstate.c: Handle the same stateid case correctly

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Sun, 08 Mar 2026 22:11:26 UTC
The branch main has been updated by rmacklem:

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

commit 016570c4463d5908953355ee1cf9a385ad9601b4
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-03-08 21:09:36 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-03-08 21:09:36 +0000

    nfs_clstate.c: Handle the same stateid case correctly
    
    When an NFSv4.1/4.2 sarver upgrades a read delegation to
    a write delegation, it does not need to change the
    delegation's stateid.
    
    Without this patch, a DELEGRETURN of the stateid was done
    for the case where the delegation stateid had not changed.
    This return was bogus, since the delegation stateid now
    represents the new write delegation.
    
    This patch fixes the priblem by checking for "same stateid"
    and only doing the DELEGRETURN when it is not the same.
    
    PR:     289711
    Tested by:      Peter Much <pmc@citylink.dinoex_sub.org>
    MFC after:      2 weeks
---
 sys/fs/nfsclient/nfs_clstate.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 712d49c7160c..6dc97142b77f 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -433,9 +433,11 @@ nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
 {
 	struct nfscldeleg *tdp;
 	struct nfsmount *nmp;
+	bool trydelegret;
 
 	KASSERT(mp != NULL, ("nfscl_deleg: mp NULL"));
 	nmp = VFSTONFS(mp);
+	trydelegret = false;
 
 	/*
 	 * Since a delegation might be added to the mount,
@@ -467,6 +469,9 @@ nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
 		 * Read delegation.  Otherwise, return the new delegation.
 		 */
 		if (dp != NULL) {
+			if (NFSBCMP(dp->nfsdl_stateid.other,
+			    tdp->nfsdl_stateid.other, NFSX_STATEIDOTHER))
+				trydelegret = true;
 			if ((dp->nfsdl_flags & NFSCLDL_WRITE) != 0 &&
 			    (tdp->nfsdl_flags & NFSCLDL_READ) != 0) {
 				TAILQ_REMOVE(&clp->nfsc_deleg, tdp, nfsdl_list);
@@ -485,7 +490,8 @@ nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
 	}
 	NFSUNLOCKCLSTATE();
 	if (tdp != NULL) {
-		nfscl_trydelegreturn(tdp, cred, nmp, p);
+		if (trydelegret)
+			nfscl_trydelegreturn(tdp, cred, nmp, p);
 		free(tdp, M_NFSCLDELEG);
 	}
 	return (0);