* [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff
@ 2026-11-06 10:54 Lorenzo Bianconi
2026-11-10 13:54 ` netdev-bot+sashiko
2026-11-12 2:13 ` Jakub Kicinski
0 siblings, 2 replies; 4+ messages in thread
From: Lorenzo Bianconi @ 2026-11-06 10:54 UTC (permalink / raw)
To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Jose Abreu, Ong Boon Leong
Cc: netdev, linux-arm-kernel, bpf, Nicolai Buchwitz, Lorenzo Bianconi
An earlier commit dropped frames split across multiple descriptors, or
using a second buffer, while XDP was enabled. This prevented the
per-descriptor XDP path from re-parsing frame tails and from applying FCS
handling to truncated buffers.
Rework stmmac_rx() to accumulate all the descriptors that make up a frame
in an xdp_buff, so the XDP program runs on the full (possibly fragmented)
packet instead of just the first buffer. Frames split across descriptors
or buffers are now passed to programs loaded with BPF_F_XDP_HAS_FRAGS;
programs without that capability continue to drop fragmented frames. When
the frame is not consumed by the program, assemble the skb from the head
buffer and the collected fragments via napi_build_skb()/
xdp_update_skb_frags_info().
Note that XDP still rejects an MTU larger than ETH_DATA_LEN
(stmmac_xdp_set_prog(), stmmac_change_mtu()) and disables SPH, so an
ordinary frame fits in a single buffer. This path is therefore exercised
by frames that do span several descriptors (for example jumbo frames the
MAC admits through its jumbo-enable bit) and is groundwork for extending
XDP to jumbo/SPH; it does not enable XDP jumbo support by itself.
To do so, store the in-progress xdp_buff in rx_q->state instead of the
partially built skb, so the accumulated head and fragments survive a NAPI
poll boundary (mid-frame dma_own or dirty_rx break). The state is saved
only while a frame is in progress and cleared once it completes, leaving
it untouched when the poll does not process anything (e.g. netpoll invoked
with a zero budget).
In addition:
- advertise NETDEV_XDP_ACT_RX_SG, since the RX path can now deliver
non-linear buffers to XDP;
- register the xdp_rxq with the fragment allocation size, so
bpf_xdp_adjust_tail() can grow the tail of fragmented frames;
- build the skb head with napi_build_skb() passing xdp->frame_sz, so
skb_shinfo() lands on the same shared_info the fragments were accumulated
into;
- carry the XDP metadata written with bpf_xdp_adjust_meta() into the skb
via skb_metadata_set(), so it is no longer dropped and TC/BPF programs
can read it through data_meta;
- attach fragments with xdp_buff_add_frag(), which initializes all the
shared_info fields and takes care of the pfmemalloc bit;
- charge fragments the whole page_pool page size (xdp->frame_sz) as
truesize instead of the old dma_buf_sz; with SPH/buf2 most frames carry
a fragment, so skb->truesize grows by roughly 1.5x on 4K pages and 2x on
64K pages, which reduces the effective socket receive buffer. The new
value is more accurate now that a fragment is a full page_pool page, but
it is a behavior change;
- release the buffers collected in the xdp_buff when a frame is dropped
on RX errors; the previous code relied on dev_kfree_skb() recycling the
skb frags, which no longer applies now that the head and fragments live
in an xdp_buff rather than a partially built skb;
- drop the whole frame when it would exceed MAX_SKB_FRAGS, where the
previous skb_add_rx_frag() calls were unbounded;
- strip the Ethernet FCS from the accumulated xdp_buff with a driver-local
tail shrink (stmmac_xdp_shrink_tail()) for both linear and fragmented
frames, instead of trimming the skb or the first descriptor length.
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
---
Rework stmmac_rx() to build the xdp_buff from all the descriptors that make
up a frame, so the XDP program runs on the whole (possibly fragmented)
packet instead of the first buffer only. Fix possible rx_q state leaks
when closing/opening the interface.
---
Changes in v9:
- Rebase on top of net-next main branch.
- Link to v8: https://lore.kernel.org/r/20261018-stmmac-rx-mb-v8-0-5f58c0b25849@oss.qualcomm.com
Changes in v8:
- Add patch 'net: stmmac: fix VLAN tag stripping on non-linear RX
frames' to fix sw rx vlan parsing.
- Rebase on top of net-next main branch.
- Link to v7: https://lore.kernel.org/r/20261014-stmmac-rx-mb-v7-0-5d2d70f052cd@oss.qualcomm.com
Changes in v7:
- Take into account the case where buf2_len is not 0 for the single
descriptor case if the interface is running in xdp mode.
- Report rx_not_ls bit if the packet is splitted over multiple
descriptors for dwmac100 and dwmac1000 cores.
- Link to v6: https://lore.kernel.org/r/20261012-stmmac-rx-mb-v6-0-2225050915c1@oss.qualcomm.com
Changes in v6:
- Drop patch 'net: stmmac: allow non-linear xdp_buff in XDP mode' since
it requires some XDP fixes.
- Introduce patch 'net: stmmac: drop split frames on AF_XDP zero-copy
RX' to always drop XDP multi-buff frames for AF_XDP mode since it not
yet supported there.
- Introduce patch 'net: stmmac: drop multi-buffer frames in XDP mode' to
fix pending bugs due to missing XDP multi-buffer support before patch
'net: stmmac: rework stmmac_rx to support XDP rx multi-buff'
- Link to v5: https://lore.kernel.org/r/20261009-stmmac-rx-mb-v5-0-c38fa4eaa138@oss.qualcomm.com
Changes in v5:
- Fix possible read after bound issue in patch 1/3.
- Do not allow parsing non-linear XDP buffers if the program loaded does
not support frags.
- Add patch 3/3 to remove MTU constraint for XDP prog.
- Link to v4: https://lore.kernel.org/r/20261006-stmmac-rx-mb-v4-0-efa4ca974e3d@oss.qualcomm.com
Changes in v4:
- Reset in_progress flag when the packet is fully consumed.
- Advetise NETDEV_XDP_ACT_RX_SG capability.
- Link to v3: https://lore.kernel.org/r/20261004-stmmac-rx-mb-v3-0-50fa171af9ec@oss.qualcomm.com
Changes in v3:
- Return secondary page in case of error.
- Introduce stmmac_xdp_shrink_tail() to remove the ethernet FCS.
- Add patch 'net: stmmac: take ownership of saved RX state at poll
entry' from James.
- Link to v2: https://lore.kernel.org/r/20260921-stmmac-rx-mb-v2-1-6e826e1ff306@oss.qualcomm.com
Changes in v2:
- Rely on xdp_buff_add_frag() to create xdp fragments
- Fix bugs in rx_q state recording
- Fix the corner case where we receive more than MAX_SKB_FRAGS fragments
- Cosmetics
- Link to v1: https://lore.kernel.org/r/20260918-stmmac-rx-mb-v1-1-0b4517d404af@oss.qualcomm.com
---
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 3 +-
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 326 ++++++++++++++--------
2 files changed, 206 insertions(+), 123 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index b76acfe0d958..5c8a38b69391 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -132,7 +132,8 @@ struct stmmac_rx_queue {
dma_addr_t dma_rx_phy;
unsigned int state_saved;
struct {
- struct sk_buff *skb;
+ struct xdp_buff xdp;
+ unsigned int n_descs;
unsigned int len;
unsigned int error;
} state;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index a2177777727e..c215cc70caa1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -159,6 +159,8 @@ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
u32 rxmode, u32 chan);
static void stmmac_vlan_restore(struct stmmac_priv *priv);
+static void stmmac_xdp_put_buff(struct stmmac_rx_queue *rx_q,
+ struct xdp_buff *xdp, bool allow_direct);
#ifdef CONFIG_DEBUG_FS
static const struct net_device_ops stmmac_netdev_ops;
@@ -2183,8 +2185,10 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv,
else
dma_free_rx_skbufs(priv, dma_conf, queue);
- dev_kfree_skb_any(rx_q->state.skb);
- rx_q->state.skb = NULL;
+ if (rx_q->state.n_descs && !rx_q->xsk_pool) {
+ stmmac_xdp_put_buff(rx_q, &rx_q->state.xdp, false);
+ rx_q->state.n_descs = 0;
+ }
rx_q->state_saved = false;
rx_q->buf_alloc_num = 0;
rx_q->xsk_pool = NULL;
@@ -2290,10 +2294,6 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
num_pages = DIV_ROUND_UP(dma_buf_sz_pad, PAGE_SIZE);
- rx_q->queue_index = queue;
- rx_q->priv_data = priv;
- rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;
-
pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
pp_params.pool_size = dma_conf->dma_rx_size;
pp_params.order = order_base_2(num_pages);
@@ -2321,6 +2321,10 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
if (!addr)
return -ENOMEM;
+ rx_q->queue_index = queue;
+ rx_q->priv_data = priv;
+ rx_q->napi_skb_frag_size = PAGE_SIZE << pp_params.order;
+
if (priv->extend_desc)
rx_q->dma_erx = addr;
else
@@ -2332,7 +2336,8 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
else
napi_id = ch->rx_napi.napi_id;
- ret = xdp_rxq_info_reg(&rx_q->xdp_rxq, priv->dev, queue, napi_id);
+ ret = __xdp_rxq_info_reg(&rx_q->xdp_rxq, priv->dev, queue, napi_id,
+ rx_q->napi_skb_frag_size);
if (ret) {
netdev_err(priv->dev, "Failed to register xdp rxq info\n");
return -EINVAL;
@@ -5495,8 +5500,8 @@ static int __stmmac_xdp_run_prog(struct stmmac_priv *priv,
static struct sk_buff *stmmac_xdp_run_prog(struct stmmac_priv *priv,
struct xdp_buff *xdp)
{
+ int res = STMMAC_XDP_CONSUMED;
struct bpf_prog *prog;
- int res;
prog = READ_ONCE(priv->xdp_prog);
if (!prog) {
@@ -5504,7 +5509,8 @@ static struct sk_buff *stmmac_xdp_run_prog(struct stmmac_priv *priv,
goto out;
}
- res = __stmmac_xdp_run_prog(priv, prog, xdp);
+ if (likely(!xdp_buff_has_frags(xdp) || prog->aux->xdp_has_frags))
+ res = __stmmac_xdp_run_prog(priv, prog, xdp);
out:
return ERR_PTR(-res);
}
@@ -5836,6 +5842,119 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
return failure ? limit : (int)count;
}
+static void
+stmmac_xdp_put_buff(struct stmmac_rx_queue *rx_q, struct xdp_buff *xdp,
+ bool allow_direct)
+{
+ struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
+ int i;
+
+ if (likely(!xdp_buff_has_frags(xdp)))
+ goto out;
+
+ for (i = 0; i < sinfo->nr_frags; i++)
+ page_pool_put_full_page(rx_q->page_pool,
+ skb_frag_page(&sinfo->frags[i]),
+ allow_direct);
+out:
+ page_pool_put_full_page(rx_q->page_pool, virt_to_head_page(xdp->data),
+ allow_direct);
+}
+
+static struct sk_buff *stmmac_build_skb(struct xdp_buff *xdp)
+{
+ struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
+ u32 metasize = xdp->data - xdp->data_meta;
+ struct sk_buff *skb;
+ u8 num_frags = 0;
+
+ if (unlikely(xdp_buff_has_frags(xdp)))
+ num_frags = sinfo->nr_frags;
+
+ skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz);
+ if (!skb)
+ return NULL;
+
+ skb_mark_for_recycle(skb);
+ skb_reserve(skb, xdp->data - xdp->data_hard_start);
+ skb_put(skb, xdp->data_end - xdp->data);
+ if (metasize)
+ skb_metadata_set(skb, metasize);
+
+ if (unlikely(xdp_buff_has_frags(xdp)))
+ xdp_update_skb_frags_info(skb, num_frags, sinfo->xdp_frags_size,
+ sinfo->xdp_frags_truesize,
+ xdp_buff_get_skb_flags(xdp));
+ return skb;
+}
+
+static bool stmmac_build_xdp_frags(struct stmmac_priv *priv,
+ struct stmmac_rx_queue *rx_q,
+ unsigned int len, struct page *page,
+ unsigned int offset,
+ enum dma_data_direction dma_dir,
+ struct xdp_buff *xdp)
+{
+ dma_addr_t dma_addr = page_pool_get_dma_addr(page) + offset;
+
+ dma_sync_single_for_cpu(priv->device, dma_addr, len, dma_dir);
+ if (!xdp_buff_add_frag(xdp, page_to_netmem(page), offset, len,
+ xdp->frame_sz)) {
+ page_pool_put_full_page(rx_q->page_pool, page, true);
+ return false;
+ }
+
+ return true;
+}
+
+static int stmmac_xdp_shrink_tail(struct stmmac_rx_queue *rx_q,
+ struct xdp_buff *xdp, int offset)
+{
+ struct skb_shared_info *sinfo;
+ int i;
+
+ if (unlikely(offset < 0 ||
+ offset > (int)xdp_get_buff_len(xdp) - ETH_HLEN))
+ return -EINVAL;
+
+ if (likely(!xdp_buff_has_frags(xdp))) {
+ xdp->data_end -= offset;
+ return 0;
+ }
+
+ sinfo = xdp_get_shared_info_from_buff(xdp);
+ for (i = sinfo->nr_frags - 1; i >= 0 && offset > 0; i--) {
+ skb_frag_t *frag = &sinfo->frags[i];
+ int delta = min_t(int, offset, skb_frag_size(frag));
+
+ if (delta == skb_frag_size(frag)) {
+ /* The whole frag is consumed by the strip: return it
+ * to the page pool right away. Its ring slot still
+ * carries the page DMA address until stmmac_rx_refill()
+ * re-arms it at the end of the NAPI poll. This is
+ * safe because HW cannot touch the slot until then.
+ */
+ page_pool_put_full_page(rx_q->page_pool,
+ skb_frag_page(frag), true);
+ sinfo->nr_frags--;
+ sinfo->xdp_frags_truesize -= xdp->frame_sz;
+ } else {
+ skb_frag_size_sub(frag, delta);
+ }
+
+ sinfo->xdp_frags_size -= delta;
+ offset -= delta;
+ }
+
+ if (unlikely(!sinfo->nr_frags)) {
+ xdp_buff_clear_frags_flag(xdp);
+ xdp_buff_clear_frag_pfmemalloc(xdp);
+ xdp->data_end -= offset;
+ }
+
+ return 0;
+}
+
/**
* stmmac_rx - manage the receive process
* @priv: driver private structure
@@ -5849,21 +5968,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
u32 rx_errors = 0, rx_dropped = 0, rx_bytes = 0, rx_packets = 0;
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ unsigned int n_descs = 0, next_entry = rx_q->cur_rx;
struct stmmac_channel *ch = &priv->channel[queue];
unsigned int count = 0, error = 0, len = 0;
- unsigned int next_entry = rx_q->cur_rx;
bool in_progress = rx_q->state_saved;
enum dma_data_direction dma_dir;
int coe = priv->hw->rx_csum;
- unsigned int desc_size;
- struct sk_buff *skb = NULL;
struct stmmac_xdp_buff ctx;
- bool fcs_stripped = false;
+ unsigned int desc_size;
int xdp_status = 0;
int bufsz;
dma_dir = page_pool_get_dma_dir(rx_q->page_pool);
- bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
+ bufsz = rx_q->napi_skb_frag_size;
+ ctx.priv = priv;
if (netif_msg_rx_status(priv)) {
void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);
@@ -5876,11 +5994,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
}
if (rx_q->state_saved) {
- skb = rx_q->state.skb;
+ ctx.xdp = rx_q->state.xdp;
error = rx_q->state.error;
+ n_descs = rx_q->state.n_descs;
len = rx_q->state.len;
- rx_q->state.skb = NULL;
rx_q->state_saved = false;
+ rx_q->state.n_descs = 0;
}
while (count < limit) {
@@ -5888,11 +6007,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
enum pkt_hash_types hash_type;
struct stmmac_rx_buffer *buf;
struct dma_desc *np, *p;
+ struct sk_buff *skb;
int entry, status;
u32 hash;
if (!in_progress) {
- skb = NULL;
error = 0;
len = 0;
}
@@ -5940,19 +6059,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
rx_errors++;
}
- /* We do not support SG in XDP mode */
- if (unlikely(!error && (status & rx_not_ls) &&
- stmmac_xdp_is_enabled(priv))) {
- rx_dropped++;
- error = 1;
- }
-
if (unlikely(error)) {
if (status & rx_not_ls)
goto read_again;
- dev_kfree_skb(skb);
- goto next;
+ goto error_free_frag;
}
/* Buffer is good. Go on. */
@@ -5962,120 +6073,79 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
buf2_len = stmmac_rx_buf2_len(priv, p, status, len);
len += buf2_len;
- /* Drop the packet if it is split over buf1 and buf2 on a
- * single descriptor if the interface is running in XDP mode.
- */
- if (unlikely(stmmac_xdp_is_enabled(priv) && buf2_len)) {
- rx_dropped++;
- goto next;
- }
-
- /* ACS is disabled; strip manually. */
- if (likely(!(status & rx_not_ls)))
- len -= ETH_FCS_LEN;
-
- if (!skb) {
- /* Each frame starts here: reset the FCS handling */
- fcs_stripped = false;
-
+ if (!n_descs) {
dma_sync_single_for_cpu(priv->device, buf->addr,
buf1_len, dma_dir);
net_prefetch(page_address(buf->page) +
buf->page_offset);
- if (stmmac_xdp_is_enabled(priv) && !buf2_len) {
- buf1_len -= ETH_FCS_LEN;
- fcs_stripped = true;
- }
-
xdp_init_buff(&ctx.xdp, bufsz, &rx_q->xdp_rxq);
xdp_prepare_buff(&ctx.xdp, page_address(buf->page),
buf->page_offset, buf1_len, true);
-
- ctx.priv = priv;
- ctx.desc = p;
- ctx.ndesc = np;
-
- skb = stmmac_xdp_run_prog(priv, &ctx.xdp);
- /* For Not XDP_PASS verdict */
- if (IS_ERR(skb)) {
- unsigned int xdp_res = -PTR_ERR(skb);
-
- if (xdp_res & STMMAC_XDP_CONSUMED) {
- page_pool_recycle_direct(rx_q->page_pool,
- virt_to_head_page(ctx.xdp.data));
- buf->page = NULL;
- rx_dropped++;
- goto next;
- } else if (xdp_res & (STMMAC_XDP_TX |
- STMMAC_XDP_REDIRECT)) {
- xdp_status |= xdp_res;
- buf->page = NULL;
- goto next;
- }
- }
- }
-
- if (!skb) {
- unsigned int metasize;
-
- /* XDP program may expand or reduce tail */
- buf1_len = ctx.xdp.data_end - ctx.xdp.data;
-
- skb = napi_build_skb(page_address(buf->page),
- rx_q->napi_skb_frag_size);
- if (!skb) {
- page_pool_recycle_direct(rx_q->page_pool,
- buf->page);
- buf->page = NULL;
- rx_dropped++;
- count++;
- goto drain_data;
- }
-
- /* XDP program may adjust header */
- skb_reserve(skb,
- ctx.xdp.data - ctx.xdp.data_hard_start);
- skb_put(skb, buf1_len);
- skb_mark_for_recycle(skb);
-
- metasize = ctx.xdp.data - ctx.xdp.data_meta;
- if (metasize)
- skb_metadata_set(skb, metasize);
buf->page = NULL;
} else if (buf1_len) {
- dma_sync_single_for_cpu(priv->device, buf->addr,
- buf1_len, dma_dir);
- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- buf->page, buf->page_offset, buf1_len,
- priv->dma_conf.dma_buf_sz);
+ if (!stmmac_build_xdp_frags(priv, rx_q, buf1_len,
+ buf->page,
+ buf->page_offset,
+ dma_dir, &ctx.xdp)) {
+ if (!error)
+ rx_dropped++;
+ error = 1;
+ }
buf->page = NULL;
}
if (buf2_len) {
- dma_sync_single_for_cpu(priv->device, buf->sec_addr,
- buf2_len, dma_dir);
- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- buf->sec_page, 0, buf2_len,
- priv->dma_conf.dma_buf_sz);
+ if (!stmmac_build_xdp_frags(priv, rx_q, buf2_len,
+ buf->sec_page, 0,
+ dma_dir, &ctx.xdp)) {
+ if (!error)
+ rx_dropped++;
+ error = 1;
+ }
buf->sec_page = NULL;
}
+ n_descs++;
-drain_data:
if (likely(status & rx_not_ls))
goto read_again;
- if (!skb)
- continue;
- /* Got entire packet into SKB. Finish it. */
+ /* ACS is disabled; strip manually. */
+ len -= ETH_FCS_LEN;
+ if (stmmac_xdp_shrink_tail(rx_q, &ctx.xdp, ETH_FCS_LEN)) {
+ if (!error)
+ rx_dropped++;
+ error = 1;
+ }
- /* Remove FCS if needed */
- if (!fcs_stripped && pskb_trim(skb, len)) {
- dev_kfree_skb_any(skb);
- rx_dropped++;
+ if (unlikely(error))
+ goto error_free_frag;
+
+ ctx.desc = p;
+ ctx.ndesc = np;
+
+ skb = stmmac_xdp_run_prog(priv, &ctx.xdp);
+ /* For Not XDP_PASS verdict */
+ if (IS_ERR(skb)) {
+ unsigned int xdp_res = -PTR_ERR(skb);
+
+ if (xdp_res & STMMAC_XDP_CONSUMED) {
+ stmmac_xdp_put_buff(rx_q, &ctx.xdp, true);
+ rx_dropped++;
+ } else if (xdp_res & (STMMAC_XDP_TX |
+ STMMAC_XDP_REDIRECT)) {
+ xdp_status |= xdp_res;
+ }
goto next;
}
+ skb = stmmac_build_skb(&ctx.xdp);
+ if (!skb) {
+ rx_dropped++;
+ goto error_free_frag;
+ }
+
+ /* Got entire packet into SKB. Finish it. */
stmmac_get_rx_hwtstamp(priv, p, np, skb);
if (priv->hw->hw_vlan_en) {
@@ -6104,15 +6174,23 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
rx_bytes += len;
next:
in_progress = false;
- skb = NULL;
+ n_descs = 0;
+ count++;
+ continue;
+error_free_frag:
+ if (n_descs)
+ stmmac_xdp_put_buff(rx_q, &ctx.xdp, true);
+ in_progress = false;
+ n_descs = 0;
count++;
}
- if (in_progress || skb) {
- rx_q->state_saved = true;
- rx_q->state.skb = skb;
+ if (in_progress || n_descs) {
+ rx_q->state.xdp = ctx.xdp;
+ rx_q->state.n_descs = n_descs;
rx_q->state.error = error;
rx_q->state.len = len;
+ rx_q->state_saved = true;
}
stmmac_finalize_xdp_rx(priv, xdp_status);
@@ -8129,6 +8207,7 @@ static int __stmmac_dvr_probe(struct device *device,
ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXCSUM;
ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
+ NETDEV_XDP_ACT_RX_SG |
NETDEV_XDP_ACT_XSK_ZEROCOPY;
/* Default qdisc num_tx_queues */
@@ -8455,9 +8534,12 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
{
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
- dev_kfree_skb_any(rx_q->state.skb);
- rx_q->state.skb = NULL;
+ if (rx_q->state.n_descs && !rx_q->xsk_pool) {
+ stmmac_xdp_put_buff(rx_q, &rx_q->state.xdp, false);
+ rx_q->state.n_descs = 0;
+ }
rx_q->state_saved = false;
+
rx_q->cur_rx = 0;
rx_q->dirty_rx = 0;
}
---
base-commit: 6584acd42820478910a771efb28a355a707e9cb0
change-id: 20260918-stmmac-rx-mb-16469a2714ea
Best regards,
--
Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff
2026-11-06 10:54 [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff Lorenzo Bianconi
@ 2026-11-10 13:54 ` netdev-bot+sashiko
2026-11-11 8:45 ` Lorenzo Bianconi
2026-11-12 2:13 ` Jakub Kicinski
1 sibling, 1 reply; 4+ messages in thread
From: netdev-bot+sashiko @ 2026-11-10 13:54 UTC (permalink / raw)
To: lorenzo.bianconi
Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, alexandre.torgue, ast, daniel, hawk,
john.fastabend, sdf, Jose.Abreu, boon.leong.ong, netdev,
linux-arm-kernel, bpf, nb
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 0 (1) · Medium: 1 · Low: 1
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] The skb head truesize grows on every received frame, including
with no XDP program loaded, and the commit message does not say so.
- [Low] The commit message overstates one change.
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the patch
makes it wider.
--
net: stmmac: rework stmmac_rx to support XDP rx multi-buff
This reworks stmmac_rx() to collect all the descriptors of a frame into an
xdp_buff, so programs with frags support see the whole packet. On XDP_PASS
the skb is built from the xdp_buff head and its fragments. The patch also
saves the in-progress xdp_buff in rx_q->state across NAPI polls, advertises
NETDEV_XDP_ACT_RX_SG, and strips the FCS with a driver-local tail shrink.
> - carry the XDP metadata written with bpf_xdp_adjust_meta() into the skb
> via skb_metadata_set(), so it is no longer dropped and TC/BPF programs
> can read it through data_meta;
[Severity: Low]
Is this item accurate? The XDP_PASS path in stmmac_rx() already did this
before the patch. The patch removes these lines from stmmac_rx():
- metasize = ctx.xdp.data - ctx.xdp.data_meta;
- if (metasize)
- skb_metadata_set(skb, metasize);
The same logic now lives in stmmac_build_skb().
Metadata on linear frames was not being dropped before. The only frames
that never got metadata were multi-descriptor or buf2 frames, and XDP mode
dropped those outright. The commit message already covers that elsewhere.
Could this item be reworded so it does not read as a fix for a metadata
loss bug?
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index a2177777727ef..c215cc70caa19 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
[ ... ]
> @@ -2290,10 +2294,6 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
> SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
> num_pages = DIV_ROUND_UP(dma_buf_sz_pad, PAGE_SIZE);
>
> - rx_q->queue_index = queue;
> - rx_q->priv_data = priv;
> - rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;
> -
> pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
> pp_params.pool_size = dma_conf->dma_rx_size;
> pp_params.order = order_base_2(num_pages);
> @@ -2321,6 +2321,10 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
> if (!addr)
> return -ENOMEM;
>
> + rx_q->queue_index = queue;
> + rx_q->priv_data = priv;
> + rx_q->napi_skb_frag_size = PAGE_SIZE << pp_params.order;
> +
[Severity: Medium]
Does this change the skb head truesize for every received frame, even when
no XDP program is attached?
Before the patch the head size was num_pages * PAGE_SIZE. Now it is rounded
up to the page_pool allocation order, and skb->truesize is derived from it
here:
stmmac_rx()
bufsz = rx_q->napi_skb_frag_size;
...
xdp_init_buff(&ctx.xdp, bufsz, &rx_q->xdp_rxq);
stmmac_build_skb()
skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz);
On 4K pages, a dma_buf_sz of 8188 (BUF_SIZE_8KiB) gives num_pages = 3, so
the head grows from 12K to 16K. A dma_buf_sz of 16368 (BUF_SIZE_16KiB)
gives num_pages = 5, so the head grows from 20K to 32K.
That shrinks the effective socket receive buffer for jumbo MTU users who
never load XDP. The commit message calls out the fragment truesize change
as a behavior change, but not this one. Should this be mentioned too?
[ ... ]
> @@ -5962,120 +6073,79 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
[ ... ]
> + skb = stmmac_xdp_run_prog(priv, &ctx.xdp);
> + /* For Not XDP_PASS verdict */
> + if (IS_ERR(skb)) {
> + unsigned int xdp_res = -PTR_ERR(skb);
> +
> + if (xdp_res & STMMAC_XDP_CONSUMED) {
> + stmmac_xdp_put_buff(rx_q, &ctx.xdp, true);
> + rx_dropped++;
> + } else if (xdp_res & (STMMAC_XDP_TX |
> + STMMAC_XDP_REDIRECT)) {
> + xdp_status |= xdp_res;
> + }
> + goto next;
> + }
[Severity: High]
This isn't a bug introduced by this patch, but multi-buffer frames can now
reach XDP_TX, which makes it worse. Does XDP_TX completion recycle pages
into this RX queue's page_pool direct cache from the wrong NAPI context?
stmmac_xdp_xmit_back() picks the TX queue by CPU, not by RX queue:
stmmac_xdp_get_tx_queue()
while (index >= priv->plat->tx_queues_to_use)
index -= priv->plat->tx_queues_to_use;
Completion then runs in that channel's tx_napi:
stmmac_napi_poll_tx()
stmmac_tx_clean()
xdp_return_frame_rx_napi(xdpf)
__xdp_return(..., napi_direct=true) /* head and every frag */
page_pool_put_unrefed_netmem(..., allow_direct=true)
page_pool_recycle_in_cache()
stmmac_tx_clean() {
...
if (xdpf &&
tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XDP_TX) {
xdp_return_frame_rx_napi(xdpf);
...
}
When allow_direct is already true, page_pool_put_unrefed_netmem() skips the
page_pool_napi_local() check. That means pool->alloc.cache and
pool->alloc.count are updated without a lock.
The tx_napi is a separate NAPI instance from the rx_napi that owns the
pool. Meanwhile the owning rx_napi can be on another CPU running
stmmac_rx_refill() or the allow_direct=true puts in stmmac_xdp_put_buff(),
stmmac_build_xdp_frags() and stmmac_xdp_shrink_tail().
Can this corrupt the alloc cache? If so, the same page could be handed to
two RX descriptors, or recycled while an skb still holds it.
The race dates back to XDP_TX support. With this patch, though, each
XDP_TX frame can push the head plus several fragment pages down this path.
Would returning XDP_TX frames with xdp_return_frame() in stmmac_tx_clean()
avoid this?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261106-stmmac-rx-mb-v9-1-c099c923664b%40oss.qualcomm.com
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff
2026-11-10 13:54 ` netdev-bot+sashiko
@ 2026-11-11 8:45 ` Lorenzo Bianconi
0 siblings, 0 replies; 4+ messages in thread
From: Lorenzo Bianconi @ 2026-11-11 8:45 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, alexandre.torgue, ast, daniel, hawk,
john.fastabend, sdf, Jose.Abreu, boon.leong.ong, netdev,
linux-arm-kernel, bpf, nb
[-- Attachment #1: Type: text/plain, Size: 7126 bytes --]
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider.
>
> Critical: 0 · High: 0 (1) · Medium: 1 · Low: 1
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [Medium] The skb head truesize grows on every received frame, including
> with no XDP program loaded, and the commit message does not say so.
> - [Low] The commit message overstates one change.
>
> Pre-existing issues:
> - [High] This problem wasn't introduced by this patch, but the patch
> makes it wider.
>
> --
>
> net: stmmac: rework stmmac_rx to support XDP rx multi-buff
>
> This reworks stmmac_rx() to collect all the descriptors of a frame into an
> xdp_buff, so programs with frags support see the whole packet. On XDP_PASS
> the skb is built from the xdp_buff head and its fragments. The patch also
> saves the in-progress xdp_buff in rx_q->state across NAPI polls, advertises
> NETDEV_XDP_ACT_RX_SG, and strips the FCS with a driver-local tail shrink.
>
> > - carry the XDP metadata written with bpf_xdp_adjust_meta() into the skb
> > via skb_metadata_set(), so it is no longer dropped and TC/BPF programs
> > can read it through data_meta;
>
> [Severity: Low]
> Is this item accurate? The XDP_PASS path in stmmac_rx() already did this
> before the patch. The patch removes these lines from stmmac_rx():
>
> - metasize = ctx.xdp.data - ctx.xdp.data_meta;
> - if (metasize)
> - skb_metadata_set(skb, metasize);
>
> The same logic now lives in stmmac_build_skb().
>
> Metadata on linear frames was not being dropped before. The only frames
> that never got metadata were multi-descriptor or buf2 frames, and XDP mode
> dropped those outright. The commit message already covers that elsewhere.
>
> Could this item be reworded so it does not read as a fix for a metadata
> loss bug?
I guess this is minor point, I can update the commit message if I need to
repost.
>
> > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> > index a2177777727ef..c215cc70caa19 100644
> > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
>
> [ ... ]
>
> > @@ -2290,10 +2294,6 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
> > SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
> > num_pages = DIV_ROUND_UP(dma_buf_sz_pad, PAGE_SIZE);
> >
> > - rx_q->queue_index = queue;
> > - rx_q->priv_data = priv;
> > - rx_q->napi_skb_frag_size = num_pages * PAGE_SIZE;
> > -
> > pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
> > pp_params.pool_size = dma_conf->dma_rx_size;
> > pp_params.order = order_base_2(num_pages);
> > @@ -2321,6 +2321,10 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
> > if (!addr)
> > return -ENOMEM;
> >
> > + rx_q->queue_index = queue;
> > + rx_q->priv_data = priv;
> > + rx_q->napi_skb_frag_size = PAGE_SIZE << pp_params.order;
> > +
>
> [Severity: Medium]
> Does this change the skb head truesize for every received frame, even when
> no XDP program is attached?
>
> Before the patch the head size was num_pages * PAGE_SIZE. Now it is rounded
> up to the page_pool allocation order, and skb->truesize is derived from it
> here:
>
> stmmac_rx()
> bufsz = rx_q->napi_skb_frag_size;
> ...
> xdp_init_buff(&ctx.xdp, bufsz, &rx_q->xdp_rxq);
>
> stmmac_build_skb()
> skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz);
>
> On 4K pages, a dma_buf_sz of 8188 (BUF_SIZE_8KiB) gives num_pages = 3, so
> the head grows from 12K to 16K. A dma_buf_sz of 16368 (BUF_SIZE_16KiB)
> gives num_pages = 5, so the head grows from 20K to 32K.
>
> That shrinks the effective socket receive buffer for jumbo MTU users who
> never load XDP. The commit message calls out the fragment truesize change
> as a behavior change, but not this one. Should this be mentioned too?
Same here, I guess this is minor point, I can update the commit message if
I need to repost.
>
> [ ... ]
>
> > @@ -5962,120 +6073,79 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
>
> [ ... ]
>
> > + skb = stmmac_xdp_run_prog(priv, &ctx.xdp);
> > + /* For Not XDP_PASS verdict */
> > + if (IS_ERR(skb)) {
> > + unsigned int xdp_res = -PTR_ERR(skb);
> > +
> > + if (xdp_res & STMMAC_XDP_CONSUMED) {
> > + stmmac_xdp_put_buff(rx_q, &ctx.xdp, true);
> > + rx_dropped++;
> > + } else if (xdp_res & (STMMAC_XDP_TX |
> > + STMMAC_XDP_REDIRECT)) {
> > + xdp_status |= xdp_res;
> > + }
> > + goto next;
> > + }
>
> [Severity: High]
> This isn't a bug introduced by this patch, but multi-buffer frames can now
> reach XDP_TX, which makes it worse. Does XDP_TX completion recycle pages
> into this RX queue's page_pool direct cache from the wrong NAPI context?
As pointed out by sashiko, this issue has not been introduced by this patch and
it should be fixed with a dedicated patch.
Regards,
Lorenzo
>
> stmmac_xdp_xmit_back() picks the TX queue by CPU, not by RX queue:
>
> stmmac_xdp_get_tx_queue()
> while (index >= priv->plat->tx_queues_to_use)
> index -= priv->plat->tx_queues_to_use;
>
> Completion then runs in that channel's tx_napi:
>
> stmmac_napi_poll_tx()
> stmmac_tx_clean()
> xdp_return_frame_rx_napi(xdpf)
> __xdp_return(..., napi_direct=true) /* head and every frag */
> page_pool_put_unrefed_netmem(..., allow_direct=true)
> page_pool_recycle_in_cache()
>
> stmmac_tx_clean() {
> ...
> if (xdpf &&
> tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XDP_TX) {
> xdp_return_frame_rx_napi(xdpf);
> ...
> }
>
> When allow_direct is already true, page_pool_put_unrefed_netmem() skips the
> page_pool_napi_local() check. That means pool->alloc.cache and
> pool->alloc.count are updated without a lock.
>
> The tx_napi is a separate NAPI instance from the rx_napi that owns the
> pool. Meanwhile the owning rx_napi can be on another CPU running
> stmmac_rx_refill() or the allow_direct=true puts in stmmac_xdp_put_buff(),
> stmmac_build_xdp_frags() and stmmac_xdp_shrink_tail().
>
> Can this corrupt the alloc cache? If so, the same page could be handed to
> two RX descriptors, or recycled while an skb still holds it.
>
> The race dates back to XDP_TX support. With this patch, though, each
> XDP_TX frame can push the head plus several fragment pages down this path.
> Would returning XDP_TX frames with xdp_return_frame() in stmmac_tx_clean()
> avoid this?
>
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261106-stmmac-rx-mb-v9-1-c099c923664b%40oss.qualcomm.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff
2026-11-06 10:54 [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff Lorenzo Bianconi
2026-11-10 13:54 ` netdev-bot+sashiko
@ 2026-11-12 2:13 ` Jakub Kicinski
1 sibling, 0 replies; 4+ messages in thread
From: Jakub Kicinski @ 2026-11-12 2:13 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev, Jose Abreu, Ong Boon Leong,
netdev, linux-arm-kernel, bpf, Nicolai Buchwitz
On Fri, 06 Nov 2026 11:54:19 +0100 Lorenzo Bianconi wrote:
> +static struct sk_buff *stmmac_build_skb(struct xdp_buff *xdp)
> +{
> + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
> + u32 metasize = xdp->data - xdp->data_meta;
> + struct sk_buff *skb;
> + u8 num_frags = 0;
> +
> + if (unlikely(xdp_buff_has_frags(xdp)))
> + num_frags = sinfo->nr_frags;
> +
> + skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz);
> + if (!skb)
> + return NULL;
> +
> + skb_mark_for_recycle(skb);
> + skb_reserve(skb, xdp->data - xdp->data_hard_start);
> + skb_put(skb, xdp->data_end - xdp->data);
> + if (metasize)
> + skb_metadata_set(skb, metasize);
> +
> + if (unlikely(xdp_buff_has_frags(xdp)))
> + xdp_update_skb_frags_info(skb, num_frags, sinfo->xdp_frags_size,
> + sinfo->xdp_frags_truesize,
> + xdp_buff_get_skb_flags(xdp));
> + return skb;
> +}
This is almost identical to xdp_build_skb_from_buff(), why not use the
common code?
> +static void
> +stmmac_xdp_put_buff(struct stmmac_rx_queue *rx_q, struct xdp_buff *xdp,
> + bool allow_direct)
> +{
> + struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
> + int i;
> +
> + if (likely(!xdp_buff_has_frags(xdp)))
> + goto out;
> +
> + for (i = 0; i < sinfo->nr_frags; i++)
> + page_pool_put_full_page(rx_q->page_pool,
> + skb_frag_page(&sinfo->frags[i]),
> + allow_direct);
> +out:
> + page_pool_put_full_page(rx_q->page_pool, virt_to_head_page(xdp->data),
> + allow_direct);
> +}
> +
> +static int stmmac_xdp_shrink_tail(struct stmmac_rx_queue *rx_q,
> + struct xdp_buff *xdp, int offset)
> +{
> + struct skb_shared_info *sinfo;
> + int i;
> +
> + if (unlikely(offset < 0 ||
> + offset > (int)xdp_get_buff_len(xdp) - ETH_HLEN))
> + return -EINVAL;
> +
> + if (likely(!xdp_buff_has_frags(xdp))) {
> + xdp->data_end -= offset;
> + return 0;
> + }
> +
> + sinfo = xdp_get_shared_info_from_buff(xdp);
> + for (i = sinfo->nr_frags - 1; i >= 0 && offset > 0; i--) {
> + skb_frag_t *frag = &sinfo->frags[i];
> + int delta = min_t(int, offset, skb_frag_size(frag));
> +
> + if (delta == skb_frag_size(frag)) {
> + /* The whole frag is consumed by the strip: return it
> + * to the page pool right away. Its ring slot still
> + * carries the page DMA address until stmmac_rx_refill()
> + * re-arms it at the end of the NAPI poll. This is
> + * safe because HW cannot touch the slot until then.
> + */
> + page_pool_put_full_page(rx_q->page_pool,
> + skb_frag_page(frag), true);
> + sinfo->nr_frags--;
> + sinfo->xdp_frags_truesize -= xdp->frame_sz;
> + } else {
> + skb_frag_size_sub(frag, delta);
> + }
> +
> + sinfo->xdp_frags_size -= delta;
> + offset -= delta;
> + }
> +
> + if (unlikely(!sinfo->nr_frags)) {
> + xdp_buff_clear_frags_flag(xdp);
> + xdp_buff_clear_frag_pfmemalloc(xdp);
> + xdp->data_end -= offset;
> + }
> +
> + return 0;
> +}
These two OTOH could easily become new common helpers, they seem to do
nothing stmmac specific?
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-12 2:13 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-06 10:54 [PATCH net-next v9] net: stmmac: rework stmmac_rx to support XDP rx multi-buff Lorenzo Bianconi
2026-11-10 13:54 ` netdev-bot+sashiko
2026-11-11 8:45 ` Lorenzo Bianconi
2026-11-12 2:13 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox