From: Mark Ruvald Pedersen <wabsie@gmail.com>
To: linux-wireless@vger.kernel.org
Cc: Jeff Johnson <jjohnson@kernel.org>, ath11k@lists.infradead.org
Subject: [RFC PATCH 3/3] wifi: ath11k: use private page-frag caches for RXDMA rings
Date: Sun, 19 Jul 2026 23:58:17 +0200 [thread overview]
Message-ID: <20260719215817.2468580-4-wabsie@gmail.com> (raw)
In-Reply-To: <20260719215817.2468580-1-wabsie@gmail.com>
ath11k allocates RXDMA skb heads with dev_alloc_skb(), which draws from
shared per-CPU page-frag caches. RX buffers from different rings and
unrelated networking allocations can therefore share a high-order
backing page despite having different ownership lifetimes.
On IPQ8074, RX ownership tracing found bounded ring and IDR occupancy
while page-frag backing grew. Two captures found current or historical
cross-origin fragments on 86-87% of the final tracker-visible physical
pages.
Give each RXDMA ring its own page_frag_cache and use it for regular and
monitor-status buffers. New fragments from distinct rings can no longer
share backing pages with one another or with unrelated users. The
monitor-destination path that intentionally uses the data refill ring
also uses that ring's cache.
Commit d455e805de70 ("wifi: ath11k: rearrange IRQ enable/disable in reset
path") moved HIF IRQ shutdown from the common crash-reconfiguration path
into the reset worker. The non-reset QMI recovery path can consequently
reach DP teardown while NAPI can still refill an RXDMA ring.
Quiesce HIF IRQ/NAPI processing on that non-reset path before teardown.
The reset worker already does so before power cycling, therefore keep the
existing reset path unchanged. This makes the cache change self-contained
without relying on repeated IRQ lifecycle transitions.
The refill paths serialize each cache with the corresponding SRNG lock.
Drain the cache only after ring-owned skbs have been DMA-unmapped and
freed and the IDR has been destroyed. Keep the dev_alloc_skb() allocation
flags and headroom behavior; build_skb() propagates head-frag and
pfmemalloc metadata from the backing page.
This leaves buffer and ring sizes and all DMA map/unmap operations
unchanged. It isolates cross-owner lifetime mixing; it does not prevent
lifetime variation among buffers belonging to the same ring.
With the private caches, page-frag backing remained bounded in natural
load, resident, and high-packet-rate pressure runs on IPQ8074. The
non-reset recovery path has not been runtime-tested in isolation.
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Fixes: d455e805de70 ("wifi: ath11k: rearrange IRQ enable/disable in reset path")
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1
Signed-off-by: Mark Ruvald Pedersen <wabsie@gmail.com>
---
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 8dacc878c006..e71a22ce332d 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -2330,6 +2330,9 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab)
{
int ret;
+ if (!ab->is_reset)
+ ath11k_hif_irq_disable(ab);
+
mutex_lock(&ab->core_lock);
ath11k_thermal_unregister(ab);
ath11k_dp_pdev_free(ab);
diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
index 84f66839f0c6..e69dc23b2051 100644
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -7,6 +7,8 @@
#ifndef ATH11K_DP_H
#define ATH11K_DP_H
+#include <linux/page_frag_cache.h>
+
#include "hal_rx.h"
#define MAX_RXDMA_PER_PDEV 2
@@ -72,6 +74,7 @@ struct dp_srng {
struct dp_rxdma_ring {
struct dp_srng refill_buf_ring;
+ struct page_frag_cache frag_cache;
struct idr bufs_idr;
/* Protects bufs_idr */
spinlock_t idr_lock;
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index e4ea0c216740..6bbfa0ebddd0 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -5,9 +5,11 @@
*/
#include <linux/fips.h>
+#include <linux/gfp.h>
#include <linux/ieee80211.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
+#include <net/sock.h>
#include "core.h"
#include "debug.h"
#include "debugfs_htt_stats.h"
@@ -340,6 +342,36 @@ static int ath11k_dp_purge_mon_ring(struct ath11k_base *ab)
return -ETIMEDOUT;
}
+static struct sk_buff *
+ath11k_dp_rx_alloc_skb(struct dp_rxdma_ring *rx_ring, unsigned int len)
+{
+ struct page_frag_cache *cache = &rx_ring->frag_cache;
+ gfp_t gfp_mask = GFP_ATOMIC;
+ 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 ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
struct dp_rxdma_ring *rx_ring,
@@ -371,8 +403,8 @@ int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
num_remain = req_entries;
while (num_remain > 0) {
- skb = dev_alloc_skb(DP_RX_BUFFER_SIZE +
- DP_RX_BUFFER_ALIGN_SIZE);
+ skb = ath11k_dp_rx_alloc_skb(rx_ring, DP_RX_BUFFER_SIZE +
+ DP_RX_BUFFER_ALIGN_SIZE);
if (!skb)
break;
@@ -453,6 +485,8 @@ static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar,
idr_destroy(&rx_ring->bufs_idr);
spin_unlock_bh(&rx_ring->idr_lock);
+ page_frag_cache_drain(&rx_ring->frag_cache);
+
return 0;
}
@@ -2867,8 +2901,8 @@ static struct sk_buff *ath11k_dp_rx_alloc_mon_status_buf(struct ath11k_base *ab,
struct sk_buff *skb;
dma_addr_t paddr;
- skb = dev_alloc_skb(DP_RX_BUFFER_SIZE +
- DP_RX_BUFFER_ALIGN_SIZE);
+ skb = ath11k_dp_rx_alloc_skb(rx_ring, DP_RX_BUFFER_SIZE +
+ DP_RX_BUFFER_ALIGN_SIZE);
if (!skb)
goto fail_alloc_skb;
prev parent reply other threads:[~2026-07-19 21:58 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-19 21:58 [RFC PATCH 0/3] wifi: ath11k: isolate RXDMA page-frag lifetimes Mark Ruvald Pedersen
2026-07-19 21:58 ` [RFC PATCH 1/3] wifi: ath11k: reserve headroom when aligning RX buffers Mark Ruvald Pedersen
2026-07-19 21:58 ` [RFC PATCH 2/3] wifi: ath11k: make external IRQ control idempotent Mark Ruvald Pedersen
2026-07-19 21:58 ` Mark Ruvald Pedersen [this message]
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=20260719215817.2468580-4-wabsie@gmail.com \
--to=wabsie@gmail.com \
--cc=ath11k@lists.infradead.org \
--cc=jjohnson@kernel.org \
--cc=linux-wireless@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.