git: 7144a1d58c5c - main - nfsd: Add glue for the nfsrdma.ko module
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 14 Aug 2026 15:01:20 UTC
The branch main has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=7144a1d58c5cfa1dcfd1a172965d73289616569c
commit 7144a1d58c5cfa1dcfd1a172965d73289616569c
Author: Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-08-14 14:56:38 +0000
Commit: Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-08-14 14:59:29 +0000
nfsd: Add glue for the nfsrdma.ko module
Thanks to Vinicius Ferrao <versatushpc.com.br>, there
is now a module that implements the server side of RDMA
for the FreeBSD NFS server.
At least for now, it will be maintained as an "unofficial
port" for FreeBSD, since it was built with generative AI
and FreeBSD is working on a policy related to these submissions.
This patch puts the "glue" needed by Vinicius's nfsrdma.ko
module in the system. This "glue" was written by me without
the use of AI.
The "unofficial port" of nfsrdma.ko will be advertised on
freebsd-current@ as soon as it is available.
(Vinicius's work was sponsored by VersatupHPC.)
Since newnfs_numnfsd is now declared extern in nfs.h,
the extern declaration can be removed from assorted files.
I'll do that as a separate commit.
Suggested by: Vinicius Ferrao <versatushpc.com.br>
MFC after: 1 month
---
sys/fs/nfs/nfs.h | 1 +
sys/fs/nfsserver/nfs_nfsdkrpc.c | 43 +++++++++++++++++++++-
sys/fs/nfsserver/nfs_nfsdserv.c | 15 ++++++++
sys/rpc/clnt_bck.c | 81 +++++++++++++++++++++++++----------------
sys/rpc/krpc.h | 27 ++++++++++++++
sys/rpc/svc.h | 11 ++++++
sys/rpc/svc_auth.c | 3 ++
7 files changed, 148 insertions(+), 33 deletions(-)
diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h
index 4e79d3afbd2f..7212a685d902 100644
--- a/sys/fs/nfs/nfs.h
+++ b/sys/fs/nfs/nfs.h
@@ -747,6 +747,7 @@ struct nfsrv_descript {
#define ND_ERELOOKUP 0x40000000000
#define ND_MACHCRED 0x80000000000
#define ND_CANEXTPG 0x100000000000
+#define ND_RDMA 0x200000000000
/*
* ND_GSS should be the "or" of all GSS type authentications.
diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c
index 16c21b4e1607..20b76d6152cd 100644
--- a/sys/fs/nfsserver/nfs_nfsdkrpc.c
+++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c
@@ -120,6 +120,10 @@ VNET_DEFINE(struct nfsv4lock, nfsd_suspend_lock);
VNET_DEFINE_STATIC(bool, nfsrvd_inited) = false;
+/* Server RDMA listen hook, set by nfsrdma at MOD_LOAD. */
+svc_rdma_listen_ftype *svc_rdma_listen = NULL;
+int nfsrvd_rdma_port = 0;
+
/*
* NFS server system calls
*/
@@ -181,7 +185,8 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
nd.nd_mreq = NULL;
nd.nd_cred = NULL;
- if (VNET(nfs_privport) != 0) {
+ /* xp_socket is NULL for RDMA. */
+ if (VNET(nfs_privport) != 0 && xprt->xp_socket != NULL) {
/* Check if source port is privileged */
u_short port;
struct sockaddr *nam = nd.nd_nam;
@@ -260,6 +265,18 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
goto out;
}
+ /*
+ * For RDMA, ND_GSSINTEGRITY and ND_GSSPRIVACY are not
+ * supported.
+ */
+ if ((nd.nd_flag & (ND_GSSINTEGRITY | ND_GSSPRIVACY)) != 0 &&
+ xprt->xp_socket == NULL) {
+ svcerr_auth(rqst, AUTH_FAILED);
+ svc_freereq(rqst);
+ m_freem(nd.nd_mrep);
+ goto out;
+ }
+
/* Acquire the principal name for the RPCSEC_GSS cases. */
if ((nd.nd_flag & (ND_NFSV4 | ND_GSS)) == (ND_NFSV4 | ND_GSS)) {
rcredp = NULL;
@@ -332,6 +349,10 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
if ((nfsrv_mextpg || xprt->xp_extpg) && nd.nd_nam2 == NULL &&
PMAP_HAS_DMAP != 0)
nd.nd_flag |= ND_CANEXTPG;
+
+ /* If the xprt xp_socket == NULL, this is a RDMA call. */
+ if (xprt->xp_socket == NULL)
+ nd.nd_flag |= ND_RDMA;
#ifdef MAC
mac_cred_associate_nfsd(nd.nd_cred);
#endif
@@ -620,6 +641,21 @@ nfsrvd_nfsd(struct thread *td, struct nfsd_nfsd_args *args)
VNET(nfsrv_numnfsd)++; /* Num for this vnet. */
NFSD_UNLOCK();
+ /*
+ * If nfsrvd_rdma_port has been set and the nfsrdma.ko module
+ * has been loaded, start the server side RDMA.
+ * RDMA does not work within a vnet jail.
+ */
+ if (!jailed(curthread->td_ucred) && svc_rdma_listen != NULL &&
+ nfsrvd_rdma_port != 0) {
+ error = svc_rdma_listen(VNET(nfsrvd_pool),
+ nfsrvd_rdma_port);
+ if (error != 0) {
+ printf("nfsrvd_nfsd: RDMA listen failed=%d\n",
+ error);
+ nfsrvd_rdma_port = 0;
+ }
+ }
error = nfsrv_createdevids(args, td);
if (error == 0) {
/* An empty string implies AUTH_SYS only. */
@@ -695,6 +731,11 @@ nfsrvd_init(int terminating)
if (terminating) {
VNET(nfsd_master_proc) = NULL;
NFSD_UNLOCK();
+
+ /* Turn off the RDMA listener, if enabled. */
+ if (!jailed(curthread->td_ucred) && svc_rdma_listen != NULL)
+ (void)svc_rdma_listen(VNET(nfsrvd_pool), 0);
+
nfsrv_freealllayoutsanddevids();
nfsrv_freeallbackchannel_xprts();
svcpool_close(VNET(nfsrvd_pool));
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 704667bd27b6..dd6e4805606a 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -49,6 +49,7 @@
#include <fs/nfs/nfsport.h>
#include <sys/extattr.h>
#include <sys/filio.h>
+#include <rpc/krpc.h>
/* Global vars */
extern u_int32_t newnfs_false, newnfs_true;
@@ -1076,6 +1077,20 @@ nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
}
*tl = txdr_unsigned(cnt);
if (m3) {
+ /*
+ * For RDMA, inform the server side rdma the reduction's
+ * position.
+ */
+ if ((nd->nd_flag & ND_RDMA) != 0 && nd->nd_xprt != NULL) {
+ KASSERT(cnt > 0,
+ ("nfsrvd_read: m3 != NULL when cnt == 0"));
+ struct rpcrdma_reduce ddp;
+
+ ddp.xid = nd->nd_retxid;
+ ddp.off = (uint32_t)m_length(nd->nd_mreq, NULL);
+ ddp.len = (uint32_t)cnt;
+ (void)SVC_CONTROL(nd->nd_xprt, SVCSET_READDDP, &ddp);
+ }
nd->nd_mb->m_next = m3;
nd->nd_mb = m2;
if ((m2->m_flags & M_EXTPG) != 0) {
diff --git a/sys/rpc/clnt_bck.c b/sys/rpc/clnt_bck.c
index 9ff85b1fa2c0..65eb95c56fa5 100644
--- a/sys/rpc/clnt_bck.c
+++ b/sys/rpc/clnt_bck.c
@@ -83,6 +83,9 @@
#include <rpc/krpc.h>
#include <rpc/rpcsec_tls.h>
+/* The RDMA module will set this function pointer non-NULL. */
+clnt_bck_rdma_send_ftype *clnt_bck_rdma_send = NULL;
+
struct cmessage {
struct cmsghdr cmsg;
struct cmsgcred cmcred;
@@ -269,12 +272,15 @@ call_again:
}
mreq->m_pkthdr.len = m_length(mreq, NULL);
- /*
- * Prepend a record marker containing the packet length.
- */
- M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK);
- *mtod(mreq, uint32_t *) =
- htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t)));
+ /* RDMA doesn't need a record marker. */
+ if (xprt->xp_socket != NULL) {
+ /*
+ * Prepend a record marker containing the packet length.
+ */
+ M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK);
+ *mtod(mreq, uint32_t *) =
+ htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t)));
+ }
cr->cr_xid = xid;
mtx_lock(&ct->ct_lock);
@@ -297,36 +303,47 @@ call_again:
TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link);
mtx_unlock(&ct->ct_lock);
- /* For RPC-over-TLS, copy mrep to a chain of ext_pgs. */
- if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0) {
- /*
- * Copy the mbuf chain to a chain of
- * ext_pgs mbuf(s) as required by KERN_TLS.
- */
- maxextsiz = TLS_MAX_MSG_SIZE_V10_2;
+ if (xprt->xp_socket != NULL) {
+ /* Handle TCP sockets. */
+ /* For RPC-over-TLS, copy mrep to a chain of ext_pgs. */
+ if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0) {
+ /*
+ * Copy the mbuf chain to a chain of
+ * ext_pgs mbuf(s) as required by KERN_TLS.
+ */
+ maxextsiz = TLS_MAX_MSG_SIZE_V10_2;
#ifdef KERN_TLS
- if (rpctls_getinfo(&maxlen, false, false))
- maxextsiz = min(maxextsiz, maxlen);
+ if (rpctls_getinfo(&maxlen, false, false))
+ maxextsiz = min(maxextsiz, maxlen);
#endif
- mreq = _rpc_copym_into_ext_pgs(mreq, maxextsiz);
- }
- /*
- * sosend consumes mreq.
- */
- sx_xlock(&xprt->xp_lock);
- error = sosend(xprt->xp_socket, NULL, NULL, mreq, NULL, 0, curthread);
- mreq = NULL;
- if (error == EMSGSIZE) {
- SOCK_SENDBUF_LOCK(xprt->xp_socket);
- sbwait(xprt->xp_socket, SO_SND);
- SOCK_SENDBUF_UNLOCK(xprt->xp_socket);
+ mreq = _rpc_copym_into_ext_pgs(mreq, maxextsiz);
+ }
+ /*
+ * sosend consumes mreq.
+ */
+ sx_xlock(&xprt->xp_lock);
+ error = sosend(xprt->xp_socket, NULL, NULL, mreq, NULL, 0,
+ curthread);
+ mreq = NULL;
+ if (error == EMSGSIZE) {
+ SOCK_SENDBUF_LOCK(xprt->xp_socket);
+ sbwait(xprt->xp_socket, SO_SND);
+ SOCK_SENDBUF_UNLOCK(xprt->xp_socket);
+ sx_xunlock(&xprt->xp_lock);
+ AUTH_VALIDATE(auth, xid, NULL, NULL);
+ mtx_lock(&ct->ct_lock);
+ TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
+ goto call_again;
+ }
sx_xunlock(&xprt->xp_lock);
- AUTH_VALIDATE(auth, xid, NULL, NULL);
- mtx_lock(&ct->ct_lock);
- TAILQ_REMOVE(&ct->ct_pending, cr, cr_link);
- goto call_again;
+ } else {
+ /* Handle RDMA. */
+ if (clnt_bck_rdma_send != NULL) {
+ clnt_bck_rdma_send(xprt, mreq);
+ mreq = NULL;
+ } else
+ error = ENOTCONN;
}
- sx_xunlock(&xprt->xp_lock);
reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL;
reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf;
diff --git a/sys/rpc/krpc.h b/sys/rpc/krpc.h
index f77e2d2d7428..f53b07ed86b6 100644
--- a/sys/rpc/krpc.h
+++ b/sys/rpc/krpc.h
@@ -42,6 +42,10 @@ enum clnt_stat clnt_bck_call(CLIENT *, struct rpc_callextra *, rpcproc_t,
struct mbuf *, struct mbuf **, struct timeval, SVCXPRT *);
struct mbuf *_rpc_copym_into_ext_pgs(struct mbuf *, int);
+/* Callback function for server side RDMA. */
+typedef int clnt_bck_rdma_send_ftype(SVCXPRT *xprt, struct mbuf *m);
+extern clnt_bck_rdma_send_ftype *clnt_bck_rdma_send;
+
/*
* A pending RPC request which awaits a reply. Requests which have
* received their reply will have cr_xid set to zero and cr_mrep to
@@ -136,6 +140,29 @@ struct cf_conn { /* kept in xprt->xp_p1 for actual connection */
void rpcnl_init(void);
+/* RDMA procedures. */
+enum rdma_proc {
+ RDMA_MSG = 0,
+ RDMA_NOMSG = 1,
+ RDMA_MSGP = 2, /* Not used. */
+ RDMA_DONE = 3, /* Not used. */
+ RDMA_ERROR = 4
+};
+
+enum rdma_errcode {
+ RDMA_ERR_VERS = 1,
+ RDMA_ERR_CHUNK = 2
+};
+
+/* Structure use by both client and server RDMA for reductions (RFC8166). */
+struct rpcrdma_reduce {
+ struct iovec *iov;
+ uint32_t xid;
+ uint32_t off;
+ uint32_t len;
+ uint8_t into_mem;
+};
+
#endif /* _KERNEL */
#endif /* _RPC_KRPC_H_ */
diff --git a/sys/rpc/svc.h b/sys/rpc/svc.h
index e73e61e5ac93..a7cbb639f48e 100644
--- a/sys/rpc/svc.h
+++ b/sys/rpc/svc.h
@@ -76,6 +76,7 @@
#define SVCSET_VERSQUIET 2
#define SVCGET_CONNMAXREC 3
#define SVCSET_CONNMAXREC 4
+#define SVCSET_READDDP 5
/*
* Operations for rpc_control().
@@ -342,6 +343,16 @@ typedef struct __rpc_svcpool {
SVCGROUP sp_groups[SVC_MAXGROUPS]; /* Thread/port groups. */
} SVCPOOL;
+/*
+ * svc_rdma_listen is the function in the nfsrdma.ko module called to
+ * enable the RDMA server side listener.
+ * nfsrvd_rdma_port - The port# that RDMA should listen on for the NFS server.
+ */
+typedef int svc_rdma_listen_ftype(SVCPOOL *pool, int port);
+extern svc_rdma_listen_ftype *svc_rdma_listen;
+extern int nfsrvd_rdma_port;
+extern int newnfs_numnfsd;
+
/*
* Operations defined on an SVCXPRT handle
*
diff --git a/sys/rpc/svc_auth.c b/sys/rpc/svc_auth.c
index acbb1112e270..030f55e44a97 100644
--- a/sys/rpc/svc_auth.c
+++ b/sys/rpc/svc_auth.c
@@ -107,6 +107,9 @@ _authenticate(struct svc_req *rqst, struct rpc_msg *msg)
dummy = _svcauth_rpcsec_gss(rqst, msg);
return (dummy);
case AUTH_TLS:
+ /* Not supported for RDMA. */
+ if (rqst->rq_xprt->xp_socket == NULL)
+ return (AUTH_REJECTEDCRED);
dummy = _svcauth_rpcsec_tls(rqst, msg);
return (dummy);
default: