[PATCH 0/3] wifi: ath11k: fix crash recovery panic and order-8 allocation failure
From: Yunseong Kim
Date: Mon Nov 02 2026 - 07:12:09 EST
Reported in Debian bug #1152007 on a ThinkPad P16v Gen 1 using Qualcomm
WCN6855 (ath11k_pci), When resuming from suspend, the driver may enter
the firmware crash recovery path:
ath11k_core_restart() -> ath11k_core_reconfigure_on_crash()
In this path, ath11k_dp_alloc() reallocates data path buffers, including
the tx_status completion buffer:
size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE;
dp->tx_ring[i].tx_status = kmalloc(size, GFP_KERNEL);
With DP_TX_COMP_RING_SIZE = 32768 and 32 bytes per descriptor, this
requires exactly 1,048,576 bytes (1 MiB), which corresponds to an order-8
contiguous allocation from the buddy allocator.
During system suspend and resume, the PM subsystem restricts memory
allocation flags by removing __GFP_IO and __GFP_FS from gfp_allowed_mask
via pm_restrict_gfp_mask(). Under GFP_NOIO:
1. Direct memory compaction is prohibited because
gfp_compaction_allowed() returns false without __GFP_IO.
2. Page reclaim cannot perform I/O to swap out anonymous pages or
write out dirty pages to backing storage.
If physical memory has become fragmented over uptime, the buddy
allocator cannot assemble a 1 MiB contiguous block, and the order-8
allocation fails with -ENOMEM despite ample total free memory.
When ath11k_dp_alloc() failed with -ENOMEM,
ath11k_core_reconfigure_on_crash() branched to its error exit path and
executed:
err_hal_srng_deinit:
ath11k_hal_srng_deinit(ab);
return ret;
This was fatal for two reasons:
1. HAL SRNG memory is allocated once at driver probe via
ath11k_hal_srng_init() and freed at driver remove via
ath11k_hal_srng_deinit(). Freeing it during crash recovery left
previously initialized CE and DP rings with dangling pointers into
unmapped coherent DMA memory (ab->hal.rdp).
2. ATH11K_FLAG_CRASH_FLUSH was cleared at the start of
ath11k_core_reconfigure_on_crash() and was not restored upon failure.
When the PM subsystem proceeded to resume subordinate devices, mac80211
invoked drv_start() (via wiphy_resume() -> ath11k_mac_op_start()),
queueing a command via ath11k_ce_send(). Because ATH11K_FLAG_CRASH_FLUSH
was clear, ath11k_ce_send() attempted to access CE ring 3 and called
ath11k_hal_srng_access_begin(), which dereferenced
srng->u.src_ring.tp_addr (pointing into the freed rdp buffer). Under an
IOMMU, this triggered an unhandled page fault in interrupt context,
panicking the kernel.
Attached decoded stacktrace with references of debian 7.2.9+deb13-amd64
vmlinux/source code:
https://gist.github.com/yskzalloc/9c3d1980a5ea0fbe0cd119042e09c54f
Patch 1:
wifi: ath11k: don't free HAL SRNG memory when crash recovery fails
- Preserves HAL SRNG memory across failed crash recovery (retaining its
probe-to-remove lifecycle).
- Restores ATH11K_FLAG_CRASH_FLUSH on failure so subsequent CE, WMI,
and DP operations immediately bail out with -ESHUTDOWN instead of
touching hardware rings or unmapped memory.
Patch 2:
wifi: ath11k: use kvmalloc() for the TX completion status buffer
- Confirms that dp->tx_ring[i].tx_status is a purely host CPU software
circular buffer used only for memcpy in
ath11k_dp_tx_completion_handler(); it is never mapped to DMA or
passed to hardware registers.
- Replaces kmalloc() with kvmalloc() (and kvfree()) to allow fallback to
virtually contiguous allocations when physical memory is fragmented
under GFP_NOIO.
- Fixes the error unwind path in ath11k_dp_alloc() to destroy IDRs and
free tx_status if an allocation failure occurs.
Patch 3:
wifi: ath11k: add KUnit tests for crash recovery and TX status allocation
- Adds hardware-independent KUnit tests exercising production recovery
and cleanup logic without hardware emulation:
* ath11k_test_recovery_success: normal recovery flow.
* ath11k_test_recovery_tx_status_alloc_fail: verifies -ENOMEM
handling, ATH11K_FLAG_CRASH_FLUSH restoration, and HAL SRNG
preservation.
* ath11k_test_recovery_subsequent_send_prevented: verifies
-ESHUTDOWN rejection on subsequent ath11k_ce_send() and
ath11k_dp_tx_send_reo_cmd().
* ath11k_test_recovery_resource_cleanup_and_teardown: verifies
recovery retry and full teardown without leaks or double-free.
* ath11k_test_dp_tx_status_kvmalloc: validates 1 MiB sizing and
kvmalloc/kvfree lifecycle.
Testing
=======
- Ran the full KUnit test suite under User Mode Linux (UML):
Ran 5 tests: passed: 5, failed: 0.
- Verified build cleanliness with CONFIG_KUNIT=n and
CONFIG_ATH11K_KUNIT_TEST=n.
Reported-by: Jeroen Trappers <jeroen.trappers@xxxxxxxxx>
Closes: https://bugs.debian.org/1152007
Link: https://bugs.debian.org/1152007
Signed-off-by: Yunseong Kim <yunseong.kim@xxxxxxxx>
---
Yunseong Kim (3):
wifi: ath11k: don't free HAL SRNG memory when crash recovery fails
wifi: ath11k: use kvmalloc() for the TX completion status buffer
wifi: ath11k: add KUnit tests for crash recovery and TX status allocation
drivers/net/wireless/ath/ath11k/.kunitconfig | 14 ++
drivers/net/wireless/ath/ath11k/Kconfig | 11 +
drivers/net/wireless/ath/ath11k/Makefile | 1 +
drivers/net/wireless/ath/ath11k/core.c | 50 +++-
drivers/net/wireless/ath/ath11k/core.h | 17 ++
drivers/net/wireless/ath/ath11k/dp.c | 22 +-
drivers/net/wireless/ath/ath11k/test.c | 330 +++++++++++++++++++++++++++
7 files changed, 434 insertions(+), 11 deletions(-)
---
base-commit: d89e1d186ec9271bf78e62771779e9fc03bf0fc3
change-id: 20261102-ath11k-oom-ed1cb6f5d114
Best regards,
--
Yunseong Kim <yunseong.kim@xxxxxxxx>