From: Nikolai Semendiaev <agent00791@gmail.com>
To: ath12k@lists.infradead.org
Cc: linux-wireless@vger.kernel.org, jjohnson@kernel.org, wabsie@gmail.com
Subject: [PATCH v2 2/2] wifi: ath12k: use private page-frag caches for RXDMA rings
Date: Thu, 8 Oct 2026 03:38:29 +0600 [thread overview]
Message-ID: <bd52182ca4916b55632a710be2f5c9cfd25f07a2.1791408913.git.agent00791@gmail.com> (raw)
In-Reply-To: <cover.1791408913.git.agent00791@gmail.com>
RXDMA buffers allocated with dev_alloc_skb() share per-CPU page-fragment
caches with other network users. Long-lived ring buffers can pin mostly
unused 32-KiB backing pages as short-lived network allocations cycle
through those caches. On QCN9274, OOM was observed during sustained WAN
downloads when Ethernet and ath12k processing shared a CPU.
Give regular and monitor RXDMA rings private page-fragment caches. The
existing refill SRNG locks serialize cache access. Preserve NET_SKB_PAD,
allocation flags and the DMA mapping and unmapping operations.
Drain each monitor cache after releasing its IDR-owned buffers, and the
regular RX cache after releasing its cookie-conversion descriptors.
Disable external IRQ/NAPI processing before non-reset crash teardown;
the reset worker already does so.
Use skb_reserve() for DMA alignment because skb_pull() cannot advance
an empty skb. Allocate extra alignment space for monitor status buffers
to preserve at least RX_MON_STATUS_BUF_SIZE of DMA capacity.
Based on Mark Ruvald Pedersen's ath11k per-ring page-fragment isolation.
Link: https://lore.kernel.org/linux-wireless/20260719215817.2468580-4-wabsie@gmail.com/
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Signed-off-by: Nikolai Semendiaev <agent00791@gmail.com>
---
drivers/net/wireless/ath/ath12k/core.c | 3 ++
drivers/net/wireless/ath/ath12k/dp.c | 4 +++
drivers/net/wireless/ath/ath12k/dp.h | 3 ++
drivers/net/wireless/ath/ath12k/dp_mon.c | 29 +++++++++------
drivers/net/wireless/ath/ath12k/dp_rx.c | 45 +++++++++++++++++++++---
drivers/net/wireless/ath/ath12k/dp_rx.h | 2 ++
6 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 0c72948f3..8216f344e 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1390,6 +1390,9 @@ static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab)
{
int ret, total_vdev;
+ if (!ab->is_reset)
+ ath12k_hif_irq_disable(ab);
+
mutex_lock(&ab->core_lock);
ath12k_link_sta_rhash_tbl_destroy(ab);
ath12k_thermal_unregister(ab);
diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c
index 83f9edfab..2f45ade5a 100644
--- a/drivers/net/wireless/ath/ath12k/dp.c
+++ b/drivers/net/wireless/ath/ath12k/dp.c
@@ -4,6 +4,8 @@
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
+#include <linux/bug.h>
+#include <linux/page_frag_cache.h>
#include "core.h"
#include "dp_tx.h"
#include "hif.h"
@@ -1020,6 +1022,8 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab)
spin_unlock_bh(&dp->rx_desc_lock);
+ page_frag_cache_drain(&dp->rx_refill_buf_ring.frag_cache);
+
/* TX Descriptor cleanup */
for (i = 0; i < ATH12K_HW_MAX_QUEUES; i++) {
spin_lock_bh(&dp->tx_desc_lock[i]);
diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h
index c53eac20b..0b68bd8ba 100644
--- a/drivers/net/wireless/ath/ath12k/dp.h
+++ b/drivers/net/wireless/ath/ath12k/dp.h
@@ -11,6 +11,7 @@
#include "hw.h"
#include "dp_htt.h"
#include "dp_cmn.h"
+#include <linux/mm_types.h>
#include <linux/rhashtable.h>
#define MAX_RXDMA_PER_PDEV 2
@@ -51,6 +52,7 @@ struct dp_srng {
struct dp_rxdma_mon_ring {
struct dp_srng refill_buf_ring;
+ struct page_frag_cache frag_cache;
struct idr bufs_idr;
/* Protects bufs_idr */
spinlock_t idr_lock;
@@ -59,6 +61,7 @@ struct dp_rxdma_mon_ring {
struct dp_rxdma_ring {
struct dp_srng refill_buf_ring;
+ struct page_frag_cache frag_cache;
int bufs_max;
};
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 709507d44..8b2b81b46 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -5,6 +5,7 @@
*/
#include "dp_mon.h"
+#include "dp_rx.h"
#include "debug.h"
#include "dp_tx.h"
#include "peer.h"
@@ -44,15 +45,17 @@ struct sk_buff
struct sk_buff *skb;
dma_addr_t paddr;
- skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE);
+ skb = ath12k_dp_rx_alloc_skb(&rx_ring->frag_cache,
+ RX_MON_STATUS_BUF_SIZE +
+ RX_MON_STATUS_BUF_ALIGN);
if (!skb)
goto fail_alloc_skb;
if (!IS_ALIGNED((unsigned long)skb->data,
RX_MON_STATUS_BUF_ALIGN)) {
- skb_pull(skb, PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) -
- skb->data);
+ skb_reserve(skb, PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) -
+ skb->data);
}
paddr = dma_map_single(ab->dev, skb->data,
@@ -598,14 +601,16 @@ int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab,
ath12k_hal_srng_access_begin(ab, srng);
while (req_entries > 0) {
- skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + DP_RX_BUFFER_ALIGN_SIZE);
+ skb = ath12k_dp_rx_alloc_skb(&buf_ring->frag_cache,
+ DP_RX_BUFFER_SIZE +
+ DP_RX_BUFFER_ALIGN_SIZE);
if (unlikely(!skb))
goto fail_alloc_skb;
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);
+ skb_reserve(skb,
+ PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) -
+ skb->data);
}
paddr = dma_map_single(ab->dev, skb->data,
@@ -687,15 +692,17 @@ int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab,
num_remain = req_entries;
while (num_remain > 0) {
- skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE);
+ skb = ath12k_dp_rx_alloc_skb(&rx_ring->frag_cache,
+ RX_MON_STATUS_BUF_SIZE +
+ RX_MON_STATUS_BUF_ALIGN);
if (!skb)
break;
if (!IS_ALIGNED((unsigned long)skb->data,
RX_MON_STATUS_BUF_ALIGN)) {
- skb_pull(skb,
- PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) -
- skb->data);
+ skb_reserve(skb,
+ PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) -
+ skb->data);
}
paddr = dma_map_single(ab->dev, skb->data,
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index cfaf26ae2..a0ea0d51e 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -5,9 +5,12 @@
*/
#include <linux/fips.h>
+#include <linux/gfp.h>
#include <linux/ieee80211.h>
#include <linux/kernel.h>
+#include <linux/page_frag_cache.h>
#include <linux/skbuff.h>
+#include <net/sock.h>
#include "core.h"
#include "debug.h"
#include "hw.h"
@@ -68,6 +71,35 @@ static void ath12k_dp_rx_enqueue_free(struct ath12k_dp *dp,
spin_unlock_bh(&dp->rx_desc_lock);
}
+struct sk_buff *ath12k_dp_rx_alloc_skb(struct page_frag_cache *cache,
+ unsigned int len)
+{
+ gfp_t gfp_mask = GFP_ATOMIC | __GFP_NOWARN;
+ struct sk_buff *skb;
+ void *data;
+
+ len += NET_SKB_PAD;
+ len = SKB_HEAD_ALIGN(len);
+
+ if (sk_memalloc_socks())
+ gfp_mask |= __GFP_MEMALLOC;
+
+ /* Refill paths serialize the per-ring cache with the SRNG lock. */
+ data = page_frag_alloc(cache, len, gfp_mask);
+ if (!data)
+ return NULL;
+
+ skb = build_skb(data, len);
+ if (!skb) {
+ page_frag_free(data);
+ return NULL;
+ }
+
+ skb_reserve(skb, NET_SKB_PAD);
+
+ return skb;
+}
+
/* Returns number of Rx buffers replenished */
int ath12k_dp_rx_bufs_replenish(struct ath12k_dp *dp,
struct dp_rxdma_ring *rx_ring,
@@ -114,16 +146,17 @@ 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_alloc_skb(&rx_ring->frag_cache,
+ DP_RX_BUFFER_SIZE +
+ DP_RX_BUFFER_ALIGN_SIZE);
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);
+ skb_reserve(skb,
+ PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) -
+ skb->data);
}
paddr = dma_map_single(dp->dev, skb->data,
@@ -193,6 +226,8 @@ static int ath12k_dp_rxdma_mon_buf_ring_free(struct ath12k_base *ab,
idr_destroy(&rx_ring->bufs_idr);
spin_unlock_bh(&rx_ring->idr_lock);
+ page_frag_cache_drain(&rx_ring->frag_cache);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h
index 827004745..99b989b69 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.h
@@ -232,6 +232,8 @@ int ath12k_dp_rx_bufs_replenish(struct ath12k_dp *dp,
struct dp_rxdma_ring *rx_ring,
struct list_head *used_list,
int req_entries);
+struct sk_buff *ath12k_dp_rx_alloc_skb(struct page_frag_cache *cache,
+ unsigned int len);
int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar);
int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id);
--
2.43.0
next prev parent reply other threads:[~2026-10-07 21:39 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 21:38 [PATCH v2 0/2] wifi: ath12k: isolate RXDMA page-fragment caches Nikolai Semendiaev
2026-10-07 21:38 ` [PATCH v2 1/2] wifi: ath12k: make external IRQ control idempotent Nikolai Semendiaev
2026-10-09 10:15 ` Baochen Qiang
2026-10-09 11:03 ` Nickolay Semendyaev
2026-10-07 21:38 ` Nikolai Semendiaev [this message]
2026-10-09 14:35 ` [PATCH v2 2/2] wifi: ath12k: use private page-frag caches for RXDMA rings Jeff Johnson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bd52182ca4916b55632a710be2f5c9cfd25f07a2.1791408913.git.agent00791@gmail.com \
--to=agent00791@gmail.com \
--cc=ath12k@lists.infradead.org \
--cc=jjohnson@kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=wabsie@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox