summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Escande <nico.escande@gmail.com>2026-08-13 14:15:28 +0200
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>2026-09-09 10:54:45 -0700
commitc7555d47b1d051e0406474349deccf74b491ae01 (patch)
treeb6995cbbb2a3708a2939e399b7ded0fa17e03240
parent52953d149ab4da0432d54b09f3c8add42b4d4a82 (diff)
downloadlinux-next-c7555d47b1d051e0406474349deccf74b491ae01.tar.gz
linux-next-c7555d47b1d051e0406474349deccf74b491ae01.zip
wifi: ath11k: fix locking problem in ath11k_dp_rx_tid_del_func()
In this function, we iterate over dp->reo_cmd_cache_flush_list using list_for_each_entry_safe(), under dp->reo_cmd_lock for concurrent access, and for each expired entries we : - drop the lock - call ath11k_dp_reo_cache_flush() - kfree() the entry - retake the lock to keep on iterating list_for_each_entry_safe() protects us from deleting the entry during iteration but doesn't protect for concurrent access. So another thread can take the lock and modify the list in between and crash like below. To fix the issue, move all entries that needs to be freed to a local list while under the lock and then iterate over the list to free the entries without holding the lock. BUG: Unable to handle kernel paging request at virtual address 00000010ddbeef8c Call trace: ath11k_dp_rx_tid_del_func+0x164/0x3c8 ath11k_dp_process_reo_status+0x1d4/0x2fc ath11k_dp_service_srng+0x334/0x338 ath11k_pcic_ext_grp_napi_poll+0x30/0xc0 __napi_poll+0x34/0x184 napi_threaded_poll+0xb4/0x1d8 kthread+0xdc/0xe0 ret_from_fork+0x10/0x20 Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-01977-QCAHKSWPL_SILICONZ-1 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com> Signed-off-by: Nicolas Escande <nico.escande@gmail.com> Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com> Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com> Link: https://patch.msgid.link/20260813-ath11k-locking-v2-1-f4113a0bd0f4@gmail.com Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 33425707c084..5b717b731197 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -762,6 +762,9 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
struct ath11k_base *ab = dp->ab;
struct dp_rx_tid *rx_tid = ctx;
struct dp_reo_cache_flush_elem *elem, *tmp;
+ struct list_head flush_list;
+
+ INIT_LIST_HEAD(&flush_list);
if (status == HAL_REO_CMD_DRAIN) {
goto free_desc;
@@ -783,23 +786,25 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list);
dp->reo_cmd_cache_flush_count++;
- /* Flush and invalidate aged REO desc from HW cache */
+ /* identify aged REO desc that needs removal */
list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list,
list) {
if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD ||
time_after(jiffies, elem->ts +
msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) {
- list_del(&elem->list);
+ list_move_tail(&elem->list, &flush_list);
dp->reo_cmd_cache_flush_count--;
- spin_unlock_bh(&dp->reo_cmd_lock);
-
- ath11k_dp_reo_cache_flush(ab, &elem->data);
- kfree(elem);
- spin_lock_bh(&dp->reo_cmd_lock);
}
}
spin_unlock_bh(&dp->reo_cmd_lock);
+ /* remove aged REO desc from HW */
+ list_for_each_entry_safe(elem, tmp, &flush_list, list) {
+ ath11k_dp_reo_cache_flush(ab, &elem->data);
+ list_del(&elem->list);
+ kfree(elem);
+ }
+
return;
free_desc:
dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,