From 05a39c2c1c18cd0c4382a4f58e0952d3f77e7dfa Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Thu, 8 Apr 2021 14:04:22 -0700 Subject: [PATCH] nfsd: fix replies from session cache for retried RPCs Recent testing of network partitioning a FreeBSD NFSv4.1 server from a Linux NFSv4.1 client identified problems with both the FreeBSD server and Linux client. The FreeBSD server failec to reply using the cached reply in the session slot when an RPC was retried on the session slot, as indicated by same slot sequence#. This patch fixes this. It should also fix a similar failure for NFSv4.0 mounts, when the sequence# in the open/lock_owner requires a reply be done from an entry locked into the DRC. This fix affects the fairly rare case where a NFSv4 client retries a non-idempotent RPC, such as a lock operation. Note that retries only occur after the client has needed to create a new TCP connection. MFC after: 2 weeks --- sys/fs/nfsserver/nfs_nfsdkrpc.c | 7 ++++++- sys/fs/nfsserver/nfs_nfsdsubs.c | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c index 7265e439d6a0..44f585ff0beb 100644 --- a/sys/fs/nfsserver/nfs_nfsdkrpc.c +++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c @@ -410,8 +410,13 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, SVCXPRT *xprt, m = NULL; if ((nd->nd_flag & ND_HASSEQUENCE) != 0) nfsrv_cache_session(nd, &m); - if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) + if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) { nd->nd_repstat = 0; + if (m != NULL) { + m_freem(nd->nd_mreq); + nd->nd_mreq = m; + } + } cacherep = RC_REPLY; } else { if (nd->nd_repstat == NFSERR_DONTREPLY) diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c index ac28119028c4..49c5cac999c7 100644 --- a/sys/fs/nfsserver/nfs_nfsdsubs.c +++ b/sys/fs/nfsserver/nfs_nfsdsubs.c @@ -1553,6 +1553,8 @@ nfsd_errmap(struct nfsrv_descript *nd) else if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || nd->nd_repstat == NFSERR_OPILLEGAL) return (txdr_unsigned(nd->nd_repstat)); + else if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) + return (txdr_unsigned(NFSERR_IO)); else if ((nd->nd_flag & ND_NFSV41) != 0) { if (nd->nd_repstat == EOPNOTSUPP) nd->nd_repstat = NFSERR_NOTSUPP;