aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2022-07-10 20:33:19 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2022-07-10 20:33:19 +0000
commit981ef32230b2fe46969c90b53864bdca4f1c3ae5 (patch)
tree39488078f9a8951805de72941d9eb86e419f8121
parente0e8d0c8d69459c7128e6fd4fb537892445ce710 (diff)
downloadsrc-981ef32230b2.tar.gz
src-981ef32230b2.zip
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 MFC after: 2 weeks
-rw-r--r--sys/fs/nfs/nfs_commonkrpc.c53
1 files 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 "