[PATCH net-next 2/3] net: stmmac: Use pagepool instead of streaming buffers for Tx
muhammad.nazim.amirul.nazle.asmade at altera.com
muhammad.nazim.amirul.nazle.asmade at altera.com
Mon Aug 3 00:05:12 PDT 2026
From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade at altera.com>
Use page pool buffers instead of streaming skb buffers in stmmac_xmit
when snps,pagepool-tx-buf-quirk is enabled. As page pool buffers are
recycled, the overhead of dma map/unmap for each transmit can be
avoided. There is a trade-off of copying from the skb into the page
pool buffer, but this helps where the copy is cheaper than dma
map/unmap (for example with IOMMU invalidate cost).
Currently page pool buffers are only used for single-fragment transmit
in the non-TSO path.
Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade at altera.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 3 +
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 75 ++++++++++++++++---
.../ethernet/stmicro/stmmac/stmmac_platform.c | 2 +
include/linux/stmmac.h | 1 +
4 files changed, 70 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 8ba8f03e1ce0..093c052b4908 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -41,6 +41,7 @@ struct stmmac_resources {
enum stmmac_txbuf_type {
STMMAC_TXBUF_T_SKB,
+ STMMAC_TXBUF_T_DMA,
STMMAC_TXBUF_T_XDP_TX,
STMMAC_TXBUF_T_XDP_NDO,
STMMAC_TXBUF_T_XSK_TX,
@@ -49,6 +50,7 @@ enum stmmac_txbuf_type {
struct stmmac_tx_info {
struct xsk_tx_metadata_compl xsk_meta;
dma_addr_t buf;
+ struct page *page;
unsigned len;
enum stmmac_txbuf_type buf_type;
bool map_as_page;
@@ -65,6 +67,7 @@ struct stmmac_tx_queue {
int tbs;
struct hrtimer txtimer;
u32 queue_index;
+ struct page_pool *page_pool;
struct stmmac_priv *priv_data;
struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp;
struct dma_edesc *dma_entx;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index ee44bd6f4d48..4aa6b1d1507b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1712,7 +1712,10 @@ static void stmmac_free_tx_buffer(struct stmmac_priv *priv,
if (tx_q->tx_skbuff_dma[i].buf &&
tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) {
- if (tx_q->tx_skbuff_dma[i].map_as_page)
+ if (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_DMA)
+ page_pool_recycle_direct(tx_q->page_pool,
+ tx_q->tx_skbuff_dma[i].page);
+ else if (tx_q->tx_skbuff_dma[i].map_as_page)
dma_unmap_page(priv->device,
tx_q->tx_skbuff_dma[i].buf,
tx_q->tx_skbuff_dma[i].len,
@@ -1735,7 +1738,8 @@ static void stmmac_free_tx_buffer(struct stmmac_priv *priv,
tx_q->xsk_frames_done++;
if (tx_q->tx_skbuff[i] &&
- tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_SKB) {
+ (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_SKB ||
+ tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_DMA)) {
dev_kfree_skb_any(tx_q->tx_skbuff[i]);
tx_q->tx_skbuff[i] = NULL;
}
@@ -2201,6 +2205,8 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv,
kfree(tx_q->tx_skbuff_dma);
kfree(tx_q->tx_skbuff);
+ if (tx_q->page_pool)
+ page_pool_destroy(tx_q->page_pool);
}
static void free_dma_tx_desc_resources(struct stmmac_priv *priv,
@@ -2335,12 +2341,32 @@ static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv,
u32 queue)
{
struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue];
+ struct page_pool_params pp_params = { 0 };
+ unsigned int num_pages;
size_t size;
void *addr;
+ int ret;
tx_q->queue_index = queue;
tx_q->priv_data = priv;
+ pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+ pp_params.pool_size = dma_conf->dma_tx_size;
+ num_pages = DIV_ROUND_UP(dma_conf->dma_buf_sz, PAGE_SIZE);
+ pp_params.order = ilog2(num_pages);
+ pp_params.nid = dev_to_node(priv->device);
+ pp_params.dev = priv->device;
+ pp_params.dma_dir = DMA_BIDIRECTIONAL;
+ pp_params.offset = 0;
+ pp_params.max_len = num_pages * PAGE_SIZE;
+
+ tx_q->page_pool = page_pool_create(&pp_params);
+ if (IS_ERR(tx_q->page_pool)) {
+ ret = PTR_ERR(tx_q->page_pool);
+ tx_q->page_pool = NULL;
+ return ret;
+ }
+
tx_q->tx_skbuff_dma = kzalloc_objs(*tx_q->tx_skbuff_dma,
dma_conf->dma_tx_size);
if (!tx_q->tx_skbuff_dma)
@@ -2836,7 +2862,8 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XDP_NDO) {
xdpf = tx_q->xdpf[entry];
skb = NULL;
- } else if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_SKB) {
+ } else if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_SKB ||
+ tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_DMA) {
xdpf = NULL;
skb = tx_q->tx_skbuff[entry];
} else {
@@ -2884,7 +2911,10 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
if (likely(tx_q->tx_skbuff_dma[entry].buf &&
tx_q->tx_skbuff_dma[entry].buf_type != STMMAC_TXBUF_T_XDP_TX)) {
- if (tx_q->tx_skbuff_dma[entry].map_as_page)
+ if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_DMA)
+ page_pool_recycle_direct(tx_q->page_pool,
+ tx_q->tx_skbuff_dma[entry].page);
+ else if (tx_q->tx_skbuff_dma[entry].map_as_page)
dma_unmap_page(priv->device,
tx_q->tx_skbuff_dma[entry].buf,
tx_q->tx_skbuff_dma[entry].len,
@@ -2922,7 +2952,8 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XSK_TX)
tx_q->xsk_frames_done++;
- if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_SKB) {
+ if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_SKB ||
+ tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_DMA) {
if (likely(skb)) {
pkts_compl++;
bytes_compl += skb->len;
@@ -4742,12 +4773,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
bool enh_desc, has_vlan, set_ic, is_jumbo = false;
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int nopaged_len = skb_headlen(skb);
+ gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN;
u32 queue = skb_get_queue_mapping(skb);
int nfrags = skb_shinfo(skb)->nr_frags;
unsigned int first_entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct dma_desc *desc, *first_desc;
struct stmmac_tx_queue *tx_q;
+ struct page *tx_buf_page;
int i, csum_insertion = 0;
int entry, first_tx;
dma_addr_t dma_addr;
@@ -4814,13 +4847,33 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
} else {
bool last_segment = (nfrags == 0);
- dma_addr = dma_map_single(priv->device, skb->data,
- nopaged_len, DMA_TO_DEVICE);
- if (dma_mapping_error(priv->device, dma_addr))
- goto dma_map_err;
+ if (!(last_segment && priv->plat->tx_buf_quirk)) {
+ dma_addr = dma_map_single(priv->device, skb->data,
+ nopaged_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, dma_addr))
+ goto dma_map_err;
- stmmac_set_tx_skb_dma_entry(tx_q, first_entry, dma_addr,
- nopaged_len, false);
+ stmmac_set_tx_skb_dma_entry(tx_q, first_entry, dma_addr,
+ nopaged_len, false);
+ } else {
+ tx_buf_page = page_pool_alloc_pages(tx_q->page_pool, gfp);
+ if (!tx_buf_page)
+ goto dma_map_err;
+
+ dma_addr = page_pool_get_dma_addr(tx_buf_page);
+ stmmac_set_tx_dma_entry(tx_q, first_entry,
+ STMMAC_TXBUF_T_DMA, dma_addr,
+ nopaged_len, false);
+ tx_q->tx_skbuff_dma[first_entry].page = tx_buf_page;
+
+ dma_sync_single_for_cpu(priv->device, dma_addr,
+ nopaged_len, DMA_BIDIRECTIONAL);
+ skb_copy_from_linear_data(skb, page_address(tx_buf_page),
+ nopaged_len);
+ dma_sync_single_for_device(priv->device, dma_addr,
+ nopaged_len,
+ DMA_BIDIRECTIONAL);
+ }
stmmac_set_desc_addr(priv, first_desc, dma_addr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index dc5f951a311d..0f8d2fc61ce4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -575,6 +575,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed);
+ plat->tx_buf_quirk = of_property_read_bool(np, "snps,pagepool-tx-buf-quirk");
+
plat->axi = stmmac_axi_setup(pdev);
rc = stmmac_mtl_setup(pdev, plat);
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 4430b967abde..d4c9d77a6f81 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -268,6 +268,7 @@ struct plat_stmmacenet_data {
u8 tx_queues_to_use;
u8 rx_sched_algorithm;
u8 tx_sched_algorithm;
+ bool tx_buf_quirk;
struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES];
struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
void (*get_interfaces)(struct stmmac_priv *priv, void *bsp_priv,
--
2.43.7
More information about the linux-arm-kernel
mailing list