aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/nfsserver/nfs_nfsdstate.c
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2021-04-26 23:24:10 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2021-04-26 23:24:10 +0000
commit87597731488105dd1ab921a95e39bb62e1abe668 (patch)
tree0b7f5a2470dda13d09d0520772acaf3226e69c83 /sys/fs/nfsserver/nfs_nfsdstate.c
parent43bbae19483fbde0a91e61acad8a6e71e334c8b8 (diff)
nfsd: fix the slot sequence# when a callback fails
Commit 4281bfec3628 patched the server so that the callback session slot would be free'd for reuse when a callback attempt fails. However, this can often result in the sequence# for the session slot to be advanced such that the client end will reply NFSERR_SEQMISORDERED. To avoid the NFSERR_SEQMISORDERED client reply, this patch negates the sequence# advance for the case where the callback has failed. The common case is a failed back channel, where the callback cannot be sent to the client, and not advancing the sequence# is correct for this case. For the uncommon case where the client's reply to the callback is lost, not advancing the sequence# will indicate to the client that the next callback is a retry and not a new callback. But, since the FreeBSD server always sets "csa_cachethis" false in the callback sequence operation, a retry and a new callback should be handled the same way by the client, so this should not matter. Until you have this patch in your NFSv4.1/4.2 server, you should consider avoiding the use of delegations. Even with this patch, interoperation with the Linux NFSv4.1/4.2 client in kernel versions prior to 5.3 can result in frequent 15second delays if delegations are enabled. This occurs because, for kernels prior to 5.3, the Linux client does a TCP reconnect every time it sees multiple concurrent callbacks and then it takes 15seconds to recover the back channel after doing so. MFC after: 2 weeks
Diffstat (limited to 'sys/fs/nfsserver/nfs_nfsdstate.c')
-rw-r--r--sys/fs/nfsserver/nfs_nfsdstate.c23
1 files changed, 20 insertions, 3 deletions
diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c
index 430f09844b83..3aebddad0962 100644
--- a/sys/fs/nfsserver/nfs_nfsdstate.c
+++ b/sys/fs/nfsserver/nfs_nfsdstate.c
@@ -4572,7 +4572,8 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp,
if ((clp->lc_flags & LCL_NFSV41) != 0) {
error = ECONNREFUSED;
if (procnum != NFSV4PROC_CBNULL)
- nfsv4_freeslot(&sep->sess_cbsess, slotpos);
+ nfsv4_freeslot(&sep->sess_cbsess, slotpos,
+ true);
nfsrv_freesession(sep, NULL);
} else if (nd->nd_procnum == NFSV4PROC_CBNULL)
error = newnfs_connect(NULL, &clp->lc_req, cred,
@@ -4604,8 +4605,24 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp,
error = ECONNREFUSED;
}
NFSD_DEBUG(4, "aft newnfs_request=%d\n", error);
- if (error != 0 && procnum != NFSV4PROC_CBNULL)
- nfsv4_freeslot(&sep->sess_cbsess, slotpos);
+ if (error != 0 && procnum != NFSV4PROC_CBNULL) {
+ /*
+ * It is likely that the callback was never
+ * processed by the client and, as such,
+ * the sequence# for the session slot needs
+ * to be backed up by one to avoid a
+ * NFSERR_SEQMISORDERED error reply.
+ * For the unlikely case where the callback
+ * was processed by the client, this will
+ * make the next callback on the slot
+ * appear to be a retry.
+ * Since callbacks never specify that the
+ * reply be cached, this "apparent retry"
+ * should not be a problem.
+ */
+ nfsv4_freeslot(&sep->sess_cbsess, slotpos,
+ true);
+ }
nfsrv_freesession(sep, NULL);
} else
error = newnfs_request(nd, NULL, clp, &clp->lc_req,