[PATCH net-next] net: stmmac: add XDP multi-buff support for TX side
Lorenzo Bianconi
lorenzo.bianconi at oss.qualcomm.com
Wed Sep 23 02:58:48 PDT 2026
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 <lorenzo.bianconi at oss.qualcomm.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 123 ++++++++++++++--------
1 file changed, 80 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..41ccde6371ec 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5239,77 +5239,114 @@ 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 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;
+ int i = 0, num_frames = 1;
+ struct dma_desc *desc;
- 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) < num_frames)
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 (!(tx_q->tx_count_frames % priv->tx_coal_frames[queue])) {
+ 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: 944ae66642b726bd6b25ae71b1e9ff88a0e0bdb0
change-id: 20260922-b4-stmmac-xmit-mb-bb78e0684cf4
Best regards,
--
Lorenzo Bianconi <lorenzo.bianconi at oss.qualcomm.com>
More information about the linux-arm-kernel
mailing list