git: 77a7a48a1cb0 - main - nfscl: Fix oddball cases for session slot release

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Wed, 07 Oct 2026 01:54:48 UTC
The branch main has been updated by rmacklem:

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

commit 77a7a48a1cb003831ff1d4342b1974fc7f79381e
Author:     J. David <j.david.lists@gmail.com>
AuthorDate: 2026-10-07 01:52:19 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-10-07 01:52:19 +0000

    nfscl: Fix oddball cases for session slot release
    
    We have identified some cases where silent slot loss can occur
    when operations on NFS mounts are aborted. We experience this
    when using NFSv4.2, but it likely also occurs with NFSv4.1.
    
    A slot is acquired for compound operations by nfsv4_setsequence()
    and freed by newnfs_request(). Any call path that abandons the
    compound before reaching newnfs_request() loses the slot permanently.
    
    We identified four call sites where this happens, one of
    which where it actually does happen for us in a semi-reproducible
    way, which allowed us to develop a candidate patch, attached.
    
    The patch adds one function, nfsv4_freeunsentslot(), to
    nfs_clcomsubs.c. It is called from each of the four call
    sites: nfsrpc_writerpc(), nfsrpc_writeds(), and two in
    nfsrpc_setextattr().
    
    nfsv4_freeunsentslot() calls nfsv4_freeslot() with resetseq
    set to true because the aborted RPC will never be sent and
    therefore the slot sequence ID should not advance.
    
    With this patch and other diagnostics, we have confirmed
    that slot loss does not occur under the conditions that
    otherwise cause it in our environment.
    
    PR:     298416
    Reviewed by:    rmacklem
    MFC after:      1 week
---
 sys/fs/nfs/nfs_var.h             |  2 ++
 sys/fs/nfsclient/nfs_clcomsubs.c | 18 ++++++++++++++++++
 sys/fs/nfsclient/nfs_clrpcops.c  |  4 ++++
 3 files changed, 24 insertions(+)

diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 38f0ff90c5da..488ddbd1c582 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -383,6 +383,8 @@ int nfscl_wcc_data(struct nfsrv_descript *, vnode_t,
 int nfsm_loadattr(struct nfsrv_descript *, struct nfsvattr *);
 int nfscl_request(struct nfsrv_descript *, vnode_t,
          NFSPROC_T *, struct ucred *);
+void nfsv4_freeunsentslot(struct nfsmount *, struct nfsclsession *,
+    struct nfsrv_descript *);
 
 /* nfs_nfsdsubs.c */
 void nfsd_fhtovp(struct nfsrv_descript *, struct nfsrvfh *, int,
diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c
index d76eecd2762f..fa772ee12c8b 100644
--- a/sys/fs/nfsclient/nfs_clcomsubs.c
+++ b/sys/fs/nfsclient/nfs_clcomsubs.c
@@ -443,3 +443,21 @@ nfscl_lockderef(struct nfsv4lock *lckp)
 	}
 	NFSUNLOCKCLSTATE();
 }
+
+/*
+ * Free the session slot of a compound that was built but will not be sent
+ * to the server.
+ */
+void
+nfsv4_freeunsentslot(struct nfsmount *nmp, struct nfsclsession *sep,
+    struct nfsrv_descript *nd)
+{
+
+	if (nmp == NULL || (nd->nd_flag & (ND_NFSV41 | ND_HASSLOTID)) !=
+	    (ND_NFSV41 | ND_HASSLOTID))
+		return;
+	if (sep == NULL)
+		sep = nfsmnt_mdssession(nmp);
+	if (sep != NULL)
+		nfsv4_freeslot(sep, nd->nd_slotid, true);
+}
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 547771e4594f..ed19a782c5ba 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -2138,6 +2138,7 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, int *iomode,
 			error = nfsm_uiombuf(nd, uiop, len);
 		}
 		if (error != 0) {
+			nfsv4_freeunsentslot(nmp, NULL, nd);
 			m_freem(nd->nd_mreq);
 			free(nd, M_TEMP);
 			return (error);
@@ -7376,6 +7377,7 @@ nfsrpc_writeds(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
 	*tl = txdr_unsigned(len);
 	error = nfsm_uiombuf(nd, uiop, len);
 	if (error != 0) {
+		nfsv4_freeunsentslot(nmp, &dsp->nfsclds_sess, nd);
 		m_freem(nd->nd_mreq);
 		return (error);
 	}
@@ -9740,6 +9742,7 @@ nfsrpc_setextattr(vnode_t vp, const char *name, struct uio *uiop,
 	NFSCL_REQSTART(nd, NFSPROC_SETEXTATTR, vp, cred);
 	if (uiop->uio_resid > nd->nd_maxreq) {
 		/* nd_maxreq is set by NFSCL_REQSTART(). */
+		nfsv4_freeunsentslot(VFSTONFS(vp->v_mount), NULL, nd);
 		m_freem(nd->nd_mreq);
 		return (EINVAL);
 	}
@@ -9750,6 +9753,7 @@ nfsrpc_setextattr(vnode_t vp, const char *name, struct uio *uiop,
 	*tl = txdr_unsigned(uiop->uio_resid);
 	error = nfsm_uiombuf(nd, uiop, uiop->uio_resid);
 	if (error != 0) {
+		nfsv4_freeunsentslot(VFSTONFS(vp->v_mount), NULL, nd);
 		m_freem(nd->nd_mreq);
 		return (error);
 	}