git: 63302e904a52 - main - nfs_clrpcops.c: Fix an mbuf leak introduced by fec497b89b1a

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Sun, 01 Nov 2026 04:06:20 UTC
The branch main has been updated by rmacklem:

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

commit 63302e904a52a30692659a9144825b657269aa4c
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-11-01 03:47:34 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-11-01 04:04:38 +0000

    nfs_clrpcops.c: Fix an mbuf leak introduced by fec497b89b1a
    
    The calls to rpc_free_rdma_reduction() were removed, but I forgot
    that newnfs_request() removes the reduction mbuf from the list
    upon success, so it has to be free'd separately.
    This patch does that.
    
    The patch also disables reduction after the first loop interation.
    This is necessary until nfsm_build_rdma_reduction() is patched
    to handle an offset within the page array.
    
    Reported by:    markj
    MFC after:      1 month
    Fixes:  884ee8d6c9b4 ("nfscl: Add some glue for client side NFS over RDMA")
---
 sys/fs/nfsclient/nfs_clrpcops.c | 57 +++++++++++++++++++++++++++--------------
 1 file changed, 38 insertions(+), 19 deletions(-)

diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 0acca38ae248..464481eca0e2 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -1830,25 +1830,10 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, struct ucred *cred,
 	struct nfsrv_descript *nd = &nfsd;
 	int rsize;
 	off_t tmp_off;
-	bool did_rdma, reduced;
+	bool did_rdma, reduced, first_loop;
 
 	*attrflagp = 0;
 	tsiz = uiop->uio_resid;
-	did_rdma = false;
-	mbflag = 0;
-	if (NFSHASRDMA(nmp) && tsiz > 0) {
-		/* Assume the rest of the RPC without data is <= 1024 bytes. */
-		if (pgp != NULL && (uiop->uio_offset & PAGE_MASK) == 0 &&
-		    uiop->uio_segflg == UIO_SYSSPACE &&
-		    !NFSHASNOREADREDUCE(nmp))
-			did_rdma = true;
-		else if (tsiz <= PAGE_SIZE - 1024)
-			mbflag = M_PROTO7;
-		else if (tsiz <= NFS_DIRBLKSIZ + PAGE_SIZE - 1024)
-			mbflag = M_PROTO8;
-		else
-			mbflag = M_PROTO9;
-	}
 	tmp_off = uiop->uio_offset + tsiz;
 	NFSLOCKMNT(nmp);
 	if (tmp_off > nmp->nm_maxfilesize || tmp_off < uiop->uio_offset) {
@@ -1858,10 +1843,32 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, struct ucred *cred,
 	rsize = nmp->nm_rsize;
 	NFSUNLOCKMNT(nmp);
 	nd->nd_mrep = NULL;
-	mr = NULL;
+	first_loop = true;
 	while (tsiz > 0) {
+		mr = NULL;
 		*attrflagp = 0;
 		len = (tsiz > rsize) ? rsize : tsiz;
+		did_rdma = false;
+		mbflag = 0;
+		if (NFSHASRDMA(nmp) && len > 0) {
+			/*
+			 * Until nfsm_build_rdma_reduction() handles an offset
+			 * within the pages, this can only be done for the first
+			 * iteration.
+			 */
+			if (first_loop && pgp != NULL &&
+			    (uiop->uio_offset & PAGE_MASK) == 0 &&
+			    uiop->uio_segflg == UIO_SYSSPACE &&
+			    !NFSHASNOREADREDUCE(nmp))
+				did_rdma = true;
+			else if (len <= PAGE_SIZE - 1024)
+				mbflag = M_PROTO7;
+			else if (len <= NFS_DIRBLKSIZ + PAGE_SIZE - 1024)
+				mbflag = M_PROTO8;
+			else
+				mbflag = M_PROTO9;
+		}
+		first_loop = false;
 		NFSCL_REQSTART(nd, NFSPROC_READ, vp, cred);
 		nd->nd_mreq->m_flags |= mbflag;
 		if (nd->nd_flag & ND_NFSV4)
@@ -1916,6 +1923,9 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, struct ucred *cred,
 				error = rpc_copy_uio_pages(mr, uiop, retlen,
 				    true);
 		}
+		if (mr != NULL)
+			m_free(mr);
+		mr = NULL;
 		if (error)
 			goto nfsmout;
 		m_freem(nd->nd_mrep);
@@ -1929,6 +1939,8 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, struct ucred *cred,
 	}
 	return (0);
 nfsmout:
+	if (mr != NULL)
+		m_free(mr);
 	if (nd->nd_mrep != NULL)
 		m_freem(nd->nd_mrep);
 	return (error);
@@ -2042,7 +2054,7 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, int *iomode,
 	nfsattrbit_t attrbits;
 	uint64_t tmp_off;
 	ssize_t tsiz, wsize;
-	bool do_append, did_rdma;
+	bool do_append, did_rdma, first_loop;
 
 	KASSERT(uiop->uio_iovcnt == 1, ("nfs: writerpc iovcnt > 1"));
 	*attrflagp = 0;
@@ -2066,7 +2078,9 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, int *iomode,
 	nd = malloc(sizeof(*nd), M_TEMP, M_WAITOK);
 	nd->nd_mrep = NULL;	/* NFSv2 sometimes does a write with */
 	nd->nd_repstat = 0;	/* uio_resid == 0, so the while is not done */
+	first_loop = true;
 	while (tsiz > 0) {
+		mr = NULL;
 		*attrflagp = 0;
 		len = (tsiz > wsize) ? wsize : tsiz;
 		if (do_append)
@@ -2116,7 +2130,7 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, int *iomode,
 			*tl = x;        /* size of this write */
 		}
 		/* For RDMA, make the data a separate chunk. */
-		if (did_rdma) {
+		if (did_rdma && first_loop) {
 			rlen = m_length(nd->nd_mreq, NULL);
 			mr = nfsm_build_rdma_reduction(pgp, nd, len, rlen,
 			    false);
@@ -2124,6 +2138,7 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, int *iomode,
 		} else {
 			error = nfsm_uiombuf(nd, uiop, len);
 		}
+		first_loop = false;
 		if (error != 0) {
 			nfsv4_freeunsentslot(nmp, NULL, nd);
 			m_freem(nd->nd_mreq);
@@ -2161,6 +2176,10 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, vm_page_t *pgp, int *iomode,
 			free(nd, M_TEMP);
 			return (error);
 		}
+		if (mr != NULL) {
+			m_free(mr);
+			mr = NULL;
+		}
 		if (nd->nd_repstat) {
 			/*
 			 * In case the rpc gets retried, roll