All of lore.kernel.org
 help / color / mirror / Atom feed
From: Taehee Yoo <ap420073@gmail.com>
To: "Alex Deucher" <alexander.deucher@amd.com>,
	"Alexei Starovoitov" <ast@kernel.org>,
	amd-gfx@lists.freedesktop.org,
	"Andrew Lunn" <andrew+netdev@lunn.ch>,
	"Andrii Nakryiko" <andrii@kernel.org>,
	"Bill Wendling" <morbo@google.com>,
	bpf@vger.kernel.org, "Christian König" <christian.koenig@amd.com>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"David Airlie" <airlied@gmail.com>,
	"David S. Miller" <davem@davemloft.net>,
	"Donald Hunter" <donald.hunter@gmail.com>,
	dri-devel@lists.freedesktop.org,
	"Eduard Zingerman" <eddyz87@gmail.com>,
	"Emil Tsalapatis" <emil@etsalapatis.com>,
	"Eric Dumazet" <edumazet@google.com>,
	"Felix Kuehling" <Felix.Kuehling@amd.com>,
	"Hoyeon Lee" <hoyeon.rhee@gmail.com>,
	"Ilias Apalodimas" <ilias.apalodimas@linaro.org>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Jesper Dangaard Brouer" <hawk@kernel.org>,
	"Jiri Olsa" <jolsa@kernel.org>,
	"John Fastabend" <john.fastabend@gmail.com>,
	"Justin Stitt" <justinstitt@google.com>,
	"Kees Cook" <kees@kernel.org>,
	"Kumar Kartikeya Dwivedi" <memxor@gmail.com>,
	"Leon Romanovsky" <leon@kernel.org>,
	linaro-mm-sig@lists.linaro.org, linux-hardening@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
	linux-media@vger.kernel.org, linux-rdma@vger.kernel.org,
	llvm@lists.linux.dev, "Mark Bloch" <mbloch@nvidia.com>,
	"Martin KaFai Lau" <martin.lau@linux.dev>,
	"Michael Chan" <michael.chan@broadcom.com>,
	"Nathan Chancellor" <nathan@kernel.org>,
	netdev@vger.kernel.org,
	"Nick Desaulniers" <ndesaulniers@google.com>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Pavan Chebbi" <pavan.chebbi@broadcom.com>,
	"Saeed Mahameed" <saeedm@nvidia.com>,
	"Shuah Khan" <shuah@kernel.org>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Simon Horman" <horms@kernel.org>, "Song Liu" <song@kernel.org>,
	"Stanislav Fomichev" <sdf@fomichev.me>,
	"Sumit Semwal" <sumit.semwal@linaro.org>,
	"Taehee Yoo" <ap420073@gmail.com>,
	"Tariq Toukan" <tariqt@nvidia.com>,
	"Yonghong Song" <yonghong.song@linux.dev>
Subject: [RFC PATCH net-next 10/13] net/mlx5e: add knod XDP offload support
Date: Sun, 19 Jul 2026 17:58:54 +0000	[thread overview]
Message-ID: <20260719175857.4071636-11-ap420073@gmail.com> (raw)
In-Reply-To: <20260719175857.4071636-1-ap420073@gmail.com>

Let mlx5e act as a knod NIC: register with the knod core, feed received
packets to the accelerator over the per-queue SPSC ring, and transmit
accelerator verdicts (XDP_TX) back through the XDP SQ.  Restricted to
inline-none TX and single-page MTU.

Signed-off-by: Taehee Yoo <ap420073@gmail.com>
(cherry picked from commit 218c3a122d6b53638b99441560b7a0da65f6d8c1)
---
 drivers/net/ethernet/mellanox/mlx5/core/en.h  |  10 +
 .../net/ethernet/mellanox/mlx5/core/en/xdp.c  | 351 ++++++++++++++++++
 .../net/ethernet/mellanox/mlx5/core/en/xdp.h  |  19 +-
 .../net/ethernet/mellanox/mlx5/core/en_main.c |  38 +-
 .../net/ethernet/mellanox/mlx5/core/en_rx.c   | 107 +++++-
 .../net/ethernet/mellanox/mlx5/core/en_txrx.c |  45 +++
 6 files changed, 564 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index d507289096c2..1401a24e1740 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -48,6 +48,7 @@
 #include <net/udp_tunnel.h>
 #include <net/switchdev.h>
 #include <net/psp/types.h>
+#include <net/knod.h>
 #include <net/xdp.h>
 #include <linux/dim.h>
 #include <linux/bits.h>
@@ -568,6 +569,8 @@ struct mlx5e_icosq {
 
 struct mlx5e_frag_page {
 	netmem_ref netmem;
+	struct page_pool *pp;
+	u32 page_idx;
 	u16 frags;
 };
 
@@ -739,6 +742,11 @@ struct mlx5e_rq {
 
 	struct mlx5e_xdp_buff mxbuf;
 
+	struct knod_dev *knodev;
+	struct knod_netdev *knetdev;
+	u32                    knod_spsc_prod_head;
+	bool                   knod_spsc_prod_valid;
+
 	/* AF_XDP zero-copy */
 	struct xsk_buff_pool  *xsk_pool;
 
@@ -985,6 +993,8 @@ struct mlx5e_priv {
 	struct dentry             *dfs_root;
 	struct mlx5_devcom_comp_dev *devcom;
 	struct ethtool_fec_hist_range *fec_ranges;
+	struct knod_dev *knodev;
+	struct knod_netdev *knetdev;
 };
 
 static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
index d8c7cb8837d7..82adfc9b7c1b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
@@ -35,8 +35,18 @@
 #include "en/xdp.h"
 #include "en/params.h"
 #include <linux/bitfield.h>
+#include <linux/module.h>
 #include <net/page_pool/helpers.h>
 
+INDIRECT_CALLABLE_SCOPE bool
+mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd,
+		     int check_result, struct xsk_tx_metadata *meta);
+
+static inline struct page_pool *mlx5e_knod_bd_pp(struct spsc_bd *bd)
+{
+	return likely(bd->pp) ? bd->pp : netmem_get_pp(bd->netmem);
+}
+
 int mlx5e_xdp_max_mtu(struct mlx5e_params *params,
 		      struct mlx5e_rq_opt_param *rqo)
 {
@@ -58,6 +68,36 @@ int mlx5e_xdp_max_mtu(struct mlx5e_params *params,
 	return MLX5E_HW2SW_MTU(params, SKB_MAX_HEAD(hr));
 }
 
+static inline bool mlx5e_xmit_xdp_offload_buff(struct mlx5e_xdpsq *sq,
+					       struct mlx5e_rq *rq,
+					       struct spsc_bd *bd)
+{
+	struct mlx5e_xmit_data_frags xdptxdf = {};
+	struct mlx5e_xmit_data *xdptxd;
+
+	/* attach is restricted to inline-none NICs, so the WQE inlines no
+	 * header and xdptxd->data is never read (left NULL here).
+	 */
+	xdptxd = &xdptxdf.xd;
+	xdptxd->len = bd->len;
+	xdptxd->has_frags = 0;
+	xdptxd->dma_addr = netmem_to_net_iov(bd->netmem)->desc.dma_addr +
+			   bd->off;
+
+	if (!mlx5e_xmit_xdp_frame(sq, xdptxd, 0, NULL))
+		return false;
+
+	mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo,
+			     (union mlx5e_xdp_info) {
+				.mode = MLX5E_XDP_XMIT_MODE_OFFLOAD });
+	mlx5e_xdpi_fifo_push(&sq->db.xdpi_fifo,
+			     (union mlx5e_xdp_info) {
+				.offload.netmem = bd->netmem,
+				.offload.pp = mlx5e_knod_bd_pp(bd) });
+
+	return true;
+}
+
 static inline bool
 mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_rq *rq,
 		    struct xdp_buff *xdp)
@@ -353,6 +393,154 @@ bool mlx5e_xdp_handle(struct mlx5e_rq *rq,
 	}
 }
 
+static inline u16 mlx5e_xdpsq_get_avail(struct mlx5e_xdpsq *sq)
+{
+	if (sq->pc == sq->cc)
+		return sq->wq.fbc.sz_m1 + 1;
+
+	return sq->wq.fbc.sz_m1 & (sq->cc - sq->pc);
+}
+
+static inline u16 mlx5e_xdpsq_get_avail_after_poll(struct mlx5e_xdpsq *sq)
+{
+	u16 avail = mlx5e_xdpsq_get_avail(sq);
+
+	if (likely(avail))
+		return avail;
+
+	mlx5e_xmit_xdp_doorbell(sq);
+	mlx5e_poll_xdpsq_cq(&sq->cq);
+
+	return mlx5e_xdpsq_get_avail(sq);
+}
+
+struct mlx5e_knod_release_batch {
+	struct spsc_bd *bds[NAPI_POLL_WEIGHT];
+	struct spsc_pass_bd pass[NAPI_POLL_WEIGHT];
+};
+
+static struct mlx5e_knod_release_batch
+mlx5e_knod_release_batch[KNOD_SPSC_MAX];
+
+static noinline int
+mlx5e_rx_offload_release_pending(struct mlx5e_rq *rq,
+				 struct knod_work_priv *wpriv,
+				 bool flush, int budget)
+{
+	struct knod_dev *knodev = rq->knodev;
+	struct mlx5e_xdpsq *sq = rq->xdpsq;
+	struct mlx5e_knod_release_batch *batch =
+		&mlx5e_knod_release_batch[rq->ix];
+	struct spsc_bd **bds = batch->bds;
+	struct spsc_pass_bd *pass = batch->pass;
+	int cnt, i, done = 0;
+
+	while (done < budget) {
+		int pass_cnt = 0;
+
+		if (!mlx5e_xdpsq_get_avail_after_poll(sq))
+			break;
+		cnt = min(NAPI_POLL_WEIGHT, budget - done);
+
+		spsc_release(&wpriv->spsc_bds, (void **)bds, cnt, &cnt);
+		if (!cnt)
+			break;
+
+		for (i = 0; i < cnt; i++) {
+			switch ((u32)bds[i]->act) {
+			case KNOD_ACT_INFLIGHT:
+				fallthrough;
+			case KNOD_IPSEC_INFLIGHT:
+				goto stop_release;
+			case KNOD_IPSEC_PASS:
+				fallthrough;
+			case KNOD_IPSEC_DROP:
+				/* Finish worker has set the final verdict.
+				 * Safe to recycle the netmem page now.
+				 */
+				page_pool_recycle_direct_netmem(
+					mlx5e_knod_bd_pp(bds[i]),
+					bds[i]->netmem);
+				break;
+			case KNOD_TX:
+				if (!mlx5e_xmit_xdp_offload_buff(rq->xdpsq, rq,
+								 bds[i]))
+					goto stop_release;
+				break;
+			case XDP_DROP:
+				fallthrough;
+			case XDP_ABORTED:
+				rq->stats->xdp_drop++;
+				page_pool_recycle_direct_netmem(
+					mlx5e_knod_bd_pp(bds[i]),
+					bds[i]->netmem);
+				break;
+			case XDP_PASS:
+				/* Hand to the common device->host delivery:
+				 * accumulate here, flush to knod_d2h_copy
+				 * after the bd loop.  The source page is
+				 * recycled by knod_d2h_drain once the copy
+				 * has landed, so it is NOT recycled here.
+				 */
+				pass[pass_cnt].netmem = bds[i]->netmem;
+				pass[pass_cnt].page_idx = bds[i]->page_idx;
+				pass[pass_cnt].off = bds[i]->off;
+				pass[pass_cnt].len = bds[i]->len;
+				pass_cnt++;
+				break;
+			case XDP_REDIRECT:
+				/* No redirect delivery path yet; recycle. */
+				page_pool_recycle_direct_netmem(
+					mlx5e_knod_bd_pp(bds[i]),
+					bds[i]->netmem);
+				break;
+			default:
+				/* Unknown value: either the accel shader
+				 * did not stamp a verdict for this slot
+				 * (lane skip bug) or the slot never went
+				 * through an accel at all. Treat as DROP +
+				 * recycle + WARN so the ring keeps advancing.
+				 */
+				rq->stats->xdp_drop++;
+				pr_warn_ratelimited("mlx5 nod: invalid bd->act=0x%llx rq%d, treating as DROP\n",
+						    bds[i]->act, rq->ix);
+				page_pool_recycle_direct_netmem(
+					mlx5e_knod_bd_pp(bds[i]),
+					bds[i]->netmem);
+				break;
+			}
+		}
+stop_release:
+		spsc_release_commit(&wpriv->spsc_bds, i);
+		done += i;
+
+		/* Issue the device->host copies for this batch's PASS bds. */
+		if (pass_cnt)
+			knod_d2h_copy(knodev, rq->ix, pass, pass_cnt);
+
+		if (i < cnt)
+			break;
+	}
+
+	if (flush)
+		mlx5e_xmit_xdp_doorbell(sq);
+
+	return done;
+}
+
+int mlx5e_rx_offload_act_handler(struct mlx5e_rq *rq, bool flush, int budget)
+{
+	struct knod_work_priv *wpriv = &rq->knodev->wpriv[rq->ix];
+
+	if (!spsc_pending(&wpriv->spsc_bds)) {
+		if (flush)
+			mlx5e_xmit_xdp_doorbell(rq->xdpsq);
+		return 0;
+	}
+
+	return mlx5e_rx_offload_release_pending(rq, wpriv, flush, budget);
+}
+
 static u16 mlx5e_xdpsq_get_next_pi(struct mlx5e_xdpsq *sq, u16 size)
 {
 	struct mlx5_wq_cyc *wq = &sq->wq;
@@ -741,6 +929,18 @@ static void mlx5e_free_xdpsq_desc(struct mlx5e_xdpsq *sq,
 			(*xsk_frames)++;
 			break;
 		}
+		case MLX5E_XDP_XMIT_MODE_OFFLOAD: {
+			netmem_ref netmem;
+			struct page_pool *pp;
+
+			xdpi = mlx5e_xdpi_fifo_pop(xdpi_fifo);
+			netmem = xdpi.offload.netmem;
+			pp = xdpi.offload.pp;
+
+			page_pool_recycle_direct_netmem(pp, netmem);
+
+			break;
+		}
 		default:
 			WARN_ON_ONCE(true);
 		}
@@ -974,3 +1174,154 @@ void mlx5e_set_xmit_fp(struct mlx5e_xdpsq *sq, bool is_mpw)
 	sq->xmit_xdp_frame = is_mpw ?
 		mlx5e_xmit_xdp_frame_mpwqe : mlx5e_xmit_xdp_frame;
 }
+
+static int mlx5e_rx_offload_xdp_attach(struct knod_dev *knodev)
+{
+	struct mlx5e_priv *priv = netdev_priv(knodev->netdev);
+	struct mlx5e_params *params = &priv->channels.params;
+	int max_mtu = mlx5e_xdp_max_mtu(params, NULL);
+
+	if (knodev->netdev->mtu > max_mtu) {
+		netdev_warn(knodev->netdev,
+			    "MTU %u too big for single-page RX offload (max %d)\n",
+			    knodev->netdev->mtu, max_mtu);
+		return -EOPNOTSUPP;
+	}
+
+	/* The offload TX WQE carries the packet only in a data segment; the
+	 * eth header is not inlined (a zero dummy stands in). NICs that require
+	 * a minimum inline header (e.g. ConnectX-4) would transmit that dummy,
+	 * so only allow attach when the device needs no inline header.
+	 */
+	if (params->tx_min_inline_mode != MLX5_INLINE_MODE_NONE) {
+		netdev_warn(knodev->netdev,
+			    "knod offload requires a NIC with inline header mode 'none'\n");
+		return -EOPNOTSUPP;
+	}
+
+	pr_debug("Attaching XDP offload to netdev %s\n", knodev->netdev->name);
+	WRITE_ONCE(priv->knodev, knodev);
+
+	return 0;
+}
+
+static int mlx5e_rx_offload_xdp_detach(struct knod_dev *knodev)
+{
+	struct mlx5e_priv *priv = netdev_priv(knodev->netdev);
+
+	pr_debug("Detaching XDP offload from netdev %s\n",
+		 knodev->netdev->name);
+	WRITE_ONCE(priv->knodev, NULL);
+
+	return 0;
+}
+
+struct knod_nic_ops nic_ops = {
+	.attach = mlx5e_rx_offload_xdp_attach,
+	.detach = mlx5e_rx_offload_xdp_detach,
+};
+
+int mlx5e_knod_init(struct mlx5e_priv *priv)
+{
+	struct knod_netdev *knetdev;
+
+	knetdev = kzalloc_obj(struct knod_netdev, GFP_KERNEL);
+	if (!knetdev) {
+		pr_debug("Failed to allocate knetdev\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&knetdev->list);
+	knetdev->dev = priv->netdev;
+	knetdev->priv = priv;
+	knetdev->nic_ops = &nic_ops;
+	knetdev->owner = THIS_MODULE;
+	knetdev->flags |= KNOD_FLAGS_XDP;
+	knod_netdev_register(knetdev);
+	priv->knetdev = knetdev;
+
+	return 0;
+}
+
+void mlx5e_knod_uninit(struct mlx5e_priv *priv)
+{
+	knod_netdev_unregister(priv->knetdev);
+	kfree(priv->knetdev);
+	priv->knetdev = NULL;
+	kfree(priv->knodev);
+	WRITE_ONCE(priv->knodev, NULL);
+}
+
+void mlx5e_rx_offload_set_napi(struct mlx5e_priv *priv)
+{
+	struct knod_dev *knodev = priv->knodev;
+	int i;
+
+	if (!knodev)
+		return;
+
+	for (i = 0; i < priv->channels.num; i++) {
+		struct mlx5e_channel *c = priv->channels.c[i];
+		struct spsc_ring *r = &knodev->wpriv[i].spsc_bds;
+
+		WRITE_ONCE(knodev->wpriv[i].napi, &c->napi);
+		c->rq.knod_spsc_prod_head = READ_ONCE(r->head);
+		c->rq.knod_spsc_prod_valid = true;
+	}
+}
+
+void mlx5e_rx_offload_clear_napi(struct mlx5e_priv *priv)
+{
+	struct knod_dev *knodev = priv->knodev;
+	int i;
+
+	if (!knodev)
+		return;
+
+	for (i = 0; i < KNOD_SPSC_MAX; i++)
+		WRITE_ONCE(knodev->wpriv[i].napi, NULL);
+}
+
+void mlx5e_rx_offload_start(struct mlx5e_priv *priv)
+{
+	if (!priv->knodev)
+		return;
+
+	knod_dev_start(priv->knodev);
+}
+
+void mlx5e_rx_offload_stop(struct mlx5e_priv *priv)
+{
+	struct knod_dev *knodev = priv->knodev;
+	int i;
+
+	if (!priv->knodev)
+		return;
+
+	knod_dev_stop(knodev);
+
+	synchronize_net();
+	for (i = 0; i < KNOD_SPSC_MAX; i++) {
+		struct spsc_bd *bd;
+
+		if (i < priv->channels.num) {
+			struct mlx5e_rq *rq = &priv->channels.c[i]->rq;
+
+			mlx5e_knod_spsc_flush(rq);
+			rq->knod_spsc_prod_head = 0;
+			rq->knod_spsc_prod_valid = false;
+		}
+
+		WRITE_ONCE(knodev->wpriv[i].napi, NULL);
+		/*
+		 * RX is quiesced now (worker stopped by knod_dev_stop, NAPI
+		 * drained by synchronize_net).  Return any frames the GPU
+		 * worker did not consume back to the page_pool before the RX
+		 * page_pool is torn down on interface down.
+		 */
+		spsc_rewind(&knodev->wpriv[i].spsc_bds);
+		while (!spsc_pop(&knodev->wpriv[i].spsc_bds, (void **)&bd))
+			page_pool_put_full_netmem(netmem_get_pp(bd->netmem),
+						  bd->netmem, true);
+	}
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
index 3c54f8962664..4de6babdafc5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
@@ -34,6 +34,7 @@
 
 #include <linux/indirect_call_wrapper.h>
 #include <net/xdp_sock.h>
+#include <net/knod.h>
 
 #include "en.h"
 #include "en/txrx.h"
@@ -64,6 +65,8 @@ enum mlx5e_xdp_xmit_mode {
 	 * page. The UMEM Completion Ring producer pointer has to be increased.
 	 */
 	MLX5E_XDP_XMIT_MODE_XSK,
+
+	MLX5E_XDP_XMIT_MODE_OFFLOAD,
 };
 
 /* xmit_mode entry is pushed to the fifo per packet, followed by multiple
@@ -78,6 +81,9 @@ enum mlx5e_xdp_xmit_mode {
  *
  * MLX5E_XDP_XMIT_MODE_XSK:
  *    frame.xsk_meta.
+ *
+ * MLX5E_XDP_XMIT_MODE_OFFLOAD:
+ *    offload.netmem.
  */
 #define MLX5E_XDP_FIFO_ENTRIES2DS_MAX_RATIO 4
 
@@ -93,6 +99,10 @@ union mlx5e_xdp_info {
 		struct page *page;
 	} page;
 	struct xsk_tx_metadata_compl xsk_meta;
+	struct {
+		netmem_ref netmem;
+		struct page_pool *pp;
+	} offload;
 };
 
 struct mlx5e_xsk_param;
@@ -100,14 +110,21 @@ int mlx5e_xdp_max_mtu(struct mlx5e_params *params,
 		      struct mlx5e_rq_opt_param *rqo);
 bool mlx5e_xdp_handle(struct mlx5e_rq *rq,
 		      struct bpf_prog *prog, struct mlx5e_xdp_buff *mlctx);
+int mlx5e_rx_offload_act_handler(struct mlx5e_rq *rq, bool flush, int budget);
+void mlx5e_knod_spsc_flush(struct mlx5e_rq *rq);
 void mlx5e_xdp_mpwqe_complete(struct mlx5e_xdpsq *sq);
 bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq);
 void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq);
 void mlx5e_set_xmit_fp(struct mlx5e_xdpsq *sq, bool is_mpw);
+int mlx5e_knod_init(struct mlx5e_priv *priv);
+void mlx5e_knod_uninit(struct mlx5e_priv *priv);
 void mlx5e_xdp_rx_poll_complete(struct mlx5e_rq *rq);
 int mlx5e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
 		   u32 flags);
-
+void mlx5e_rx_offload_start(struct mlx5e_priv *priv);
+void mlx5e_rx_offload_stop(struct mlx5e_priv *priv);
+void mlx5e_rx_offload_set_napi(struct mlx5e_priv *priv);
+void mlx5e_rx_offload_clear_napi(struct mlx5e_priv *priv);
 extern const struct xdp_metadata_ops mlx5e_xdp_metadata_ops;
 extern const struct xsk_tx_metadata_ops mlx5e_xsk_tx_metadata_ops;
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index aa8610cedaa8..cfc6ba6dc492 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -918,6 +918,7 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params,
 	pool_size = 1 << params->log_rq_mtu_frames;
 
 	rq->mkey_be = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey);
+	rq->knodev = rq->priv->knodev;
 
 	switch (rq->wq_type) {
 	case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
@@ -1022,7 +1023,7 @@ static int mlx5e_alloc_rq(struct mlx5e_params *params,
 		pp_params.queue_idx = rq->ix;
 
 		/* Shampo header data split allow for unreadable netmem */
-		if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state))
+		if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state) || rq->knodev)
 			pp_params.flags |= PP_FLAG_ALLOW_UNREADABLE_NETMEM;
 
 		/* page_pool can be used even when there is no rq->xdp_prog,
@@ -2873,7 +2874,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
 	c->netdev   = priv->netdev;
 	c->mkey_be  = cpu_to_be32(mdev->mlx5e_res.hw_objs.mkey);
 	c->num_tc   = mlx5e_get_dcb_num_tc(params);
-	c->xdp      = !!params->xdp_prog;
+	c->xdp      = !!params->xdp_prog || !!priv->knodev;
 	c->stats    = &priv->channel_stats[ix]->ch;
 	c->aff_mask = irq_get_effective_affinity_mask(irq);
 	c->lag_port = mlx5e_enumerate_lag_port(mdev, ix);
@@ -3365,6 +3366,8 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv)
 
 	if (priv->rx_res)
 		mlx5e_rx_res_channels_activate(priv->rx_res, &priv->channels);
+
+	mlx5e_rx_offload_set_napi(priv);
 }
 
 static void mlx5e_cancel_tx_timeout_work(struct mlx5e_priv *priv)
@@ -3376,6 +3379,8 @@ static void mlx5e_cancel_tx_timeout_work(struct mlx5e_priv *priv)
 
 void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv)
 {
+	mlx5e_rx_offload_clear_napi(priv);
+
 	if (priv->rx_res)
 		mlx5e_rx_res_channels_deactivate(priv->rx_res);
 
@@ -3592,6 +3597,7 @@ int mlx5e_open(struct net_device *netdev)
 		mlx5e_modify_admin_state(priv->mdev, MLX5_PORT_UP);
 	mutex_unlock(&priv->state_lock);
 
+	mlx5e_rx_offload_start(priv);
 	return err;
 }
 
@@ -3623,6 +3629,7 @@ int mlx5e_close(struct net_device *netdev)
 	if (!netif_device_present(netdev))
 		return -ENODEV;
 
+	mlx5e_rx_offload_stop(priv);
 	mutex_lock(&priv->state_lock);
 	mlx5e_modify_admin_state(priv->mdev, MLX5_PORT_DOWN);
 	err = mlx5e_close_locked(netdev);
@@ -4475,7 +4482,7 @@ void mlx5e_set_xdp_feature(struct mlx5e_priv *priv)
 	    params->packet_merge.type == MLX5E_PACKET_MERGE_NONE)
 		val = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
 		      NETDEV_XDP_ACT_XSK_ZEROCOPY |
-		      NETDEV_XDP_ACT_RX_SG;
+		      NETDEV_XDP_ACT_RX_SG | NETDEV_XDP_ACT_HW_OFFLOAD;
 
 	if (netdev->netdev_ops->ndo_xdp_xmit && params->xdp_prog)
 		val |= NETDEV_XDP_ACT_NDO_XMIT |
@@ -4738,6 +4745,14 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu,
 		goto out;
 	}
 
+	if (priv->knodev && new_mtu > mlx5e_xdp_max_mtu(&new_params, NULL)) {
+		netdev_warn(netdev,
+			    "MTU %d too big for single-page RX offload (max %d)\n",
+			    new_mtu, mlx5e_xdp_max_mtu(&new_params, NULL));
+		err = -EINVAL;
+		goto out;
+	}
+
 	if (priv->xsk.refcnt &&
 	    !mlx5e_xsk_validate_mtu(netdev, &priv->channels,
 				    &new_params, priv->mdev)) {
@@ -5261,15 +5276,30 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
 
 static int mlx5e_xdp(struct net_device *dev, struct netdev_bpf *xdp)
 {
+	struct mlx5e_priv *priv = netdev_priv(dev);
+	struct knod_dev *knodev = priv->knodev;
+	int rc;
+
 	switch (xdp->command) {
 	case XDP_SETUP_PROG:
 		return mlx5e_xdp_set(dev, xdp->prog);
 	case XDP_SETUP_XSK_POOL:
 		return mlx5e_xsk_setup_pool(dev, xdp->xsk.pool,
 					    xdp->xsk.queue_id);
+	case XDP_SETUP_PROG_HW:
+	case BPF_OFFLOAD_MAP_ALLOC:
+	case BPF_OFFLOAD_MAP_FREE:
+		if (!knodev)
+			return -EOPNOTSUPP;
+
+		rc = knod_dev_xdp_install(knodev, xdp);
+		break;
+
 	default:
 		return -EINVAL;
 	}
+
+	return rc;
 }
 
 #ifdef CONFIG_MLX5_ESWITCH
@@ -6931,6 +6961,7 @@ static int _mlx5e_probe(struct auxiliary_device *adev)
 	mlx5e_dcbnl_init_app(priv);
 	mlx5_core_uplink_netdev_set(mdev, netdev);
 	mlx5e_params_print_info(mdev, &priv->channels.params);
+	mlx5e_knod_init(priv);
 	return 0;
 
 err_resume:
@@ -6982,6 +7013,7 @@ static void _mlx5e_remove(struct auxiliary_device *adev)
 	struct mlx5e_priv *priv = netdev_priv(netdev);
 	struct mlx5_core_dev *mdev = edev->mdev;
 
+	mlx5e_knod_uninit(priv);
 	mlx5_eswitch_safe_aux_devs_remove(mdev);
 	mlx5_core_uplink_netdev_set(mdev, NULL);
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 6fbc0441c4b8..ff4627a0895c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -276,15 +276,20 @@ static int mlx5e_page_alloc_fragmented(struct page_pool *pp,
 				       struct mlx5e_frag_page *frag_page)
 {
 	netmem_ref netmem = page_pool_dev_alloc_netmems(pp);
+	u32 page_idx = 0;
 
 	if (unlikely(!netmem))
 		return -ENOMEM;
 
 	page_pool_fragment_netmem(netmem, MLX5E_PAGECNT_BIAS_MAX);
+	if (netmem_is_net_iov(netmem))
+		page_idx = net_iov_binding_idx(netmem_to_net_iov(netmem));
 
 	*frag_page = (struct mlx5e_frag_page) {
-		.netmem	= netmem,
-		.frags	= 0,
+		.netmem		= netmem,
+		.pp		= pp,
+		.page_idx	= page_idx,
+		.frags		= 0,
 	};
 
 	return 0;
@@ -1572,19 +1577,92 @@ static void mlx5e_fill_mxbuf(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
 	mxbuf->rq = rq;
 }
 
+static inline int mlx5e_knod_spsc_produce_defer(struct mlx5e_rq *rq,
+						struct knod_work_priv *wpriv,
+						struct spsc_bd **bd)
+{
+	struct spsc_ring *r = &wpriv->spsc_bds;
+	unsigned int head;
+	unsigned int tail;
+
+	if (unlikely(!rq->knod_spsc_prod_valid)) {
+		rq->knod_spsc_prod_head = READ_ONCE(r->head);
+		rq->knod_spsc_prod_valid = true;
+	}
+
+	head = rq->knod_spsc_prod_head;
+	/* acquire tail to observe the slots the GPU worker has released */
+	tail = smp_load_acquire(&r->tail);
+	if (unlikely(head - tail > r->mask))
+		return -ENOSPC;
+
+	*bd = r->slots[head & r->mask];
+	rq->knod_spsc_prod_head = head + 1;
+
+	return 0;
+}
+
+void mlx5e_knod_spsc_flush(struct mlx5e_rq *rq)
+{
+	struct knod_work_priv *wpriv;
+	struct spsc_ring *r;
+	unsigned int head;
+
+	if (unlikely(!rq->knodev || !rq->knod_spsc_prod_valid))
+		return;
+
+	wpriv = &rq->knodev->wpriv[rq->ix];
+	r = &wpriv->spsc_bds;
+	head = rq->knod_spsc_prod_head;
+	if (head == READ_ONCE(r->head))
+		return;
+
+	/* drain WC descriptor stores before publishing the new head */
+	wmb();
+	/* release: publish the produced descriptors to the GPU worker */
+	smp_store_release(&r->head, head);
+}
+
 static struct sk_buff *
 mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi,
 			  struct mlx5_cqe64 *cqe, u32 cqe_bcnt)
 {
 	struct mlx5e_frag_page *frag_page = wi->frag_page;
 	u16 rx_headroom = rq->buff.headroom;
+	struct knod_work_priv *wpriv;
 	struct bpf_prog *prog;
 	struct sk_buff *skb;
+	struct spsc_bd *bd;
 	u32 metasize = 0;
 	void *va, *data;
 	dma_addr_t addr;
 	u32 frag_size;
 
+	if (likely(rq->knodev)) {
+		wpriv = &rq->knodev->wpriv[rq->ix];
+		if (unlikely(mlx5e_knod_spsc_produce_defer(rq, wpriv, &bd))) {
+			mlx5e_knod_spsc_flush(rq);
+			mlx5e_rx_offload_act_handler(rq, false, INT_MAX);
+			if (mlx5e_knod_spsc_produce_defer(rq, wpriv, &bd)) {
+				rq->stats->buff_alloc_err++;
+				return NULL;
+			}
+		}
+
+		bd->netmem = frag_page->netmem;
+		bd->pp = frag_page->pp;
+		bd->len = cqe_bcnt;
+		bd->off = wi->offset + rx_headroom;
+		bd->page_idx = frag_page->page_idx;
+		frag_page->frags++;
+		rq->stats->packets++;
+		rq->stats->bytes += cqe_bcnt;
+		pr_debug("mlx5_nod: spsc produce q=%d len=%u\n",
+			 rq->ix, cqe_bcnt);
+
+		return NULL;
+	}
+
 	va             = netmem_address(frag_page->netmem) + wi->offset;
 	data           = va + rx_headroom;
 	frag_size      = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt);
@@ -2101,8 +2179,10 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
 {
 	struct mlx5e_frag_page *frag_page = &wi->alloc_units.frag_pages[page_idx];
 	u16 rx_headroom = rq->buff.headroom;
+	struct knod_work_priv *wpriv;
 	struct bpf_prog *prog;
 	struct sk_buff *skb;
+	struct spsc_bd *bd;
 	u32 metasize = 0;
 	void *va, *data;
 	dma_addr_t addr;
@@ -2114,6 +2194,29 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
 		return NULL;
 	}
 
+	if (likely(rq->knodev)) {
+		wpriv = &rq->knodev->wpriv[rq->ix];
+		if (unlikely(mlx5e_knod_spsc_produce_defer(rq, wpriv, &bd))) {
+			mlx5e_knod_spsc_flush(rq);
+			mlx5e_rx_offload_act_handler(rq, false, INT_MAX);
+			if (mlx5e_knod_spsc_produce_defer(rq, wpriv, &bd)) {
+				rq->stats->buff_alloc_err++;
+				return NULL;
+			}
+		}
+
+		bd->netmem = frag_page->netmem;
+		bd->pp = frag_page->pp;
+		bd->len = cqe_bcnt;
+		bd->off = head_offset + rx_headroom;
+		bd->page_idx = frag_page->page_idx;
+		frag_page->frags++;
+		rq->stats->packets++;
+		rq->stats->bytes += cqe_bcnt;
+
+		return NULL;
+	}
+
 	va             = netmem_address(frag_page->netmem) + head_offset;
 	data           = va + rx_headroom;
 	frag_size      = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
index 185105606469..10c2ffd93dab 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
@@ -177,6 +177,51 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
 	if (likely(budget - work_done))
 		work_done += mlx5e_poll_rx_cq(&rq->cq, budget - work_done);
 
+	if (likely(rq->knodev))
+		mlx5e_knod_spsc_flush(rq);
+
+	/* KNOD release can process thousands of completed verdicts and enqueue
+	 * XDP_TX MPWQEs. Refill RX WQEs first so the NIC is not left waiting
+	 * for descriptors while the release side drains. The normal post below
+	 * stays in place to publish pages recycled by this release pass.
+	 */
+	if (likely(rq->knodev))
+		busy |= INDIRECT_CALL_2(rq->post_wqes,
+					mlx5e_post_rx_mpwqes,
+					mlx5e_post_rx_wqes,
+					rq);
+
+	/* Drain SPSC bd ring + IPsec desc_ring unconditionally.
+	 * napi_schedule from the GPU finish_worker may wake us with
+	 * zero new CQEs, so act_handler (called per-CQE inside
+	 * poll_rx_cq) won't run.  Without this top-level call,
+	 * PASS/DROP-stamped bds are never recycled after traffic stops
+	 * and the SPSC ring fills up.
+	 */
+	if (likely(rq->knodev)) {
+		struct knod_work_priv *wpriv = &rq->knodev->wpriv[rq->ix];
+		struct napi_struct *napi;
+
+		work_done += mlx5e_rx_offload_act_handler(rq, true,
+							  budget - work_done);
+
+		/* KNOD direct XDP_TX keeps the RX netmem owned by the TX SQ
+		 * until the NIC reports TX completion.  The normal NAPI order
+		 * polls the XDP SQ before RX CQ processing, then the KNOD
+		 * release pass can enqueue and doorbell a large burst of
+		 * MPWQEs.  Poll once more here so completions that arrived
+		 * during RX/release processing are visible before the final
+		 * RX repost below.
+		 */
+		if (rq->xdpsq)
+			busy |= mlx5e_poll_xdpsq_cq(&rq->xdpsq->cq);
+
+		napi = READ_ONCE(wpriv->napi);
+		if (napi)
+			knod_dev_xdp_drain_pass(rq->knodev, napi, rq->ix,
+						budget);
+	}
+
 	busy |= work_done == budget;
 
 	mlx5e_poll_ico_cq(&c->icosq.cq);
-- 
2.43.0


  parent reply	other threads:[~2026-07-19 18:01 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-19 17:58 [RFC PATCH net-next 00/13] net: knod: in-kernel network offload device Taehee Yoo
2026-07-19 17:58 ` [RFC PATCH net-next 01/13] net: knod: add uapi and core headers Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 02/13] net: devmem: extend memory provider for knod Taehee Yoo
2026-07-20 19:43   ` Mina Almasry
2026-07-21 16:15     ` Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 03/13] net: core: add XDP_MODE_HW offload hook " Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 04/13] net: knod: add offload device core and control plane Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 05/13] bpf: offload: allow PERCPU_ARRAY maps for offloaded programs Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 06/13] drm/amdkfd: prepare kfd core for the knod provider Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 07/13] drm/amdkfd: add knod provider core Taehee Yoo
2026-07-21  7:17   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 08/13] drm/amdkfd: add GPU instruction emitter and disassembler Taehee Yoo
2026-07-20 20:05   ` Natalie Vock
2026-07-20 20:53     ` Andrew Lunn
2026-07-21 16:36       ` Hoyeon Lee
2026-07-19 17:58 ` [RFC PATCH net-next 09/13] drm/amdkfd: add BPF-to-GPU JIT offload Taehee Yoo
2026-07-19 17:58 ` Taehee Yoo [this message]
2026-07-21  7:17   ` [RFC PATCH net-next 10/13] net/mlx5e: add knod XDP offload support sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 11/13] bnxt_en: " Taehee Yoo
2026-07-21  7:18   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 12/13] selftests: drivers/net: add knod tests Taehee Yoo
2026-07-21  7:18   ` sashiko-bot
2026-07-19 17:58 ` [RFC PATCH net-next 13/13] drm/amdkfd: add IPsec full-packet offload Taehee Yoo
2026-07-21  7:18   ` sashiko-bot
2026-07-20 19:18 ` [RFC PATCH net-next 00/13] net: knod: in-kernel network offload device Mina Almasry
2026-07-21 15:17   ` Taehee Yoo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260719175857.4071636-11-ap420073@gmail.com \
    --to=ap420073@gmail.com \
    --cc=Felix.Kuehling@amd.com \
    --cc=airlied@gmail.com \
    --cc=alexander.deucher@amd.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=christian.koenig@amd.com \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=donald.hunter@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=eddyz87@gmail.com \
    --cc=edumazet@google.com \
    --cc=emil@etsalapatis.com \
    --cc=hawk@kernel.org \
    --cc=horms@kernel.org \
    --cc=hoyeon.rhee@gmail.com \
    --cc=ilias.apalodimas@linaro.org \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=justinstitt@google.com \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=leon@kernel.org \
    --cc=linaro-mm-sig@lists.linaro.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=martin.lau@linux.dev \
    --cc=mbloch@nvidia.com \
    --cc=memxor@gmail.com \
    --cc=michael.chan@broadcom.com \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pavan.chebbi@broadcom.com \
    --cc=saeedm@nvidia.com \
    --cc=sdf@fomichev.me \
    --cc=shuah@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=song@kernel.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tariqt@nvidia.com \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.