[PATCH 3/3] wifi: ath12k: rely on page_pool APIs for buffer allocation

Lorenzo Bianconi lorenzo.bianconi at oss.qualcomm.com
Thu Oct 8 06:16:00 PDT 2026


Convert the Rx refill buffers to the page_pool API instead of allocating
an skb with dev_alloc_skb() and mapping it with dma_map_single(). The pool
handles the DMA mapping and buffer recycling.

Add a page_pool to struct dp_rxdma_ring, created in
ath12k_dp_rxdma_ring_buf_setup() and destroyed in
ath12k_dp_rxdma_ring_buf_cleanup().

Update ath12k_dp_unmap_rx_buf() to sync page_pool buffers for the CPU
instead of unmapping them, and guard the teardown paths
(ath12k_dp_cc_cleanup(), ath12k_dp_h_msdu_buffer_type()) against
page_pool buffers. In the re-inject path, sync an already mapped
page_pool buffer for the device instead of mapping it again, and fill the
descriptor with the DMA address stored in the rxcb.

The monitor, CE and TX paths are left unchanged.

This is just preliminary support: the goal is to use a single page to map
two separated fragments of 2048B.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi at oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/Kconfig       |   1 +
 drivers/net/wireless/ath/ath12k/dp.h          |   1 +
 drivers/net/wireless/ath/ath12k/dp_rx.c       | 101 ++++++++++++++++++--------
 drivers/net/wireless/ath/ath12k/dp_rx.h       |  32 ++++++--
 drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c |  34 ++++++---
 5 files changed, 121 insertions(+), 48 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig
index 0d5d1c55bfc1..13985daffaee 100644
--- a/drivers/net/wireless/ath/ath12k/Kconfig
+++ b/drivers/net/wireless/ath/ath12k/Kconfig
@@ -7,6 +7,7 @@ config ATH12K
 	select QRTR
 	select QRTR_MHI
 	select PCI_PWRCTRL_PWRSEQ if HAVE_PWRCTRL
+	select PAGE_POOL
 	help
 	  Enable support for Qualcomm Technologies Wi-Fi 7 (IEEE
 	  802.11be) family of chipsets, for example WCN7850 and
diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h
index c53eac20b989..599ce3c45c7a 100644
--- a/drivers/net/wireless/ath/ath12k/dp.h
+++ b/drivers/net/wireless/ath/ath12k/dp.h
@@ -59,6 +59,7 @@ struct dp_rxdma_mon_ring {
 
 struct dp_rxdma_ring {
 	struct dp_srng refill_buf_ring;
+	struct page_pool *page_pool;
 	int bufs_max;
 };
 
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index cfaf26ae2720..de9ea84323df 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -68,6 +68,31 @@ static void ath12k_dp_rx_enqueue_free(struct ath12k_dp *dp,
 	spin_unlock_bh(&dp->rx_desc_lock);
 }
 
+static struct sk_buff *ath12k_dp_rx_build_skb(struct ath12k_dp *dp,
+					      struct dp_rxdma_ring *rx_ring)
+{
+	struct sk_buff *skb;
+	struct page *page;
+
+	if (!rx_ring->page_pool)
+		return NULL;
+
+	page = page_pool_dev_alloc_pages(rx_ring->page_pool);
+	if (!page)
+		return NULL;
+
+	skb = build_skb(page_address(page), PAGE_SIZE);
+	if (!skb) {
+		page_pool_put_full_page(rx_ring->page_pool, page, false);
+		return NULL;
+	}
+
+	ATH12K_SKB_RXCB(skb)->paddr = page_pool_get_dma_addr(page);
+	skb_mark_for_recycle(skb);
+
+	return skb;
+}
+
 /* Returns number of Rx buffers replenished */
 int ath12k_dp_rx_bufs_replenish(struct ath12k_dp *dp,
 				struct dp_rxdma_ring *rx_ring,
@@ -81,7 +106,6 @@ int ath12k_dp_rx_bufs_replenish(struct ath12k_dp *dp,
 	int num_free;
 	int num_remain;
 	u32 cookie;
-	dma_addr_t paddr;
 	struct ath12k_rx_desc_info *rx_desc;
 	enum hal_rx_buf_return_buf_manager mgr = dp->hal->hal_params->rx_buf_rbm;
 
@@ -114,51 +138,33 @@ int ath12k_dp_rx_bufs_replenish(struct ath12k_dp *dp,
 	}
 
 	while (num_remain > 0) {
-		skb = dev_alloc_skb(DP_RX_BUFFER_SIZE +
-				    DP_RX_BUFFER_ALIGN_SIZE);
+		skb = ath12k_dp_rx_build_skb(dp, rx_ring);
 		if (!skb)
 			break;
 
-		if (!IS_ALIGNED((unsigned long)skb->data,
-				DP_RX_BUFFER_ALIGN_SIZE)) {
-			skb_pull(skb,
-				 PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) -
-				 skb->data);
-		}
-
-		paddr = dma_map_single(dp->dev, skb->data,
-				       skb->len + skb_tailroom(skb),
-				       DMA_FROM_DEVICE);
-		if (dma_mapping_error(dp->dev, paddr))
-			goto fail_free_skb;
-
 		rx_desc = list_first_entry_or_null(used_list,
 						   struct ath12k_rx_desc_info,
 						   list);
 		if (!rx_desc)
-			goto fail_dma_unmap;
-
-		rx_desc->skb = skb;
-		cookie = rx_desc->cookie;
+			goto fail_free_skb;
 
 		desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
 		if (!desc)
-			goto fail_dma_unmap;
+			goto fail_free_skb;
 
-		list_del(&rx_desc->list);
-		ATH12K_SKB_RXCB(skb)->paddr = paddr;
+		rx_desc->skb = skb;
+		cookie = rx_desc->cookie;
 
+		list_del(&rx_desc->list);
 		num_remain--;
 
-		ath12k_hal_rx_buf_addr_info_set(dp->hal, desc, paddr, cookie,
-						mgr);
+		ath12k_hal_rx_buf_addr_info_set(dp->hal, desc,
+						ATH12K_SKB_RXCB(skb)->paddr,
+						cookie, mgr);
 	}
 
 	goto out;
 
-fail_dma_unmap:
-	dma_unmap_single(dp->dev, paddr, skb->len + skb_tailroom(skb),
-			 DMA_FROM_DEVICE);
 fail_free_skb:
 	dev_kfree_skb_any(skb);
 out:
@@ -196,12 +202,21 @@ static int ath12k_dp_rxdma_mon_buf_ring_free(struct ath12k_base *ab,
 	return 0;
 }
 
+static void ath12k_dp_rxdma_ring_buf_cleanup(struct dp_rxdma_ring *rx_ring)
+{
+	if (rx_ring->page_pool) {
+		page_pool_destroy(rx_ring->page_pool);
+		rx_ring->page_pool = NULL;
+	}
+}
+
 static int ath12k_dp_rxdma_buf_free(struct ath12k_base *ab)
 {
 	struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
 	int i;
 
 	ath12k_dp_rxdma_mon_buf_ring_free(ab, &dp->rxdma_mon_buf_ring);
+	ath12k_dp_rxdma_ring_buf_cleanup(&dp->rx_refill_buf_ring);
 
 	if (ab->hw_params->rxdma1_enable)
 		return 0;
@@ -236,11 +251,28 @@ static int ath12k_dp_rxdma_mon_ring_buf_setup(struct ath12k_base *ab,
 static int ath12k_dp_rxdma_ring_buf_setup(struct ath12k_base *ab,
 					  struct dp_rxdma_ring *rx_ring)
 {
+	struct page_pool_params pp_params = {
+		.order = 0,
+		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+		.nid = NUMA_NO_NODE,
+		.dma_dir = DMA_BIDIRECTIONAL,
+		.max_len = PAGE_SIZE,
+		.dev = ab->dev,
+		.offset = 0,
+	};
 	LIST_HEAD(list);
 
 	rx_ring->bufs_max = rx_ring->refill_buf_ring.size /
 			ath12k_hal_srng_get_entrysize(ab, HAL_RXDMA_BUF);
 
+	rx_ring->page_pool = page_pool_create(&pp_params);
+	if (IS_ERR(rx_ring->page_pool)) {
+		int err = PTR_ERR(rx_ring->page_pool);
+
+		rx_ring->page_pool = NULL;
+		return err;
+	}
+
 	ath12k_dp_rx_bufs_replenish(ath12k_ab_to_dp(ab), rx_ring, &list, 0);
 
 	return 0;
@@ -263,10 +295,13 @@ static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab)
 		ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab,
 							 &dp->rxdma_mon_buf_ring,
 							 HAL_RXDMA_MONITOR_BUF);
-		if (ret)
+		if (ret) {
 			ath12k_warn(ab,
 				    "failed to setup HAL_RXDMA_MONITOR_BUF\n");
-		return ret;
+			goto error;
+		}
+
+		return 0;
 	}
 
 	for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
@@ -276,11 +311,15 @@ static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab)
 		if (ret) {
 			ath12k_warn(ab,
 				    "failed to setup HAL_RXDMA_MONITOR_STATUS\n");
-			return ret;
+			goto error;
 		}
 	}
 
 	return 0;
+error:
+	ath12k_dp_rxdma_ring_buf_cleanup(&dp->rx_refill_buf_ring);
+
+	return ret;
 }
 
 static void ath12k_dp_rx_pdev_srng_free(struct ath12k *ar)
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h
index c3715c99054f..a1113f8b7399 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.h
@@ -6,6 +6,7 @@
 #ifndef ATH12K_DP_RX_H
 #define ATH12K_DP_RX_H
 
+#include <net/page_pool/helpers.h>
 #include "core.h"
 #include "debug.h"
 
@@ -187,15 +188,32 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal,
 	hal->ops->extract_rx_desc_data(rx_info, rx_desc, ldesc);
 }
 
-static inline void ath12k_dp_unmap_rx_buf(struct ath12k_dp *dp,
-					  struct sk_buff *skb)
+static inline bool ath12k_skb_from_page_pool(struct sk_buff *skb)
 {
-	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(skb);
-	enum dma_data_direction dir;
+	if (!skb->head_frag || !skb->pp_recycle)
+		return false;
 
-	dir = rxcb->reinjected ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
-	dma_unmap_single(dp->dev, rxcb->paddr, skb->len + skb_tailroom(skb),
-			 dir);
+	return page_pool_page_is_pp(virt_to_head_page(skb->head));
+}
+
+static inline void ath12k_dp_unmap_rx_buf(struct ath12k_dp *dp, struct sk_buff *skb)
+{
+	if (likely(ath12k_skb_from_page_pool(skb))) {
+		struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
+		struct page *page = virt_to_head_page(skb->data);
+		int offset = (void *)skb->data - page_address(page);
+
+		page_pool_dma_sync_for_cpu(rx_ring->page_pool, page,
+					   offset,
+					   skb->len + skb_tailroom(skb));
+	} else {
+		struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(skb);
+		enum dma_data_direction dir;
+
+		dir = rxcb->reinjected ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
+		dma_unmap_single(dp->dev, rxcb->paddr,
+				 skb->len + skb_tailroom(skb), dir);
+	}
 }
 
 void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
index 9cd9c0bb07db..d1703c736d49 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
@@ -902,14 +902,26 @@ static int ath12k_wifi7_dp_rx_h_defrag_reo_reinject(struct ath12k_dp *dp,
 	/* change msdu len in hal rx desc */
 	ath12k_dp_rxdesc_set_msdu_len(hal, rx_desc, defrag_skb->len - hal_rx_desc_sz);
 
-	buf_paddr = dma_map_single(dp->dev, defrag_skb->data,
-				   defrag_skb->len + skb_tailroom(defrag_skb),
-				   DMA_BIDIRECTIONAL);
-	if (dma_mapping_error(dp->dev, buf_paddr))
-		return -ENOMEM;
+	if (ath12k_skb_from_page_pool(defrag_skb)) {
+		struct dp_rxdma_ring *rx_ring;
+
+		rx_ring = &dp->rx_refill_buf_ring;
+		dma_sync_single_for_device(dp->dev,
+					   ATH12K_SKB_RXCB(defrag_skb)->paddr,
+					   defrag_skb->len +
+					   skb_tailroom(defrag_skb),
+					   rx_ring->page_pool->p.dma_dir);
+	} else {
+		buf_paddr = dma_map_single(dp->dev, defrag_skb->data,
+					   defrag_skb->len +
+					   skb_tailroom(defrag_skb),
+					   DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(dp->dev, buf_paddr))
+			return -ENOMEM;
 
+		rxcb->paddr = buf_paddr;
+	}
 	rxcb->reinjected = true;
-	rxcb->paddr = buf_paddr;
 
 	spin_lock_bh(&dp->rx_desc_lock);
 	desc_info = list_first_entry_or_null(&dp->rx_desc_free_list,
@@ -928,8 +940,8 @@ static int ath12k_wifi7_dp_rx_h_defrag_reo_reinject(struct ath12k_dp *dp,
 	list_del(&desc_info->list);
 	spin_unlock_bh(&dp->rx_desc_lock);
 
-	ath12k_wifi7_hal_rx_buf_addr_info_set(&msdu0->buf_addr_info, buf_paddr,
-					      desc_info->cookie,
+	ath12k_wifi7_hal_rx_buf_addr_info_set(&msdu0->buf_addr_info,
+					      rxcb->paddr, desc_info->cookie,
 					      HAL_RX_BUF_RBM_SW3_BM);
 
 	/* Fill mpdu details into reo entrance ring */
@@ -994,8 +1006,10 @@ static int ath12k_wifi7_dp_rx_h_defrag_reo_reinject(struct ath12k_dp *dp,
 	list_add_tail(&desc_info->list, &dp->rx_desc_free_list);
 	spin_unlock_bh(&dp->rx_desc_lock);
 err_unmap_dma:
-	dma_unmap_single(dp->dev, buf_paddr, defrag_skb->len + skb_tailroom(defrag_skb),
-			 DMA_BIDIRECTIONAL);
+	if (!ath12k_skb_from_page_pool(defrag_skb))
+		dma_unmap_single(dp->dev, ATH12K_SKB_RXCB(defrag_skb)->paddr,
+				 defrag_skb->len + skb_tailroom(defrag_skb),
+				 DMA_BIDIRECTIONAL);
 	return ret;
 }
 

-- 
2.56.0




More information about the ath12k mailing list