[PATCH net-next v2] net: stmmac: add XDP multi-buff support for TX side
From: Lorenzo Bianconi <hidden>
Date: 2026-09-24 12:34:44
Also in:
bpf, linux-arm-kernel
Subsystem:
networking drivers, stmmac ethernet driver, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Maxime Chevallier, Linus Torvalds
Extend stmmac_xdp_xmit_xdpf() to transmit XDP frames with fragments (multi-buff). Each buffer, i.e. the frame head and every frag, is mapped and programmed into a dedicated TX descriptor. Signed-off-by: Lorenzo Bianconi <redacted> --- Changes in v2: - Fix Tx coalescing logic in stmmac_xdp_xmit_xdpf() - Consider the max between STMMAC_TX_THRESH() and num_frames as threshold in stmmac_xdp_xmit_xdpf(). - Link to v1: https://lore.kernel.org/r/20260923-b4-stmmac-xmit-mb-v1-1-a71a1c522142@oss.qualcomm.com (local) --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 126 ++++++++++++++-------- 1 file changed, 83 insertions(+), 43 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index bf9e7e4cb1c3..089281ef9e61 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c@@ -5239,77 +5239,117 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, struct xdp_frame *xdpf, bool dma_map) { - struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; + struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported; + unsigned int txq_thr = STMMAC_TX_THRESH(priv); + unsigned int first_entry = tx_q->cur_tx; unsigned int entry = tx_q->cur_tx; - enum stmmac_txbuf_type buf_type; - struct dma_desc *tx_desc; - dma_addr_t dma_addr; - bool set_ic; + unsigned int num_frames = 1; + struct dma_desc *desc; + int i = 0; - if (stmmac_tx_avail(priv, queue) < STMMAC_TX_THRESH(priv)) + if (unlikely(xdp_frame_has_frags(xdpf))) + num_frames += sinfo->nr_frags; + + if (stmmac_tx_avail(priv, queue) < max(num_frames, txq_thr)) return STMMAC_XDP_CONSUMED; if (priv->est && priv->est->enable && priv->est->max_sdu[queue] && - xdpf->len > priv->est->max_sdu[queue]) { + xdp_get_frame_len(xdpf) > priv->est->max_sdu[queue]) { priv->xstats.max_sdu_txq_drop[queue]++; return STMMAC_XDP_CONSUMED; } - tx_desc = stmmac_get_tx_desc(priv, tx_q, entry); - if (dma_map) { - dma_addr = dma_map_single(priv->device, xdpf->data, - xdpf->len, DMA_TO_DEVICE); - if (dma_mapping_error(priv->device, dma_addr)) - return STMMAC_XDP_CONSUMED; - - buf_type = STMMAC_TXBUF_T_XDP_NDO; - } else { - struct page *page = virt_to_page(xdpf->data); - - dma_addr = page_pool_get_dma_addr(page) + sizeof(*xdpf) + - xdpf->headroom; - dma_sync_single_for_device(priv->device, dma_addr, - xdpf->len, DMA_BIDIRECTIONAL); + while (true) { + skb_frag_t *frag = i ? &sinfo->frags[i - 1] : NULL; + int len = frag ? skb_frag_size(frag) : xdpf->len; + bool last_frame = i == num_frames - 1; + enum stmmac_txbuf_type buf_type; + dma_addr_t dma_addr; - buf_type = STMMAC_TXBUF_T_XDP_TX; - } + desc = stmmac_get_tx_desc(priv, tx_q, entry); + if (dma_map) { + if (frag) + dma_addr = skb_frag_dma_map(priv->device, + frag, 0, len, + DMA_TO_DEVICE); + else + dma_addr = dma_map_single(priv->device, + xdpf->data, len, + DMA_TO_DEVICE); + if (dma_mapping_error(priv->device, dma_addr)) + goto error_dma_unmap; - stmmac_set_tx_dma_entry(tx_q, entry, buf_type, dma_addr, xdpf->len, - false); - stmmac_set_tx_dma_last_segment(tx_q, entry); + buf_type = STMMAC_TXBUF_T_XDP_NDO; + } else { + struct page *page; - tx_q->xdpf[entry] = xdpf; + page = frag ? skb_frag_page(frag) + : virt_to_page(xdpf->data); + dma_addr = page_pool_get_dma_addr(page); + if (frag) + dma_addr += skb_frag_off(frag); + else + dma_addr += sizeof(*xdpf) + xdpf->headroom; + dma_sync_single_for_device(priv->device, dma_addr, + len, DMA_BIDIRECTIONAL); + buf_type = STMMAC_TXBUF_T_XDP_TX; + } - stmmac_set_desc_addr(priv, tx_desc, dma_addr); + stmmac_set_tx_dma_entry(tx_q, entry, buf_type, dma_addr, len, + dma_map && frag); + stmmac_set_desc_addr(priv, desc, dma_addr); + stmmac_prepare_tx_desc(priv, desc, !i, len, csum, + priv->descriptor_mode, !!i, last_frame, + xdp_get_frame_len(xdpf)); + tx_q->xdpf[entry] = last_frame ? xdpf : NULL; + if (last_frame) { + stmmac_set_tx_dma_last_segment(tx_q, entry); + break; + } - stmmac_prepare_tx_desc(priv, tx_desc, 1, xdpf->len, - csum, priv->descriptor_mode, true, true, - xdpf->len); + entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); + i++; + } + tx_q->tx_count_frames += num_frames; - tx_q->tx_count_frames++; + if (priv->tx_coal_frames[queue] && + (tx_q->tx_count_frames % priv->tx_coal_frames[queue]) < num_frames) { + struct stmmac_txq_stats *txq_stats; - if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0) - set_ic = true; - else - set_ic = false; - - if (set_ic) { + desc = stmmac_get_tx_desc(priv, tx_q, entry); + stmmac_set_tx_ic(priv, desc); tx_q->tx_count_frames = 0; - stmmac_set_tx_ic(priv, tx_desc); + + txq_stats = &priv->xstats.txq_stats[queue]; u64_stats_update_begin(&txq_stats->q_syncp); u64_stats_inc(&txq_stats->q.tx_set_ic_bit); u64_stats_update_end(&txq_stats->q_syncp); } + /* Set the OWN bit on the first descriptor now that all descriptors + * for this skb are populated. + */ + desc = stmmac_get_tx_desc(priv, tx_q, first_entry); + stmmac_set_tx_owner(priv, desc); + tx_q->cur_tx = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); stmmac_enable_dma_transmission(priv, priv->ioaddr, queue); - entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size); - tx_q->cur_tx = entry; - return STMMAC_XDP_TX; + +error_dma_unmap: + while (first_entry != entry) { + desc = stmmac_get_tx_desc(priv, tx_q, first_entry); + stmmac_release_tx_desc(priv, desc, priv->descriptor_mode); + stmmac_free_tx_buffer(priv, &priv->dma_conf, queue, + first_entry); + first_entry = STMMAC_NEXT_ENTRY(first_entry, + priv->dma_conf.dma_tx_size); + } + + return STMMAC_XDP_CONSUMED; } static int stmmac_xdp_get_tx_queue(struct stmmac_priv *priv,
--- base-commit: 1c85ea5df86bed136589a2866c22f394020d3fdf change-id: 20260922-b4-stmmac-xmit-mb-bb78e0684cf4 Best regards, -- Lorenzo Bianconi [off-list ref]