git: f5fb5e07df5b - stable/13 - nfscl: Enable detection of bad session slots

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Thu, 28 Jul 2022 20:15:58 UTC
The branch stable/13 has been updated by rmacklem:

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

commit f5fb5e07df5bd7a6df170474a3df724ee3538785
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2022-07-10 20:33:19 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2022-07-28 20:11:07 +0000

    nfscl: Enable detection of bad session slots
    
    To deal with broken session slots caused by the use of the
    "soft" and/or "intr" mount options, nfsv4_sequencelookup()
    has been modified to track the potentially broken session
    slots (commit 40ada74ee1da).  Then, when all session slots
    are potentially broken, nfsv4_sequencelookup() does a
    DeleteSession operation, so that the NFSv4.1/4.2 server will
    reply NFSERR_BADSESSION to uses of the session.
    The client will then recover by doing a CreateSession to
    acquire a new session.
    
    This patch adds the code that marks potentially bad
    slots, so that the above semantics become functional.
    It has been successfully tested against a FreeBSD
    NFSv4.1/4.2 server, but does not work against a Linux 5.15
    NFSv4.1/4.2 server. (The Linux 5.15 server creates
    a new session with the same sessionid as the destroyed
    one and, as such, keeps returning NFSERR_BADSESSION.
    I believe this is a bug in the Linux server.)
    
    However, this should not cause a regression and will
    make "intr" mounts fairly usable against the NFSv4.1/4.2
    servers where it works.
    
    PR: 260011
    (cherry picked from commit 981ef32230b2fe46969c90b53864bdca4f1c3ae5)
---
 sys/fs/nfs/nfs_commonkrpc.c | 53 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 9 deletions(-)

diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c
index 20b12767d29d..150d92414e7a 100644
--- a/sys/fs/nfs/nfs_commonkrpc.c
+++ b/sys/fs/nfs/nfs_commonkrpc.c
@@ -925,10 +925,8 @@ tryagain:
 	} else if (stat == RPC_PROGVERSMISMATCH) {
 		NFSINCRGLOBAL(nfsstatsv1.rpcinvalid);
 		error = EPROTONOSUPPORT;
-	} else if (stat == RPC_INTR) {
-		error = EINTR;
 	} else if (stat == RPC_CANTSEND || stat == RPC_CANTRECV ||
-	     stat == RPC_SYSTEMERROR) {
+	     stat == RPC_SYSTEMERROR || stat == RPC_INTR) {
 		/* Check for a session slot that needs to be free'd. */
 		if ((nd->nd_flag & (ND_NFSV41 | ND_HASSLOTID)) ==
 		    (ND_NFSV41 | ND_HASSLOTID) && nmp != NULL &&
@@ -951,12 +949,17 @@ tryagain:
 			 */
 			mtx_lock(&sep->nfsess_mtx);
 			sep->nfsess_slotseq[nd->nd_slotid] += 10;
+			sep->nfsess_badslots |= (0x1ULL << nd->nd_slotid);
 			mtx_unlock(&sep->nfsess_mtx);
 			/* And free the slot. */
 			nfsv4_freeslot(sep, nd->nd_slotid, false);
 		}
-		NFSINCRGLOBAL(nfsstatsv1.rpcinvalid);
-		error = ENXIO;
+		if (stat == RPC_INTR)
+			error = EINTR;
+		else {
+			NFSINCRGLOBAL(nfsstatsv1.rpcinvalid);
+			error = ENXIO;
+		}
 	} else {
 		NFSINCRGLOBAL(nfsstatsv1.rpcinvalid);
 		error = EACCES;
@@ -1019,8 +1022,16 @@ tryagain:
 			 * If the first op is Sequence, free up the slot.
 			 */
 			if ((nmp != NULL && i == NFSV4OP_SEQUENCE && j != 0) ||
-			    (clp != NULL && i == NFSV4OP_CBSEQUENCE && j != 0))
+			   (clp != NULL && i == NFSV4OP_CBSEQUENCE && j != 0)) {
 				NFSCL_DEBUG(1, "failed seq=%d\n", j);
+				if (sep != NULL && i == NFSV4OP_SEQUENCE &&
+				    j == NFSERR_SEQMISORDERED) {
+					mtx_lock(&sep->nfsess_mtx);
+					sep->nfsess_badslots |=
+					    (0x1ULL << nd->nd_slotid);
+					mtx_unlock(&sep->nfsess_mtx);
+				}
+			}
 			if (((nmp != NULL && i == NFSV4OP_SEQUENCE && j == 0) ||
 			    (clp != NULL && i == NFSV4OP_CBSEQUENCE &&
 			    j == 0)) && sep != NULL) {
@@ -1039,11 +1050,35 @@ tryagain:
 					retseq = fxdr_unsigned(uint32_t, *tl++);
 					slot = fxdr_unsigned(int, *tl++);
 					if ((nd->nd_flag & ND_HASSLOTID) != 0) {
-						if (slot != nd->nd_slotid) {
+						if (slot >= NFSV4_SLOTS ||
+						    (i == NFSV4OP_CBSEQUENCE &&
+						     slot >= NFSV4_CBSLOTS)) {
 							printf("newnfs_request:"
-							    " Wrong session "
-							    "slot=%d\n", slot);
+							    " Bogus slot\n");
 							slot = nd->nd_slotid;
+						} else if (slot !=
+						    nd->nd_slotid) {
+						    printf("newnfs_request:"
+							" Wrong session "
+							"srvslot=%d "
+							"slot=%d\n", slot,
+							nd->nd_slotid);
+						    if (i == NFSV4OP_SEQUENCE) {
+							/*
+							 * Mark both slots as
+							 * bad, because we do
+							 * not know if the
+							 * server has advanced
+							 * the sequence# for
+							 * either of them.
+							 */
+							sep->nfsess_badslots |=
+							    (0x1ULL << slot);
+							sep->nfsess_badslots |=
+							    (0x1ULL <<
+							     nd->nd_slotid);
+						    }
+						    slot = nd->nd_slotid;
 						}
 					} else if (slot != 0) {
 						printf("newnfs_request: Bad "