public inbox for dev@dpdk.org
 help / color / mirror / Atom feed
* [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames
@ 2026-03-12  7:34 Sriram Yagnaraman
  2026-03-12 16:06 ` Stephen Hemminger
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Sriram Yagnaraman @ 2026-03-12  7:34 UTC (permalink / raw)
  To: dev; +Cc: xavier.guillaume, stephen, robin.l.lin, Sriram Yagnaraman

Enable jumbo frame reception with default mbuf data room size by
chaining multiple mbufs when packet exceeds single mbuf tailroom.

Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
requested. Packets are dropped if they exceed single mbuf size
and scatter is not enabled, or if mbuf allocation fails during
chaining. Error counter rx_dropped_pkts tracks all drops.

This allows receiving 9KB jumbo frames using standard 2KB mbufs,
chaining ~5 segments per jumbo packet.
---
 doc/guides/rel_notes/release_26_03.rst    |  5 ++
 drivers/net/af_packet/rte_eth_af_packet.c | 76 +++++++++++++++++++----
 2 files changed, 68 insertions(+), 13 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index fdc880687b..ec627e76c9 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -65,6 +65,11 @@ New Features
 
   * Added support for V4000 Krackan2e.
 
+* **Updated AF_PACKET ethernet driver.**
+
+  * Added support for multi-segment mbuf reception to handle jumbo frames
+    with standard mbuf sizes when scatter Rx offload is enabled.
+
 * **Updated CESNET nfb ethernet driver.**
 
   * The timestamp value has been updated to make it usable.
diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
index e132dc387b..5d43306c09 100644
--- a/drivers/net/af_packet/rte_eth_af_packet.c
+++ b/drivers/net/af_packet/rte_eth_af_packet.c
@@ -56,6 +56,7 @@ struct __rte_cache_aligned pkt_rx_queue {
 	uint16_t in_port;
 	uint8_t vlan_strip;
 	uint8_t timestamp_offloading;
+	uint8_t scatter_enabled;
 
 	volatile unsigned long rx_pkts;
 	volatile unsigned long rx_bytes;
@@ -125,12 +126,13 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 {
 	unsigned i;
 	struct tpacket2_hdr *ppd;
-	struct rte_mbuf *mbuf;
+	struct rte_mbuf *mbuf, *seg, *prev;
 	uint8_t *pbuf;
 	struct pkt_rx_queue *pkt_q = queue;
 	uint16_t num_rx = 0;
 	unsigned long num_rx_bytes = 0;
 	unsigned int framecount, framenum;
+	uint16_t pkt_len, data_len, remaining;
 
 	if (unlikely(nb_pkts == 0))
 		return 0;
@@ -154,8 +156,11 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			break;
 		}
 
-		/* drop packets that won't fit in the mbuf */
-		if (ppd->tp_snaplen > rte_pktmbuf_tailroom(mbuf)) {
+		pkt_len = ppd->tp_snaplen;
+		pbuf = (uint8_t *) ppd + ppd->tp_mac;
+
+		/* drop packets that won't fit in single mbuf if scatter not enabled */
+		if (!pkt_q->scatter_enabled && pkt_len > rte_pktmbuf_tailroom(mbuf)) {
 			rte_pktmbuf_free(mbuf);
 			ppd->tp_status = TP_STATUS_KERNEL;
 			if (++framenum >= framecount)
@@ -164,10 +169,57 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			continue;
 		}
 
-		/* packet will fit in the mbuf, go ahead and receive it */
-		rte_pktmbuf_pkt_len(mbuf) = rte_pktmbuf_data_len(mbuf) = ppd->tp_snaplen;
-		pbuf = (uint8_t *) ppd + ppd->tp_mac;
-		memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, rte_pktmbuf_data_len(mbuf));
+		/* copy first segment */
+		data_len = RTE_MIN(pkt_len, rte_pktmbuf_tailroom(mbuf));
+		memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, data_len);
+		rte_pktmbuf_data_len(mbuf) = data_len;
+		mbuf->nb_segs = 1;
+		remaining = pkt_len - data_len;
+		pbuf += data_len;
+		prev = mbuf;
+
+		/* chain additional segments if needed */
+		while (remaining > 0) {
+			seg = rte_pktmbuf_alloc(pkt_q->mb_pool);
+			if (unlikely(seg == NULL)) {
+				rte_pktmbuf_free(mbuf);
+				ppd->tp_status = TP_STATUS_KERNEL;
+				if (++framenum >= framecount)
+					framenum = 0;
+				pkt_q->rx_dropped_pkts++;
+				continue;
+			}
+
+			/* Remove headroom to maximize data space in chained segments */
+			rte_pktmbuf_prepend(seg, rte_pktmbuf_headroom(seg));
+
+			data_len = RTE_MIN(remaining, rte_pktmbuf_tailroom(seg));
+			if (unlikely(data_len == 0)) {
+				rte_pktmbuf_free(seg);
+				rte_pktmbuf_free(mbuf);
+				ppd->tp_status = TP_STATUS_KERNEL;
+				if (++framenum >= framecount)
+					framenum = 0;
+				pkt_q->rx_dropped_pkts++;
+				continue;
+			}
+
+			memcpy(rte_pktmbuf_mtod(seg, void *), pbuf, data_len);
+			rte_pktmbuf_data_len(seg) = data_len;
+			pbuf += data_len;
+			remaining -= data_len;
+
+			prev->next = seg;
+			prev = seg;
+			mbuf->nb_segs++;
+		}
+
+		/* release incoming frame and advance ring buffer */
+		ppd->tp_status = TP_STATUS_KERNEL;
+		if (++framenum >= framecount)
+			framenum = 0;
+
+		rte_pktmbuf_pkt_len(mbuf) = pkt_len;
 
 		/* check for vlan info */
 		if (ppd->tp_status & TP_STATUS_VLAN_VALID) {
@@ -188,14 +240,10 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			mbuf->ol_flags |= timestamp_dynflag;
 		}
 
-		/* release incoming frame and advance ring buffer */
-		ppd->tp_status = TP_STATUS_KERNEL;
-		if (++framenum >= framecount)
-			framenum = 0;
 		mbuf->port = pkt_q->in_port;
 
 		/* account for the receive frame */
-		bufs[i] = mbuf;
+		bufs[num_rx] = mbuf;
 		num_rx++;
 		num_rx_bytes += mbuf->pkt_len;
 	}
@@ -412,7 +460,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
 		RTE_ETH_TX_OFFLOAD_VLAN_INSERT;
 	dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_VLAN_STRIP |
-		RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+		RTE_ETH_RX_OFFLOAD_TIMESTAMP |
+		RTE_ETH_RX_OFFLOAD_SCATTER;
 
 	return 0;
 }
@@ -599,6 +648,7 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
 	pkt_q->in_port = dev->data->port_id;
 	pkt_q->vlan_strip = internals->vlan_strip;
 	pkt_q->timestamp_offloading = internals->timestamp_offloading;
+	pkt_q->scatter_enabled = !!(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER);
 
 	return 0;
 }
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames
  2026-03-12  7:34 [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames Sriram Yagnaraman
@ 2026-03-12 16:06 ` Stephen Hemminger
  2026-03-12 16:34 ` Stephen Hemminger
  2026-03-13  9:10 ` [PATCH v4] " Sriram Yagnaraman
  2 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-03-12 16:06 UTC (permalink / raw)
  To: Sriram Yagnaraman; +Cc: dev, xavier.guillaume, robin.l.lin

On Thu, 12 Mar 2026 08:34:03 +0100
Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> wrote:

> Enable jumbo frame reception with default mbuf data room size by
> chaining multiple mbufs when packet exceeds single mbuf tailroom.
> 
> Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
> requested. Packets are dropped if they exceed single mbuf size
> and scatter is not enabled, or if mbuf allocation fails during
> chaining. Error counter rx_dropped_pkts tracks all drops.
> 
> This allows receiving 9KB jumbo frames using standard 2KB mbufs,
> chaining ~5 segments per jumbo packet.
> ---

Scatter is good, but additional checks are necessary to validate
mbuf pool and buffer constraints. See virtio configure for example.

bool
virtio_rx_check_scatter(uint16_t max_rx_pkt_len, uint16_t rx_buf_size,
			bool rx_scatter_enabled, const char **error)
{
	if (!rx_scatter_enabled && max_rx_pkt_len > rx_buf_size) {
		*error = "Rx scatter is disabled and RxQ mbuf pool object size is too small";
		return false;
	}

	return true;
}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames
  2026-03-12  7:34 [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames Sriram Yagnaraman
  2026-03-12 16:06 ` Stephen Hemminger
@ 2026-03-12 16:34 ` Stephen Hemminger
  2026-03-13  9:10 ` [PATCH v4] " Sriram Yagnaraman
  2 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-03-12 16:34 UTC (permalink / raw)
  To: Sriram Yagnaraman; +Cc: dev, xavier.guillaume, robin.l.lin

On Thu, 12 Mar 2026 08:34:03 +0100
Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> wrote:

> Enable jumbo frame reception with default mbuf data room size by
> chaining multiple mbufs when packet exceeds single mbuf tailroom.
> 
> Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
> requested. Packets are dropped if they exceed single mbuf size
> and scatter is not enabled, or if mbuf allocation fails during
> chaining. Error counter rx_dropped_pkts tracks all drops.
> 
> This allows receiving 9KB jumbo frames using standard 2KB mbufs,
> chaining ~5 segments per jumbo packet.
> ---

Project does not accept patches without DCO (ie. Signed-off-by)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v4] net/af_packet: add multi-segment mbuf support for jumbo frames
  2026-03-12  7:34 [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames Sriram Yagnaraman
  2026-03-12 16:06 ` Stephen Hemminger
  2026-03-12 16:34 ` Stephen Hemminger
@ 2026-03-13  9:10 ` Sriram Yagnaraman
  2026-03-18  9:47   ` [PATCH v5] " Sriram Yagnaraman
  2 siblings, 1 reply; 9+ messages in thread
From: Sriram Yagnaraman @ 2026-03-13  9:10 UTC (permalink / raw)
  To: dev; +Cc: stephen, xavier.guillaume, robin.l.lin, Sriram Yagnaraman

Enable jumbo frame reception with default mbuf data room size by
chaining multiple mbufs when packet exceeds single mbuf tailroom.

Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
requested. Packets are dropped if they exceed single mbuf size
and scatter is not enabled, or if mbuf allocation fails during
chaining. Error counter rx_dropped_pkts tracks all drops.

This allows receiving 9KB jumbo frames using standard 2KB mbufs,
chaining ~5 segments per jumbo packet.

Signed-off-by: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>
---
 doc/guides/rel_notes/release_26_03.rst    |  5 ++
 drivers/net/af_packet/rte_eth_af_packet.c | 79 +++++++++++++++++------
 2 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 9649185133..1048a6b595 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -65,6 +65,11 @@ New Features
 
   * Added support for V4000 Krackan2e.
 
+* **Updated AF_PACKET ethernet driver.**
+
+  * Added support for multi-segment mbuf reception to handle jumbo frames
+    with standard mbuf sizes when scatter Rx offload is enabled.
+
 * **Updated CESNET nfb ethernet driver.**
 
   * The timestamp value has been updated to make it usable.
diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
index e132dc387b..13daa02fe0 100644
--- a/drivers/net/af_packet/rte_eth_af_packet.c
+++ b/drivers/net/af_packet/rte_eth_af_packet.c
@@ -56,6 +56,7 @@ struct __rte_cache_aligned pkt_rx_queue {
 	uint16_t in_port;
 	uint8_t vlan_strip;
 	uint8_t timestamp_offloading;
+	uint8_t scatter_enabled;
 
 	volatile unsigned long rx_pkts;
 	volatile unsigned long rx_bytes;
@@ -125,12 +126,13 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 {
 	unsigned i;
 	struct tpacket2_hdr *ppd;
-	struct rte_mbuf *mbuf;
+	struct rte_mbuf *mbuf, *seg, *prev;
 	uint8_t *pbuf;
 	struct pkt_rx_queue *pkt_q = queue;
 	uint16_t num_rx = 0;
 	unsigned long num_rx_bytes = 0;
 	unsigned int framecount, framenum;
+	uint16_t pkt_len, data_len, remaining;
 
 	if (unlikely(nb_pkts == 0))
 		return 0;
@@ -154,20 +156,49 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			break;
 		}
 
-		/* drop packets that won't fit in the mbuf */
-		if (ppd->tp_snaplen > rte_pktmbuf_tailroom(mbuf)) {
+		pkt_len = ppd->tp_snaplen;
+		pbuf = (uint8_t *)ppd + ppd->tp_mac;
+
+		/* drop packets that won't fit in single mbuf if scatter not enabled */
+		if (!pkt_q->scatter_enabled && pkt_len > rte_pktmbuf_tailroom(mbuf)) {
 			rte_pktmbuf_free(mbuf);
-			ppd->tp_status = TP_STATUS_KERNEL;
-			if (++framenum >= framecount)
-				framenum = 0;
 			pkt_q->rx_dropped_pkts++;
-			continue;
+			goto release_frame;
+		}
+
+		/* copy first segment */
+		data_len = RTE_MIN(pkt_len, rte_pktmbuf_tailroom(mbuf));
+		memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, data_len);
+		rte_pktmbuf_data_len(mbuf) = data_len;
+		mbuf->nb_segs = 1;
+		remaining = pkt_len - data_len;
+		pbuf += data_len;
+		prev = mbuf;
+
+		/* chain additional segments if needed */
+		while (remaining > 0) {
+			seg = rte_pktmbuf_alloc(pkt_q->mb_pool);
+			if (unlikely(seg == NULL)) {
+				rte_pktmbuf_free(mbuf);
+				pkt_q->rx_dropped_pkts++;
+				goto release_frame;
+			}
+
+			/* Remove headroom to maximize data space in chained segments */
+			rte_pktmbuf_prepend(seg, rte_pktmbuf_headroom(seg));
+
+			data_len = RTE_MIN(remaining, rte_pktmbuf_tailroom(seg));
+			memcpy(rte_pktmbuf_mtod(seg, void *), pbuf, data_len);
+			rte_pktmbuf_data_len(seg) = data_len;
+			pbuf += data_len;
+			remaining -= data_len;
+
+			prev->next = seg;
+			prev = seg;
+			mbuf->nb_segs++;
 		}
 
-		/* packet will fit in the mbuf, go ahead and receive it */
-		rte_pktmbuf_pkt_len(mbuf) = rte_pktmbuf_data_len(mbuf) = ppd->tp_snaplen;
-		pbuf = (uint8_t *) ppd + ppd->tp_mac;
-		memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, rte_pktmbuf_data_len(mbuf));
+		rte_pktmbuf_pkt_len(mbuf) = pkt_len;
 
 		/* check for vlan info */
 		if (ppd->tp_status & TP_STATUS_VLAN_VALID) {
@@ -188,16 +219,18 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			mbuf->ol_flags |= timestamp_dynflag;
 		}
 
-		/* release incoming frame and advance ring buffer */
-		ppd->tp_status = TP_STATUS_KERNEL;
-		if (++framenum >= framecount)
-			framenum = 0;
 		mbuf->port = pkt_q->in_port;
 
 		/* account for the receive frame */
-		bufs[i] = mbuf;
+		bufs[num_rx] = mbuf;
 		num_rx++;
 		num_rx_bytes += mbuf->pkt_len;
+
+release_frame:
+		/* release incoming frame and advance ring buffer */
+		ppd->tp_status = TP_STATUS_KERNEL;
+		if (++framenum >= framecount)
+			framenum = 0;
 	}
 	pkt_q->framenum = framenum;
 	pkt_q->rx_pkts += num_rx;
@@ -412,7 +445,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
 		RTE_ETH_TX_OFFLOAD_VLAN_INSERT;
 	dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_VLAN_STRIP |
-		RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+		RTE_ETH_RX_OFFLOAD_TIMESTAMP |
+		RTE_ETH_RX_OFFLOAD_SCATTER;
 
 	return 0;
 }
@@ -579,26 +613,29 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
 	struct pmd_internals *internals = dev->data->dev_private;
 	struct pkt_rx_queue *pkt_q = &internals->rx_queue[rx_queue_id];
 	unsigned int buf_size, data_size;
+	bool scatter_enabled;
 
 	pkt_q->mb_pool = mb_pool;
 
-	/* Now get the space available for data in the mbuf */
 	buf_size = rte_pktmbuf_data_room_size(pkt_q->mb_pool) -
 		RTE_PKTMBUF_HEADROOM;
 	data_size = internals->req.tp_frame_size;
 	data_size -= TPACKET2_HDRLEN - sizeof(struct sockaddr_ll);
 
-	if (data_size > buf_size) {
+	scatter_enabled = !!(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER);
+
+	if (!scatter_enabled && data_size > buf_size) {
 		PMD_LOG(ERR,
-			"%s: %d bytes will not fit in mbuf (%d bytes)",
+			"%s: %d bytes will not fit in mbuf (%d bytes), enable scatter offload",
 			dev->device->name, data_size, buf_size);
-		return -ENOMEM;
+		return -EINVAL;
 	}
 
 	dev->data->rx_queues[rx_queue_id] = pkt_q;
 	pkt_q->in_port = dev->data->port_id;
 	pkt_q->vlan_strip = internals->vlan_strip;
 	pkt_q->timestamp_offloading = internals->timestamp_offloading;
+	pkt_q->scatter_enabled = scatter_enabled;
 
 	return 0;
 }
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5] net/af_packet: add multi-segment mbuf support for jumbo frames
  2026-03-13  9:10 ` [PATCH v4] " Sriram Yagnaraman
@ 2026-03-18  9:47   ` Sriram Yagnaraman
  2026-03-23 23:42     ` Stephen Hemminger
  2026-03-24 16:29     ` [PATCH v6] net/af_packet: add Rx scatter " Sriram Yagnaraman
  0 siblings, 2 replies; 9+ messages in thread
From: Sriram Yagnaraman @ 2026-03-18  9:47 UTC (permalink / raw)
  To: dev; +Cc: stephen, xavier.guillaume, robin.l.lin, Sriram Yagnaraman

Enable jumbo frame reception with default mbuf data room size by
chaining multiple mbufs when packet exceeds single mbuf tailroom.

Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
requested. Packets are dropped if they exceed single mbuf size
and scatter is not enabled, or if mbuf allocation fails during
chaining. Error counter rx_dropped_pkts tracks all drops.

This allows receiving 9KB jumbo frames using standard 2KB mbufs,
chaining ~5 segments per jumbo packet.

Signed-off-by: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>
---
v5:
* Rebased on main (26.03-rc2) now that Xavier's jumbo frame series is merged
* Removed Depends-on (dependency is now in main)
* Extracted scatter copy into eth_af_packet_rx_scatter() helper (matches pcap pattern)

v4:
* Added scatter validation in rx_queue_setup to reject invalid configurations
* Fixed control flow: use goto instead of continue in scatter loop
* Moved frame release to common label to prevent use-after-free
* Ensured VLAN/timestamp extraction happens before releasing frame to kernel

v3:
* Added conditional scatter based on RTE_ETH_RX_OFFLOAD_SCATTER configuration
* Removed headroom from chained segments to maximize data capacity
* Fixed infinite loop risk by removing zero tailroom check (trust mempool config)

v2:
* Fixed goto label to properly exit outer loop on allocation failure
* Changed bufs indexing from [i] to [num_rx] for correct packet placement
---
 doc/guides/rel_notes/release_26_03.rst    |  5 ++
 drivers/net/af_packet/rte_eth_af_packet.c | 97 ++++++++++++++++++-----
 2 files changed, 82 insertions(+), 20 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 3d2ed19eb8..54e3bdbcc7 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -65,6 +65,11 @@ New Features
 
   * Added support for V4000 Krackan2e.
 
+* **Updated AF_PACKET ethernet driver.**
+
+  * Added support for multi-segment mbuf reception to handle jumbo frames
+    with standard mbuf sizes when scatter Rx offload is enabled.
+
 * **Updated CESNET nfb ethernet driver.**
 
   * The timestamp value has been updated to make it usable.
diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
index e132dc387b..7db3843610 100644
--- a/drivers/net/af_packet/rte_eth_af_packet.c
+++ b/drivers/net/af_packet/rte_eth_af_packet.c
@@ -56,6 +56,7 @@ struct __rte_cache_aligned pkt_rx_queue {
 	uint16_t in_port;
 	uint8_t vlan_strip;
 	uint8_t timestamp_offloading;
+	uint8_t scatter_enabled;
 
 	volatile unsigned long rx_pkts;
 	volatile unsigned long rx_bytes;
@@ -120,6 +121,46 @@ RTE_LOG_REGISTER_DEFAULT(af_packet_logtype, NOTICE);
 	RTE_LOG_LINE(level, AFPACKET, "%s(): " fmt ":%s", __func__, \
 		## __VA_ARGS__, strerror(errno))
 
+/*
+ * Copy packet data into chained mbufs when it exceeds single mbuf tailroom.
+ * Returns 0 on success, -1 on mbuf allocation failure.
+ */
+static int
+eth_af_packet_rx_scatter(struct rte_mempool *mb_pool, struct rte_mbuf *mbuf,
+		const uint8_t *data, uint16_t data_len)
+{
+	uint16_t len = rte_pktmbuf_tailroom(mbuf);
+	struct rte_mbuf *m = mbuf;
+
+	memcpy(rte_pktmbuf_mtod(mbuf, void *), data, len);
+	rte_pktmbuf_data_len(mbuf) = len;
+	data_len -= len;
+	data += len;
+
+	while (data_len > 0) {
+		m->next = rte_pktmbuf_alloc(mb_pool);
+		if (unlikely(m->next == NULL))
+			return -1;
+
+		m = m->next;
+
+		/* Headroom is not needed in chained mbufs */
+		rte_pktmbuf_prepend(m, rte_pktmbuf_headroom(m));
+		m->pkt_len = 0;
+		m->data_len = 0;
+
+		len = RTE_MIN(rte_pktmbuf_tailroom(m), data_len);
+		memcpy(rte_pktmbuf_mtod(m, void *), data, len);
+		rte_pktmbuf_data_len(m) = len;
+
+		mbuf->nb_segs++;
+		data_len -= len;
+		data += len;
+	}
+
+	return 0;
+}
+
 static uint16_t
 eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 {
@@ -131,6 +172,7 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 	uint16_t num_rx = 0;
 	unsigned long num_rx_bytes = 0;
 	unsigned int framecount, framenum;
+	uint16_t pkt_len;
 
 	if (unlikely(nb_pkts == 0))
 		return 0;
@@ -154,20 +196,29 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			break;
 		}
 
-		/* drop packets that won't fit in the mbuf */
-		if (ppd->tp_snaplen > rte_pktmbuf_tailroom(mbuf)) {
+		pkt_len = ppd->tp_snaplen;
+		pbuf = (uint8_t *)ppd + ppd->tp_mac;
+
+		if (pkt_len <= rte_pktmbuf_tailroom(mbuf)) {
+			/* packet fits in a single mbuf */
+			memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, pkt_len);
+			rte_pktmbuf_data_len(mbuf) = pkt_len;
+		} else if (pkt_q->scatter_enabled) {
+			/* scatter into chained mbufs */
+			if (unlikely(eth_af_packet_rx_scatter(pkt_q->mb_pool,
+					mbuf, pbuf, pkt_len) < 0)) {
+				rte_pktmbuf_free(mbuf);
+				pkt_q->rx_dropped_pkts++;
+				goto release_frame;
+			}
+		} else {
+			/* oversized and no scatter - drop */
 			rte_pktmbuf_free(mbuf);
-			ppd->tp_status = TP_STATUS_KERNEL;
-			if (++framenum >= framecount)
-				framenum = 0;
 			pkt_q->rx_dropped_pkts++;
-			continue;
+			goto release_frame;
 		}
 
-		/* packet will fit in the mbuf, go ahead and receive it */
-		rte_pktmbuf_pkt_len(mbuf) = rte_pktmbuf_data_len(mbuf) = ppd->tp_snaplen;
-		pbuf = (uint8_t *) ppd + ppd->tp_mac;
-		memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, rte_pktmbuf_data_len(mbuf));
+		rte_pktmbuf_pkt_len(mbuf) = pkt_len;
 
 		/* check for vlan info */
 		if (ppd->tp_status & TP_STATUS_VLAN_VALID) {
@@ -188,16 +239,18 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			mbuf->ol_flags |= timestamp_dynflag;
 		}
 
-		/* release incoming frame and advance ring buffer */
-		ppd->tp_status = TP_STATUS_KERNEL;
-		if (++framenum >= framecount)
-			framenum = 0;
 		mbuf->port = pkt_q->in_port;
 
 		/* account for the receive frame */
-		bufs[i] = mbuf;
+		bufs[num_rx] = mbuf;
 		num_rx++;
 		num_rx_bytes += mbuf->pkt_len;
+
+release_frame:
+		/* release incoming frame and advance ring buffer */
+		ppd->tp_status = TP_STATUS_KERNEL;
+		if (++framenum >= framecount)
+			framenum = 0;
 	}
 	pkt_q->framenum = framenum;
 	pkt_q->rx_pkts += num_rx;
@@ -412,7 +465,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
 		RTE_ETH_TX_OFFLOAD_VLAN_INSERT;
 	dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_VLAN_STRIP |
-		RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+		RTE_ETH_RX_OFFLOAD_TIMESTAMP |
+		RTE_ETH_RX_OFFLOAD_SCATTER;
 
 	return 0;
 }
@@ -579,26 +633,29 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
 	struct pmd_internals *internals = dev->data->dev_private;
 	struct pkt_rx_queue *pkt_q = &internals->rx_queue[rx_queue_id];
 	unsigned int buf_size, data_size;
+	bool scatter_enabled;
 
 	pkt_q->mb_pool = mb_pool;
 
-	/* Now get the space available for data in the mbuf */
 	buf_size = rte_pktmbuf_data_room_size(pkt_q->mb_pool) -
 		RTE_PKTMBUF_HEADROOM;
 	data_size = internals->req.tp_frame_size;
 	data_size -= TPACKET2_HDRLEN - sizeof(struct sockaddr_ll);
 
-	if (data_size > buf_size) {
+	scatter_enabled = !!(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER);
+
+	if (!scatter_enabled && data_size > buf_size) {
 		PMD_LOG(ERR,
-			"%s: %d bytes will not fit in mbuf (%d bytes)",
+			"%s: %d bytes will not fit in mbuf (%d bytes), enable scatter offload",
 			dev->device->name, data_size, buf_size);
-		return -ENOMEM;
+		return -EINVAL;
 	}
 
 	dev->data->rx_queues[rx_queue_id] = pkt_q;
 	pkt_q->in_port = dev->data->port_id;
 	pkt_q->vlan_strip = internals->vlan_strip;
 	pkt_q->timestamp_offloading = internals->timestamp_offloading;
+	pkt_q->scatter_enabled = scatter_enabled;
 
 	return 0;
 }
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v5] net/af_packet: add multi-segment mbuf support for jumbo frames
  2026-03-18  9:47   ` [PATCH v5] " Sriram Yagnaraman
@ 2026-03-23 23:42     ` Stephen Hemminger
  2026-03-24 16:29     ` [PATCH v6] net/af_packet: add Rx scatter " Sriram Yagnaraman
  1 sibling, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-03-23 23:42 UTC (permalink / raw)
  To: Sriram Yagnaraman; +Cc: dev, xavier.guillaume, robin.l.lin

On Wed, 18 Mar 2026 10:47:26 +0100
Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> wrote:

> Enable jumbo frame reception with default mbuf data room size by
> chaining multiple mbufs when packet exceeds single mbuf tailroom.
> 
> Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
> requested. Packets are dropped if they exceed single mbuf size
> and scatter is not enabled, or if mbuf allocation fails during
> chaining. Error counter rx_dropped_pkts tracks all drops.
> 
> This allows receiving 9KB jumbo frames using standard 2KB mbufs,
> chaining ~5 segments per jumbo packet.
> 
> Signed-off-by: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>
> ---

In general looks good, but AI review found some things.

Summary:

Thanks for the v5, Sriram. A few comments below.

1) pkt_len is uint16_t but tp_snaplen is __u32

The scatter helper also takes uint16_t data_len, so anything above 64KB silently truncates. For jumbo frames this is fine in practice, but the type mismatch is a latent bug. Consider uint32_t for both, or at least a comment explaining why uint16_t is sufficient.

2) Scatter alloc failure should bump rx_nombuf, not just rx_dropped_pkts

When rte_pktmbuf_alloc() fails for a chained segment inside eth_af_packet_rx_scatter(), only rx_dropped_pkts is incremented. The head mbuf alloc failure correctly bumps rx_nombuf. The scatter case is also a no-mbuf condition and should be reported as such, otherwise operators can't distinguish memory pressure from oversized-packet drops in the stats.

3) Reclaiming headroom in chained segments

The prepend/zero-data_len/zero-pkt_len sequence works but is roundabout. Setting m->data_off = 0 directly would be cleaner and avoid the intermediate state where data_len is non-zero then immediately overwritten.

4) bufs[num_rx] fix looks correct

The change from bufs[i] to bufs[num_rx] fixes a pre-existing gap in the output array when packets are dropped via continue. Good catch.

Overall the structure looks solid — the scatter helper extraction is clean, the goto-based frame release avoids the earlier use-after-free risk, and the queue setup validation is correct.


Full detail:

## Review: [PATCH v5] net/af_packet: add multi-segment mbuf support for jumbo frames

### Error: `eth_af_packet_rx_scatter()` leaks chained mbufs on allocation failure

When `rte_pktmbuf_alloc()` fails inside the `while` loop, the function returns `-1`. The caller then calls `rte_pktmbuf_free(mbuf)` on the head mbuf. However, before the failure, previous iterations of the loop have already linked allocated segments via `m->next`. The question is whether `rte_pktmbuf_free()` walks the chain — it does, so segments linked before the failure *will* be freed. But there's a subtlety: `mbuf->nb_segs` is incremented for each successfully chained segment, and `rte_pktmbuf_free()` uses `nb_segs` to walk the chain. Since `nb_segs` and the `->next` linkage are kept in sync, this is actually correct.

On closer inspection: **no leak here.** Disregard — the caller's `rte_pktmbuf_free(mbuf)` correctly frees the entire chain. (Keeping this analysis transparent so you can verify the reasoning.)

### Error: `pkt_len` declared as `uint16_t` but `tp_snaplen` is `uint32_t` — silent truncation

`ppd->tp_snaplen` is `__u32` in the kernel's `tpacket2_hdr`. The patch declares `pkt_len` as `uint16_t`:

```c
uint16_t pkt_len;
...
pkt_len = ppd->tp_snaplen;
```

For jumbo frames up to 9KB this works, but `tp_snaplen` can be up to 65535 (or larger with cooked sockets). If `tp_snaplen > 65535` this silently truncates, and even values in the 32768–65535 range could cause issues if `pkt_len` is used in signed comparisons. More practically, the scatter helper also takes `uint16_t data_len`, capping the maximum receivable packet at 64KB.

Since this patch specifically targets jumbo frames (9KB), the practical risk is low, but the type should match the source. Consider using `uint32_t` for `pkt_len` and the `data_len` parameter of `eth_af_packet_rx_scatter()`, or at minimum add a bounds check / comment explaining why `uint16_t` is sufficient.

### Error: `rte_pktmbuf_pkt_len(mbuf)` not set in the single-mbuf fast path before VLAN reinsertion

In the patched code, the single-mbuf path sets `data_len` but defers setting `pkt_len` until after the VLAN/timestamp block:

```c
if (pkt_len <= rte_pktmbuf_tailroom(mbuf)) {
    memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, pkt_len);
    rte_pktmbuf_data_len(mbuf) = pkt_len;
} else if (...) { ... }

rte_pktmbuf_pkt_len(mbuf) = pkt_len;

/* check for vlan info */
if (ppd->tp_status & TP_STATUS_VLAN_VALID) {
    ...
    if (!pkt_q->vlan_strip && rte_vlan_insert(&mbuf))
```

`rte_vlan_insert()` reads `mbuf->pkt_len` to determine packet length. At that point `pkt_len` has been assigned to `rte_pktmbuf_pkt_len(mbuf)` — wait, looking again, `pkt_len` assignment is on the line *before* the VLAN block. Let me re-read the patch flow:

```
        } else { ... goto release_frame; }

        rte_pktmbuf_pkt_len(mbuf) = pkt_len;   // <-- this line

        /* check for vlan info */
```

Yes, `pkt_len` is set before the VLAN check. This is correct. Disregard.

### Warning: `rx_nombuf` not incremented on scatter allocation failure

In the original code, `rx_nombuf` is incremented when `rte_pktmbuf_alloc()` fails for the head mbuf. In the scatter path, when `rte_pktmbuf_alloc()` fails for a *chained* segment, only `rx_dropped_pkts` is incremented. This is arguably a no-mbuf condition and should also increment `rx_nombuf` so that `rte_eth_stats.rx_nombuf` accurately reflects memory pressure. The current code makes it impossible to distinguish "dropped because too large and no scatter" from "dropped because mbuf pool exhausted during scatter."

Suggested fix: increment `pkt_q->rx_nombuf` instead of (or in addition to) `rx_dropped_pkts` when scatter allocation fails.

### Warning: `rte_pktmbuf_prepend()` return value unchecked

In `eth_af_packet_rx_scatter()`:

```c
rte_pktmbuf_prepend(m, rte_pktmbuf_headroom(m));
m->pkt_len = 0;
m->data_len = 0;
```

`rte_pktmbuf_prepend()` can return `NULL` if the headroom requested exceeds available headroom (shouldn't happen for a freshly allocated mbuf, but it's defensive). More importantly, after `rte_pktmbuf_prepend()`, `data_len` is increased by the headroom amount, but then immediately overwritten to `0`. The `pkt_len` is similarly set to `0`. This sequence works but is fragile — a cleaner approach would be to directly manipulate `m->data_off = 0` to reclaim the headroom, which is what this is effectively doing. Consider:

```c
m->data_off = 0;
```

This is simpler, less error-prone, and avoids the intermediate state where `data_len` contains a non-zero value that is immediately discarded.

### Warning: `scatter_enabled` read from `rxmode.offloads` in `rx_queue_setup`

Per AGENTS.md guidance, reading from `dev->data->dev_conf.rxmode` after configure can become stale. In this case, `rx_queue_setup` is called between `dev_configure` and `dev_start`, so the value should be consistent. However, the canonical pattern in DPDK is to check `dev->data->scattered_rx` or the offloads via `dev->data->dev_conf.rxmode.offloads` (which is acceptable in queue setup since it runs in the configure-to-start window). This is borderline acceptable but worth noting — if `mtu_set` is called after queue setup, the scatter state won't be re-evaluated. The AF_PACKET PMD's `mtu_set` doesn't re-select Rx functions or re-evaluate scatter, which is a pre-existing gap not introduced by this patch.

### Info: `bufs[i]` changed to `bufs[num_rx]` — correct fix for existing bug

The original code used `bufs[i]` which would leave gaps in the output array when packets are dropped (via `continue`). The change to `bufs[num_rx]` is correct and actually fixes a pre-existing bug in the drop path. Good catch in this patch.

### Info: Consider adding `rx_nombuf` tracking for the scatter failure case

As noted above, distinguishing "no mbuf" from "oversized drop" in statistics would be valuable for debugging. Minor suggestion for a follow-up.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v6] net/af_packet: add Rx scatter for jumbo frames
  2026-03-18  9:47   ` [PATCH v5] " Sriram Yagnaraman
  2026-03-23 23:42     ` Stephen Hemminger
@ 2026-03-24 16:29     ` Sriram Yagnaraman
  2026-03-25 17:31       ` Stephen Hemminger
  1 sibling, 1 reply; 9+ messages in thread
From: Sriram Yagnaraman @ 2026-03-24 16:29 UTC (permalink / raw)
  To: stephen; +Cc: dev, xavier.guillaume, robin.l.lin, Sriram Yagnaraman

Enable jumbo frame reception with default mbuf data room size by
chaining multiple mbufs when packet exceeds single mbuf tailroom.

Scatter Rx is only enabled when RTE_ETH_RX_OFFLOAD_SCATTER is
requested. Packets exceeding single mbuf size are dropped if
scatter is not enabled. Allocation failures during chaining
increment rx_nombuf. Oversized drops increment rx_dropped_pkts.

Signed-off-by: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>
---
v6:
- Use uint32_t for pkt_len and scatter data_len to match tp_snaplen
  type __u32. (Stephen Hemminger)
- Increment rx_nombuf on scatter alloc failure instead of
  rx_dropped_pkts. (Stephen Hemminger)
- Set m->data_off = 0 directly instead of rte_pktmbuf_prepend
  sequence for chained segments. (Stephen Hemminger)

v5:
- Rebased on main (26.03-rc2), removed Depends-on
- Extracted scatter into eth_af_packet_rx_scatter() helper

v4:
- Added scatter validation in rx_queue_setup
- Fixed control flow: goto instead of continue in scatter loop
- Moved frame release to common label to prevent use-after-free

v3:
- Conditional scatter based on RTE_ETH_RX_OFFLOAD_SCATTER
- Removed headroom from chained segments

v2:
- Fixed goto label to properly exit outer loop on alloc failure
- Changed bufs indexing from [i] to [num_rx]
 doc/guides/rel_notes/release_26_03.rst    |  5 ++
 drivers/net/af_packet/rte_eth_af_packet.c | 95 ++++++++++++++++++-----
 2 files changed, 80 insertions(+), 20 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 3d2ed19eb8..54e3bdbcc7 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -65,6 +65,11 @@ New Features
 
   * Added support for V4000 Krackan2e.
 
+* **Updated AF_PACKET ethernet driver.**
+
+  * Added support for multi-segment mbuf reception to handle jumbo frames
+    with standard mbuf sizes when scatter Rx offload is enabled.
+
 * **Updated CESNET nfb ethernet driver.**
 
   * The timestamp value has been updated to make it usable.
diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
index e132dc387b..0ee94e71ea 100644
--- a/drivers/net/af_packet/rte_eth_af_packet.c
+++ b/drivers/net/af_packet/rte_eth_af_packet.c
@@ -56,6 +56,7 @@ struct __rte_cache_aligned pkt_rx_queue {
 	uint16_t in_port;
 	uint8_t vlan_strip;
 	uint8_t timestamp_offloading;
+	uint8_t scatter_enabled;
 
 	volatile unsigned long rx_pkts;
 	volatile unsigned long rx_bytes;
@@ -120,6 +121,44 @@ RTE_LOG_REGISTER_DEFAULT(af_packet_logtype, NOTICE);
 	RTE_LOG_LINE(level, AFPACKET, "%s(): " fmt ":%s", __func__, \
 		## __VA_ARGS__, strerror(errno))
 
+/*
+ * Copy packet data into chained mbufs when it exceeds single mbuf tailroom.
+ * Returns 0 on success, -1 on mbuf allocation failure.
+ */
+static int
+eth_af_packet_rx_scatter(struct rte_mempool *mb_pool, struct rte_mbuf *mbuf,
+		const uint8_t *data, uint32_t data_len)
+{
+	uint16_t len = rte_pktmbuf_tailroom(mbuf);
+	struct rte_mbuf *m = mbuf;
+
+	memcpy(rte_pktmbuf_mtod(mbuf, void *), data, len);
+	rte_pktmbuf_data_len(mbuf) = len;
+	data_len -= len;
+	data += len;
+
+	while (data_len > 0) {
+		m->next = rte_pktmbuf_alloc(mb_pool);
+		if (unlikely(m->next == NULL))
+			return -1;
+
+		m = m->next;
+
+		/* Headroom is not needed in chained mbufs */
+		m->data_off = 0;
+
+		len = RTE_MIN(rte_pktmbuf_tailroom(m), data_len);
+		memcpy(rte_pktmbuf_mtod(m, void *), data, len);
+		rte_pktmbuf_data_len(m) = len;
+
+		mbuf->nb_segs++;
+		data_len -= len;
+		data += len;
+	}
+
+	return 0;
+}
+
 static uint16_t
 eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 {
@@ -131,6 +170,7 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 	uint16_t num_rx = 0;
 	unsigned long num_rx_bytes = 0;
 	unsigned int framecount, framenum;
+	uint32_t pkt_len;
 
 	if (unlikely(nb_pkts == 0))
 		return 0;
@@ -154,20 +194,29 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			break;
 		}
 
-		/* drop packets that won't fit in the mbuf */
-		if (ppd->tp_snaplen > rte_pktmbuf_tailroom(mbuf)) {
+		pkt_len = ppd->tp_snaplen;
+		pbuf = (uint8_t *)ppd + ppd->tp_mac;
+
+		if (pkt_len <= rte_pktmbuf_tailroom(mbuf)) {
+			/* packet fits in a single mbuf */
+			memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, pkt_len);
+			rte_pktmbuf_data_len(mbuf) = pkt_len;
+		} else if (pkt_q->scatter_enabled) {
+			/* scatter into chained mbufs */
+			if (unlikely(eth_af_packet_rx_scatter(pkt_q->mb_pool,
+					mbuf, pbuf, pkt_len) < 0)) {
+				rte_pktmbuf_free(mbuf);
+				pkt_q->rx_nombuf++;
+				goto release_frame;
+			}
+		} else {
+			/* oversized and no scatter - drop */
 			rte_pktmbuf_free(mbuf);
-			ppd->tp_status = TP_STATUS_KERNEL;
-			if (++framenum >= framecount)
-				framenum = 0;
 			pkt_q->rx_dropped_pkts++;
-			continue;
+			goto release_frame;
 		}
 
-		/* packet will fit in the mbuf, go ahead and receive it */
-		rte_pktmbuf_pkt_len(mbuf) = rte_pktmbuf_data_len(mbuf) = ppd->tp_snaplen;
-		pbuf = (uint8_t *) ppd + ppd->tp_mac;
-		memcpy(rte_pktmbuf_mtod(mbuf, void *), pbuf, rte_pktmbuf_data_len(mbuf));
+		rte_pktmbuf_pkt_len(mbuf) = pkt_len;
 
 		/* check for vlan info */
 		if (ppd->tp_status & TP_STATUS_VLAN_VALID) {
@@ -188,16 +237,18 @@ eth_af_packet_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			mbuf->ol_flags |= timestamp_dynflag;
 		}
 
-		/* release incoming frame and advance ring buffer */
-		ppd->tp_status = TP_STATUS_KERNEL;
-		if (++framenum >= framecount)
-			framenum = 0;
 		mbuf->port = pkt_q->in_port;
 
 		/* account for the receive frame */
-		bufs[i] = mbuf;
+		bufs[num_rx] = mbuf;
 		num_rx++;
 		num_rx_bytes += mbuf->pkt_len;
+
+release_frame:
+		/* release incoming frame and advance ring buffer */
+		ppd->tp_status = TP_STATUS_KERNEL;
+		if (++framenum >= framecount)
+			framenum = 0;
 	}
 	pkt_q->framenum = framenum;
 	pkt_q->rx_pkts += num_rx;
@@ -412,7 +463,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
 		RTE_ETH_TX_OFFLOAD_VLAN_INSERT;
 	dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_VLAN_STRIP |
-		RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+		RTE_ETH_RX_OFFLOAD_TIMESTAMP |
+		RTE_ETH_RX_OFFLOAD_SCATTER;
 
 	return 0;
 }
@@ -579,26 +631,29 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
 	struct pmd_internals *internals = dev->data->dev_private;
 	struct pkt_rx_queue *pkt_q = &internals->rx_queue[rx_queue_id];
 	unsigned int buf_size, data_size;
+	bool scatter_enabled;
 
 	pkt_q->mb_pool = mb_pool;
 
-	/* Now get the space available for data in the mbuf */
 	buf_size = rte_pktmbuf_data_room_size(pkt_q->mb_pool) -
 		RTE_PKTMBUF_HEADROOM;
 	data_size = internals->req.tp_frame_size;
 	data_size -= TPACKET2_HDRLEN - sizeof(struct sockaddr_ll);
 
-	if (data_size > buf_size) {
+	scatter_enabled = !!(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER);
+
+	if (!scatter_enabled && data_size > buf_size) {
 		PMD_LOG(ERR,
-			"%s: %d bytes will not fit in mbuf (%d bytes)",
+			"%s: %d bytes will not fit in mbuf (%d bytes), enable scatter offload",
 			dev->device->name, data_size, buf_size);
-		return -ENOMEM;
+		return -EINVAL;
 	}
 
 	dev->data->rx_queues[rx_queue_id] = pkt_q;
 	pkt_q->in_port = dev->data->port_id;
 	pkt_q->vlan_strip = internals->vlan_strip;
 	pkt_q->timestamp_offloading = internals->timestamp_offloading;
+	pkt_q->scatter_enabled = scatter_enabled;
 
 	return 0;
 }
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v6] net/af_packet: add Rx scatter for jumbo frames
  2026-03-24 16:29     ` [PATCH v6] net/af_packet: add Rx scatter " Sriram Yagnaraman
@ 2026-03-25 17:31       ` Stephen Hemminger
  2026-03-26 13:01         ` Sriram Yagnaraman
  0 siblings, 1 reply; 9+ messages in thread
From: Stephen Hemminger @ 2026-03-25 17:31 UTC (permalink / raw)
  To: Sriram Yagnaraman; +Cc: dev, xavier.guillaume, robin.l.lin

On Tue, 24 Mar 2026 17:29:52 +0100
Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> wrote:

> +/*
> + * Copy packet data into chained mbufs when it exceeds single mbuf tailroom.
> + * Returns 0 on success, -1 on mbuf allocation failure.
> + */
> +static int
> +eth_af_packet_rx_scatter(struct rte_mempool *mb_pool, struct rte_mbuf *mbuf,
> +		const uint8_t *data, uint32_t data_len)
> +{
> +	uint16_t len = rte_pktmbuf_tailroom(mbuf);
> +	struct rte_mbuf *m = mbuf;
> +
> +	memcpy(rte_pktmbuf_mtod(mbuf, void *), data, len);
> +	rte_pktmbuf_data_len(mbuf) = len;

Very minor personal preference here. I prefer avoiding using
rte_pktmbuf_data_len() and rte_pktmbuf_pkt_len() since they are
really macros and the use of data_len and pkt_len directly is clearer
in code that is manipulating other mbuf fields.

Applied to next-net

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [PATCH v6] net/af_packet: add Rx scatter for jumbo frames
  2026-03-25 17:31       ` Stephen Hemminger
@ 2026-03-26 13:01         ` Sriram Yagnaraman
  0 siblings, 0 replies; 9+ messages in thread
From: Sriram Yagnaraman @ 2026-03-26 13:01 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: dev@dpdk.org, xavier.guillaume@ovhcloud.com, Robin Lin L



> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Wednesday, 25 March 2026 18:32
> To: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>
> Cc: dev@dpdk.org; xavier.guillaume@ovhcloud.com; Robin Lin L
> <robin.l.lin@ericsson.com>
> Subject: Re: [PATCH v6] net/af_packet: add Rx scatter for jumbo frames
> 
> On Tue, 24 Mar 2026 17:29:52 +0100
> Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> wrote:
> 
> > +/*
> > + * Copy packet data into chained mbufs when it exceeds single mbuf
> tailroom.
> > + * Returns 0 on success, -1 on mbuf allocation failure.
> > + */
> > +static int
> > +eth_af_packet_rx_scatter(struct rte_mempool *mb_pool, struct rte_mbuf
> *mbuf,
> > +		const uint8_t *data, uint32_t data_len) {
> > +	uint16_t len = rte_pktmbuf_tailroom(mbuf);
> > +	struct rte_mbuf *m = mbuf;
> > +
> > +	memcpy(rte_pktmbuf_mtod(mbuf, void *), data, len);
> > +	rte_pktmbuf_data_len(mbuf) = len;
> 
> Very minor personal preference here. I prefer avoiding using
> rte_pktmbuf_data_len() and rte_pktmbuf_pkt_len() since they are really
> macros and the use of data_len and pkt_len directly is clearer in code that is
> manipulating other mbuf fields.
> 

I agree, will keep in mind for future patches. Thank you for applying my patches.

> Applied to next-net

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-03-26 13:01 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-12  7:34 [PATCH v3] net/af_packet: add multi-segment mbuf support for jumbo frames Sriram Yagnaraman
2026-03-12 16:06 ` Stephen Hemminger
2026-03-12 16:34 ` Stephen Hemminger
2026-03-13  9:10 ` [PATCH v4] " Sriram Yagnaraman
2026-03-18  9:47   ` [PATCH v5] " Sriram Yagnaraman
2026-03-23 23:42     ` Stephen Hemminger
2026-03-24 16:29     ` [PATCH v6] net/af_packet: add Rx scatter " Sriram Yagnaraman
2026-03-25 17:31       ` Stephen Hemminger
2026-03-26 13:01         ` Sriram Yagnaraman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox