git: f41d83578e8e - main - nfsclient: Fix problems with the NFS over RDMA glue
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 01 Sep 2026 14:06:47 UTC
The branch main has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=f41d83578e8eb1efced76704f00bcf6b201c3821
commit f41d83578e8eb1efced76704f00bcf6b201c3821
Author: Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-09-01 13:53:34 +0000
Commit: Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-09-01 14:05:20 +0000
nfsclient: Fix problems with the NFS over RDMA glue
There were a couple of problems detected w.r.t. the
"glue" for the nfsclrdma.ko module.
- When the NFS server has a small reply for a read,
it can choose to not use the reduction chunk
(separate memory area for the read data). I did
not realize this was the case.
- There was a bug in rpc_copy_uio_pages() function
that caused intermittent crashes in memcpy().
This patch fixes the above cases. It uses M_PROTO6
to mark that an RPC reply has used a reduction chunk,
so that read can handle it correctly. Read also now
provides a reduction chunk for all read sizes, since
the worst case for the rest of the read RPC reply is
close to the 1024 byte limit. (NFSv4 uses strings
instead of uid/gid in the attributes and these name
strings can be rather large.)
I wanted to get the "glue" into main so that others
could test the module more easily. Avaliability of
the module will be announced on freebsd-current@ soon.
It should not affect non-RDMA operation.
I've specified a long MFC, since the module still
requires extensive testing and, hopefully, a review.
MFC after: 3 months
Fixes: 884ee8d6c9b4 ("nfscl: Add some glue for client side NFS over RDMA")
---
sys/fs/nfsclient/nfs_clrpcops.c | 58 ++++++++++++++++++++++------
sys/rpc/rpc_generic.c | 84 ++++++++++++++++++++++++-----------------
2 files changed, 95 insertions(+), 47 deletions(-)
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 6e84567649d7..d8e1f17b66ae 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -1822,7 +1822,7 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
nfsv4stateid_t *stateidp, NFSPROC_T *p, struct nfsvattr *nap,
int *attrflagp)
{
- u_int32_t *tl;
+ uint32_t *tl, mbflag;
int error = 0, len, retlen, tsiz, eof = 0;
struct nfsrv_descript nfsd;
struct mbuf *mr;
@@ -1830,13 +1830,23 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
struct nfsrv_descript *nd = &nfsd;
int rsize;
off_t tmp_off;
- bool did_rdma;
+ bool did_rdma, reduced;
*attrflagp = 0;
tsiz = uiop->uio_resid;
did_rdma = false;
- if (NFSHASRDMA(nmp) && tsiz > RPCRDMA_MAX_SMALL_MSG / 2)
- did_rdma = true;
+ mbflag = 0;
+ if (NFSHASRDMA(nmp) && tsiz > 0) {
+ /* Assume the rest of the RPC without data is <= 1024 bytes. */
+ if ((uiop->uio_offset & PAGE_MASK) == 0)
+ 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) {
@@ -1846,10 +1856,12 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
rsize = nmp->nm_rsize;
NFSUNLOCKMNT(nmp);
nd->nd_mrep = NULL;
+ mr = NULL;
while (tsiz > 0) {
*attrflagp = 0;
len = (tsiz > rsize) ? rsize : tsiz;
NFSCL_REQSTART(nd, NFSPROC_READ, vp, cred);
+ nd->nd_mreq->m_flags |= mbflag;
if (nd->nd_flag & ND_NFSV4)
nfsm_stateidtom(nd, stateidp, NFSSTATEID_PUTSTATEID);
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED * 3);
@@ -1870,8 +1882,14 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
* (See the comment in nfsrpc_writerpc() for more info.)
*/
error = nfscl_request(nd, vp, p, cred);
- if (error)
+ if (error) {
+ if (mr != NULL)
+ rpc_free_rdma_reduction(mr);
return (error);
+ }
+ reduced = false;
+ if ((nd->nd_mrep->m_flags & M_PROTO6) != 0)
+ reduced = true;
if (nd->nd_flag & ND_NFSV3) {
error = nfscl_postop_attr(nd, nap, attrflagp);
} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV2)) {
@@ -1892,11 +1910,16 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
eof = fxdr_unsigned(int, *tl);
}
NFSM_STRSIZ(retlen, len);
- if (!did_rdma) {
- error = nfsm_mbufuio(nd, uiop, retlen);
- } else {
- error = rpc_copy_uio_pages(mr, uiop, retlen, true);
+ if (retlen > 0) {
+ if (!did_rdma || !reduced)
+ error = nfsm_mbufuio(nd, uiop, retlen);
+ else
+ error = rpc_copy_uio_pages(mr, uiop, retlen,
+ true);
+ }
+ if (mr != NULL) {
rpc_free_rdma_reduction(mr);
+ mr = NULL;
}
if (error)
goto nfsmout;
@@ -1911,6 +1934,8 @@ nfsrpc_readrpc(vnode_t vp, struct uio *uiop, struct ucred *cred,
}
return (0);
nfsmout:
+ if (mr != NULL)
+ rpc_free_rdma_reduction(mr);
if (nd->nd_mrep != NULL)
m_freem(nd->nd_mrep);
return (error);
@@ -2029,7 +2054,7 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
*attrflagp = 0;
tsiz = uiop->uio_resid;
did_rdma = false;
- if (NFSHASRDMA(nmp) && tsiz > RPCRDMA_MAX_SMALL_MSG / 2)
+ if (NFSHASRDMA(nmp) && (uiop->uio_offset & PAGE_MASK) == 0)
did_rdma = true;
tmp_off = uiop->uio_offset + tsiz;
NFSLOCKMNT(nmp);
@@ -2045,6 +2070,7 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, 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 */
+ mr = NULL;
while (tsiz > 0) {
*attrflagp = 0;
len = (tsiz > wsize) ? wsize : tsiz;
@@ -2099,8 +2125,10 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
rlen = m_length(nd->nd_mreq, NULL);
mr = nfsm_build_rdma_reduction(nd, len, rlen, false);
error = rpc_copy_uio_pages(mr, uiop, len, false);
- if (error)
+ if (error != 0) {
rpc_free_rdma_reduction(mr);
+ mr = NULL;
+ }
} else {
error = nfsm_uiombuf(nd, uiop, len);
}
@@ -2137,11 +2165,17 @@ nfsrpc_writerpc(vnode_t vp, struct uio *uiop, int *iomode,
}
error = nfscl_request(nd, vp, p, cred);
if (error) {
+ if (mr != NULL)
+ rpc_free_rdma_reduction(mr);
free(nd, M_TEMP);
return (error);
}
- if (did_rdma && !NFSHASNOWRITEREDUCE(nmp))
+ if (did_rdma && !NFSHASNOWRITEREDUCE(nmp)) {
+ KASSERT(mr != NULL, ("nfsrpc_writerpc: Null mr"));
+
rpc_free_rdma_reduction(mr);
+ mr = NULL;
+ }
if (nd->nd_repstat) {
/*
* In case the rpc gets retried, roll
diff --git a/sys/rpc/rpc_generic.c b/sys/rpc/rpc_generic.c
index 585d89a6931c..7172a19164c2 100644
--- a/sys/rpc/rpc_generic.c
+++ b/sys/rpc/rpc_generic.c
@@ -985,59 +985,73 @@ rpc_free_rdma_reduction(struct mbuf *mr)
* Copy data between anonymous pages and uiop.
*/
int
-rpc_copy_uio_pages(struct mbuf *mr, struct uio *uiop, int len, bool from_pages)
+rpc_copy_uio_pages(struct mbuf *mr, struct uio *uiop, int siz, bool from_pages)
{
struct rpcrdma_reduce_pg *rb;
- struct iovec *iov;
- int cplen, error, lastlen, i, xfer;
- char *cp;
+ char *cp, *uiocp;
+ int error, left, len, i, uiosiz, xfer;
rb = mtod(mr, struct rpcrdma_reduce_pg *);
- iov = uiop->uio_iov;
- lastlen = rb->len % PAGE_SIZE;
- if (lastlen == 0)
- lastlen = PAGE_SIZE;
- for (i = 0; i < rb->npg && len > 0; i++) {
- xfer = (i == rb->npg - 1) ? lastlen : PAGE_SIZE;
- xfer = MIN(xfer, len);
- cp = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(rb->pg[i]));
- while (xfer > 0) {
- while (iov->iov_len == 0) {
- if (uiop->uio_iovcnt > 0) {
- iov++;
- uiop->uio_iovcnt--;
- } else {
- return (ENOMEM);
- }
+ if (siz > rb->len)
+ return (EBADRPC);
+ i = 0;
+ cp = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(rb->pg[i]));
+ len = PAGE_SIZE;
+ if (i == rb->npg - 1 && siz < PAGE_SIZE)
+ len = siz;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EBADRPC);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ if (len == 0) {
+ if (i == rb->npg - 1)
+ return (EBADRPC);
+ i++;
+ cp = PHYS_TO_DMAP(
+ VM_PAGE_TO_PHYS(rb->pg[i]));
+ len = PAGE_SIZE;
+ if (i == rb->npg - 1 && siz < PAGE_SIZE)
+ len = siz;
}
- cplen = MIN(iov->iov_len, xfer);
+ xfer = (left > len) ? len : left;
if (from_pages) {
if (uiop->uio_segflg == UIO_SYSSPACE) {
- memcpy(iov->iov_base, cp, cplen);
+ memcpy(uiocp, cp, xfer);
} else {
- error = copyout(cp, iov->iov_base,
- cplen);
+ error = copyout(cp, uiocp, xfer);
if (error != 0)
- return (error);
+ return (EBADRPC);
}
} else {
if (uiop->uio_segflg == UIO_SYSSPACE) {
- memcpy(cp, iov->iov_base, cplen);
+ memcpy(cp, uiocp, xfer);
} else {
- error = copyin(iov->iov_base, cp,
- cplen);
+ error = copyout(uiocp, cp, xfer);
if (error != 0)
- return (error);
+ return (EBADRPC);
}
}
- iov->iov_len -= cplen;
- iov->iov_base = (char *)iov->iov_base + cplen;
- uiop->uio_offset += cplen;
- uiop->uio_resid -= cplen;
- xfer -= cplen;
- cp += cplen;
+ left -= xfer;
len -= xfer;
+ cp += xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base = (void *)
+ ((char *)uiop->uio_iov->iov_base + uiosiz);
+ uiop->uio_iov->iov_len -= uiosiz;
}
+ siz -= uiosiz;
}
return (0);
}