From bf7c19e231e2f48cafddbe3f4c2a2f5b6184b55f Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:17:58 +0530 Subject: [PATCH 01/27] wifi: ath12k: move dp_profile_params to dp.h Move struct ath12k_dp_profile_params from core.h to dp.h as it is specifically used for Data Path operations. This improves code organization by keeping DP-related structures in the DP header. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-2-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 9 --------- drivers/net/wireless/ath/ath12k/dp.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index a98fc6e0699d..4718da494aab 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -928,15 +928,6 @@ struct ath12k_wsi_info { u32 hw_link_id_base; }; -struct ath12k_dp_profile_params { - u32 tx_comp_ring_size; - u32 rxdma_monitor_buf_ring_size; - u32 rxdma_monitor_dst_ring_size; - u32 num_pool_tx_desc; - u32 rx_desc_count; - u32 rx_release_ring_size; -}; - struct ath12k_mem_profile_based_param { u32 num_vdevs; u32 max_client_single; diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index bef0f2ba0560..72d3de2db5d8 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -23,6 +23,15 @@ struct ath12k_ext_irq_grp; struct ath12k_dp_rx_tid; struct ath12k_dp_rx_tid_rxq; +struct ath12k_dp_profile_params { + u32 tx_comp_ring_size; + u32 rxdma_monitor_buf_ring_size; + u32 rxdma_monitor_dst_ring_size; + u32 num_pool_tx_desc; + u32 rx_desc_count; + u32 rx_release_ring_size; +}; + #define DP_MON_PURGE_TIMEOUT_MS 100 #define DP_MON_SERVICE_BUDGET 128 From eaff19e86c2dbde320ccc8812cafb0a366dc65f4 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:17:59 +0530 Subject: [PATCH 02/27] wifi: ath12k: convert DP_TX_COMP_RING_SIZE to inline helper Replace DP_TX_COMP_RING_SIZE macro with ath12k_dp_tx_comp_ring_size() static inline helper. Pass the DP profile parameters explicitly to improve type safety and make the interface explicit. Update code using ATH12K_TX_COMPL_NEXT to pass ring size directly rather than deriving it from the ath12k_base structure. Also remove the unused DP_TX_IDR_SIZE macro. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-3-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 10 +++++++--- drivers/net/wireless/ath/ath12k/dp.h | 11 +++++++---- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 12 +++++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index ef9601152f81..6d5db403b731 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -439,6 +439,7 @@ static void ath12k_dp_srng_common_cleanup(struct ath12k_base *ab) static int ath12k_dp_srng_common_setup(struct ath12k_base *ab) { + const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); const struct ath12k_hal_tcl_to_wbm_rbm_map *map; struct hal_srng *srng; @@ -469,7 +470,7 @@ static int ath12k_dp_srng_common_setup(struct ath12k_base *ab) ret = ath12k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_comp_ring, HAL_WBM2SW_RELEASE, tx_comp_ring_num, 0, - DP_TX_COMP_RING_SIZE(ab)); + ath12k_dp_tx_comp_ring_size(dp_params)); if (ret) { ath12k_warn(ab, "failed to set up tcl_comp ring (%d) :%d\n", tx_comp_ring_num, ret); @@ -1465,6 +1466,7 @@ static int ath12k_dp_reoq_lut_setup(struct ath12k_base *ab) static int ath12k_dp_setup(struct ath12k_base *ab) { + const struct ath12k_dp_profile_params *dp_params; struct ath12k_dp *dp; struct hal_srng *srng = NULL; size_t size = 0; @@ -1474,6 +1476,7 @@ static int ath12k_dp_setup(struct ath12k_base *ab) dp = ath12k_ab_to_dp(ab); dp->ab = ab; + dp_params = &ab->profile_param->dp_params; INIT_LIST_HEAD(&dp->reo_cmd_list); INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list); @@ -1528,7 +1531,7 @@ static int ath12k_dp_setup(struct ath12k_base *ab) goto fail_dp_bank_profiles_cleanup; size = ab->hal.hal_wbm_release_ring_tx_size * - DP_TX_COMP_RING_SIZE(ab); + ath12k_dp_tx_comp_ring_size(dp_params); ret = ath12k_dp_reoq_lut_setup(ab); if (ret) { @@ -1540,7 +1543,8 @@ static int ath12k_dp_setup(struct ath12k_base *ab) dp->tx_ring[i].tcl_data_ring_id = i; dp->tx_ring[i].tx_status_head = 0; - dp->tx_ring[i].tx_status_tail = DP_TX_COMP_RING_SIZE(ab) - 1; + dp->tx_ring[i].tx_status_tail = + ath12k_dp_tx_comp_ring_size(dp_params) - 1; dp->tx_ring[i].tx_status = kmalloc(size, GFP_KERNEL); if (!dp->tx_ring[i].tx_status) { ret = -ENOMEM; diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 72d3de2db5d8..6540b3453cc6 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -61,7 +61,7 @@ struct dp_rxdma_ring { int bufs_max; }; -#define ATH12K_TX_COMPL_NEXT(ab, x) (((x) + 1) % DP_TX_COMP_RING_SIZE(ab)) +#define ATH12K_TX_COMPL_NEXT(ring_size, x) (((x) + 1) % (ring_size)) struct dp_tx_ring { u8 tcl_data_ring_id; @@ -201,9 +201,6 @@ struct ath12k_pdev_dp { #define DP_WBM_RELEASE_RING_SIZE 64 #define DP_TCL_DATA_RING_SIZE 512 -#define DP_TX_COMP_RING_SIZE(ab) \ - ((ab)->profile_param->dp_params.tx_comp_ring_size) -#define DP_TX_IDR_SIZE(ab) DP_TX_COMP_RING_SIZE(ab) #define DP_TCL_CMD_RING_SIZE 32 #define DP_TCL_STATUS_RING_SIZE 32 #define DP_REO_DST_RING_MAX 8 @@ -689,6 +686,12 @@ ath12k_dp_to_pdev_dp(struct ath12k_dp *dp, u8 pdev_idx) return rcu_dereference(dp->dp_pdevs[pdev_idx]); } +static inline u32 +ath12k_dp_tx_comp_ring_size(const struct ath12k_dp_profile_params *p) +{ + return p->tx_comp_ring_size; +} + void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 7c12dba4cc51..3f6df96de160 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -929,12 +929,14 @@ void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id) u64 desc_va; enum hal_wbm_rel_src_module buf_rel_source; enum hal_wbm_tqm_rel_reason rel_status; + u32 ring_size; spin_lock_bh(&status_ring->lock); + ring_size = ath12k_dp_tx_comp_ring_size(&ab->profile_param->dp_params); ath12k_hal_srng_access_begin(ab, status_ring); - while (ATH12K_TX_COMPL_NEXT(ab, tx_ring->tx_status_head) != + while (ATH12K_TX_COMPL_NEXT(ring_size, tx_ring->tx_status_head) != tx_ring->tx_status_tail) { desc = ath12k_hal_srng_dst_get_next_entry(ab, status_ring); if (!desc) @@ -943,11 +945,11 @@ void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id) memcpy(&tx_ring->tx_status[tx_ring->tx_status_head], desc, sizeof(*desc)); tx_ring->tx_status_head = - ATH12K_TX_COMPL_NEXT(ab, tx_ring->tx_status_head); + ATH12K_TX_COMPL_NEXT(ring_size, tx_ring->tx_status_head); } if (ath12k_hal_srng_dst_peek(ab, status_ring) && - (ATH12K_TX_COMPL_NEXT(ab, tx_ring->tx_status_head) == + (ATH12K_TX_COMPL_NEXT(ring_size, tx_ring->tx_status_head) == tx_ring->tx_status_tail)) { /* TODO: Process pending tx_status messages when kfifo_is_full() */ ath12k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); @@ -957,13 +959,13 @@ void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id) spin_unlock_bh(&status_ring->lock); - while (ATH12K_TX_COMPL_NEXT(ab, tx_ring->tx_status_tail) != + while (ATH12K_TX_COMPL_NEXT(ring_size, tx_ring->tx_status_tail) != tx_ring->tx_status_head) { struct hal_wbm_completion_ring_tx *tx_status; u32 desc_id; tx_ring->tx_status_tail = - ATH12K_TX_COMPL_NEXT(ab, tx_ring->tx_status_tail); + ATH12K_TX_COMPL_NEXT(ring_size, tx_ring->tx_status_tail); tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; ath12k_wifi7_dp_tx_status_parse(dp, tx_status, &ts); From d57f90669269fad2dd9ca98cd32b7412ceccb0b0 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:18:00 +0530 Subject: [PATCH 03/27] wifi: ath12k: convert DP_RXDMA_MONITOR_BUF_RING_SIZE to inline helper Replace DP_RXDMA_MONITOR_BUF_RING_SIZE macro with ath12k_dp_rxdma_monitor_buf_ring_size() static inline helper. Pass the DP profile parameters explicitly to improve type safety and make the interface explicit. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-4-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.h | 8 ++++++-- drivers/net/wireless/ath/ath12k/dp_rx.c | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 6540b3453cc6..9081655eae77 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -216,8 +216,6 @@ struct ath12k_pdev_dp { #define DP_RXDMA_REFILL_RING_SIZE 2048 #define DP_RXDMA_ERR_DST_RING_SIZE 1024 #define DP_RXDMA_MON_STATUS_RING_SIZE 1024 -#define DP_RXDMA_MONITOR_BUF_RING_SIZE(ab) \ - ((ab)->profile_param->dp_params.rxdma_monitor_buf_ring_size) #define DP_RXDMA_MONITOR_DST_RING_SIZE(ab) \ ((ab)->profile_param->dp_params.rxdma_monitor_dst_ring_size) #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 @@ -692,6 +690,12 @@ ath12k_dp_tx_comp_ring_size(const struct ath12k_dp_profile_params *p) return p->tx_comp_ring_size; } +static inline u32 +ath12k_dp_rxdma_monitor_buf_ring_size(const struct ath12k_dp_profile_params *p) +{ + return p->rxdma_monitor_buf_ring_size; +} + void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 41ac0a4072ae..75ada41aa2ba 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1704,6 +1704,8 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) int ath12k_dp_rx_alloc(struct ath12k_base *ab) { + const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; + u32 monitor_buf_ring_size = ath12k_dp_rxdma_monitor_buf_ring_size(dp_params); struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct dp_srng *srng; int i, ret; @@ -1748,7 +1750,7 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab) ret = ath12k_dp_srng_setup(ab, &dp->rxdma_mon_buf_ring.refill_buf_ring, HAL_RXDMA_MONITOR_BUF, 0, 0, - DP_RXDMA_MONITOR_BUF_RING_SIZE(ab)); + monitor_buf_ring_size); if (ret) { ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n"); return ret; From 62e2758d17d307753457bd51af074be5f8265c03 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:18:01 +0530 Subject: [PATCH 04/27] wifi: ath12k: convert DP_RXDMA_MONITOR_DST_RING_SIZE to inline helper Replace DP_RXDMA_MONITOR_DST_RING_SIZE macro with ath12k_dp_rxdma_monitor_dst_ring_size() static inline helper. Pass the DP profile parameters explicitly to improve type safety and make the interface explicit. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-5-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.h | 8 ++++++-- drivers/net/wireless/ath/ath12k/dp_rx.c | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 9081655eae77..a7cd2535866b 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -216,8 +216,6 @@ struct ath12k_pdev_dp { #define DP_RXDMA_REFILL_RING_SIZE 2048 #define DP_RXDMA_ERR_DST_RING_SIZE 1024 #define DP_RXDMA_MON_STATUS_RING_SIZE 1024 -#define DP_RXDMA_MONITOR_DST_RING_SIZE(ab) \ - ((ab)->profile_param->dp_params.rxdma_monitor_dst_ring_size) #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 #define DP_TX_MONITOR_BUF_RING_SIZE 4096 #define DP_TX_MONITOR_DEST_RING_SIZE 2048 @@ -696,6 +694,12 @@ ath12k_dp_rxdma_monitor_buf_ring_size(const struct ath12k_dp_profile_params *p) return p->rxdma_monitor_buf_ring_size; } +static inline u32 +ath12k_dp_rxdma_monitor_dst_ring_size(const struct ath12k_dp_profile_params *p) +{ + return p->rxdma_monitor_dst_ring_size; +} + void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 75ada41aa2ba..8bb2b57aed15 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -330,16 +330,20 @@ static int ath12k_dp_rx_pdev_srng_alloc(struct ath12k *ar) { struct ath12k_pdev_dp *dp = &ar->dp; struct ath12k_base *ab = ar->ab; + u32 monitor_dst_ring_size; + u32 mac_id = dp->mac_id; int i; int ret; - u32 mac_id = dp->mac_id; + + monitor_dst_ring_size = + ath12k_dp_rxdma_monitor_dst_ring_size(&ab->profile_param->dp_params); for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { ret = ath12k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring[i], HAL_RXDMA_MONITOR_DST, 0, mac_id + i, - DP_RXDMA_MONITOR_DST_RING_SIZE(ab)); + monitor_dst_ring_size); if (ret) { ath12k_warn(ar->ab, "failed to setup HAL_RXDMA_MONITOR_DST\n"); From 99b85a24189a518938eb0f1c4f8e15fbab4373ec Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:18:02 +0530 Subject: [PATCH 05/27] wifi: ath12k: convert ATH12K_NUM_POOL_TX_DESC to inline helper Replace ATH12K_NUM_POOL_TX_DESC with the ath12k_dp_num_pool_tx_desc() static inline helper. Also replace ATH12K_TX_SPT_PAGES_PER_POOL, ATH12K_NUM_TX_SPT_PAGES, and ATH12K_RX_SPT_PAGE_OFFSET, which are derived from ATH12K_NUM_POOL_TX_DESC, with static inline helper functions. Pass the DP profile parameters explicitly to improve type safety and make the interface more explicit. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-6-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 55 ++++++++++++++++----------- drivers/net/wireless/ath/ath12k/dp.h | 32 ++++++++++++---- drivers/net/wireless/ath/ath12k/mac.c | 5 ++- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 6d5db403b731..e8a82a86bd9b 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -967,14 +967,15 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) { - struct ath12k_rx_desc_info *desc_info; + const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; + u32 pool_id, tx_spt_page, tx_spt_pages_per_pool; struct ath12k_tx_desc_info *tx_desc_info, *tmp1; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); + struct ath12k_rx_desc_info *desc_info; struct ath12k_skb_cb *skb_cb; struct sk_buff *skb; struct ath12k *ar; int i, j; - u32 pool_id, tx_spt_page; if (!dp->spt_info) return; @@ -1056,12 +1057,13 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) } if (dp->txbaddr) { + tx_spt_pages_per_pool = ath12k_dp_tx_spt_pages_per_pool(dp_params); + for (pool_id = 0; pool_id < ATH12K_HW_MAX_QUEUES; pool_id++) { spin_lock_bh(&dp->tx_desc_lock[pool_id]); - for (i = 0; i < ATH12K_TX_SPT_PAGES_PER_POOL(ab); i++) { - tx_spt_page = i + pool_id * - ATH12K_TX_SPT_PAGES_PER_POOL(ab); + for (i = 0; i < tx_spt_pages_per_pool; i++) { + tx_spt_page = i + pool_id * tx_spt_pages_per_pool; if (!dp->txbaddr[tx_spt_page]) continue; @@ -1155,13 +1157,15 @@ static void *ath12k_dp_cc_get_desc_addr_ptr(struct ath12k_dp *dp, struct ath12k_rx_desc_info *ath12k_dp_get_rx_desc(struct ath12k_dp *dp, u32 cookie) { + const struct ath12k_dp_profile_params *dp_params; struct ath12k_rx_desc_info **desc_addr_ptr; u16 start_ppt_idx, end_ppt_idx, ppt_idx, spt_idx; ppt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_PPT); spt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_SPT); + dp_params = &dp->ab->profile_param->dp_params; - start_ppt_idx = dp->rx_ppt_base + ATH12K_RX_SPT_PAGE_OFFSET(dp->ab); + start_ppt_idx = dp->rx_ppt_base + ath12k_dp_rx_spt_page_offset(dp_params); end_ppt_idx = start_ppt_idx + ATH12K_NUM_RX_SPT_PAGES(dp->ab); if (ppt_idx < start_ppt_idx || @@ -1179,15 +1183,16 @@ EXPORT_SYMBOL(ath12k_dp_get_rx_desc); struct ath12k_tx_desc_info *ath12k_dp_get_tx_desc(struct ath12k_dp *dp, u32 cookie) { + const struct ath12k_dp_profile_params *dp_params; struct ath12k_tx_desc_info **desc_addr_ptr; u16 start_ppt_idx, end_ppt_idx, ppt_idx, spt_idx; ppt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_PPT); spt_idx = u32_get_bits(cookie, ATH12K_DP_CC_COOKIE_SPT); + dp_params = &dp->ab->profile_param->dp_params; start_ppt_idx = ATH12K_TX_SPT_PAGE_OFFSET; - end_ppt_idx = start_ppt_idx + - (ATH12K_TX_SPT_PAGES_PER_POOL(dp->ab) * ATH12K_HW_MAX_QUEUES); + end_ppt_idx = start_ppt_idx + ath12k_dp_num_tx_spt_pages(dp_params); if (ppt_idx < start_ppt_idx || ppt_idx >= end_ppt_idx || @@ -1202,12 +1207,13 @@ EXPORT_SYMBOL(ath12k_dp_get_tx_desc); static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) { - struct ath12k_dp *dp = ath12k_ab_to_dp(ab); + const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; struct ath12k_rx_desc_info *rx_descs, **rx_desc_addr; struct ath12k_tx_desc_info *tx_descs, **tx_desc_addr; + u32 i, j, pool_id, tx_spt_page, tx_spt_pages_per_pool; u32 num_rx_spt_pages = ATH12K_NUM_RX_SPT_PAGES(ab); - u32 i, j, pool_id, tx_spt_page; - u32 ppt_idx, cookie_ppt_idx; + u32 ppt_idx, cookie_ppt_idx, rx_spt_page_offset; + struct ath12k_dp *dp = ath12k_ab_to_dp(ab); spin_lock_bh(&dp->rx_desc_lock); @@ -1219,9 +1225,9 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) return -ENOMEM; } - /* First ATH12K_NUM_RX_SPT_PAGES(ab) of allocated SPT pages are used for - * RX - */ + rx_spt_page_offset = ath12k_dp_rx_spt_page_offset(dp_params); + + /* First num_rx_spt_pages of allocated SPT pages are used for RX */ for (i = 0; i < num_rx_spt_pages; i++) { rx_descs = kzalloc_objs(*rx_descs, ATH12K_MAX_SPT_ENTRIES, GFP_ATOMIC); @@ -1231,7 +1237,7 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) return -ENOMEM; } - ppt_idx = ATH12K_RX_SPT_PAGE_OFFSET(ab) + i; + ppt_idx = rx_spt_page_offset + i; cookie_ppt_idx = dp->rx_ppt_base + ppt_idx; dp->rxbaddr[i] = &rx_descs[0]; @@ -1250,14 +1256,17 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) spin_unlock_bh(&dp->rx_desc_lock); dp->txbaddr = kzalloc_objs(struct ath12k_tx_desc_info *, - ATH12K_NUM_TX_SPT_PAGES(ab), GFP_ATOMIC); + ath12k_dp_num_tx_spt_pages(dp_params), + GFP_ATOMIC); if (!dp->txbaddr) return -ENOMEM; + tx_spt_pages_per_pool = ath12k_dp_tx_spt_pages_per_pool(dp_params); + for (pool_id = 0; pool_id < ATH12K_HW_MAX_QUEUES; pool_id++) { spin_lock_bh(&dp->tx_desc_lock[pool_id]); - for (i = 0; i < ATH12K_TX_SPT_PAGES_PER_POOL(ab); i++) { + for (i = 0; i < tx_spt_pages_per_pool; i++) { tx_descs = kzalloc_objs(*tx_descs, ATH12K_MAX_SPT_ENTRIES, GFP_ATOMIC); @@ -1268,8 +1277,7 @@ static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) return -ENOMEM; } - tx_spt_page = i + pool_id * - ATH12K_TX_SPT_PAGES_PER_POOL(ab); + tx_spt_page = i + pool_id * tx_spt_pages_per_pool; ppt_idx = ATH12K_TX_SPT_PAGE_OFFSET + tx_spt_page; dp->txbaddr[tx_spt_page] = &tx_descs[0]; @@ -1295,6 +1303,7 @@ static int ath12k_dp_cmem_init(struct ath12k_base *ab, struct ath12k_dp *dp, enum ath12k_dp_desc_type type) { + const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; u32 cmem_base; int i, start, end; @@ -1303,11 +1312,11 @@ static int ath12k_dp_cmem_init(struct ath12k_base *ab, switch (type) { case ATH12K_DP_TX_DESC: start = ATH12K_TX_SPT_PAGE_OFFSET; - end = start + ATH12K_NUM_TX_SPT_PAGES(ab); + end = start + ath12k_dp_num_tx_spt_pages(dp_params); break; case ATH12K_DP_RX_DESC: cmem_base += ATH12K_PPT_ADDR_OFFSET(dp->rx_ppt_base); - start = ATH12K_RX_SPT_PAGE_OFFSET(ab); + start = ath12k_dp_rx_spt_page_offset(dp_params); end = start + ATH12K_NUM_RX_SPT_PAGES(ab); break; default: @@ -1338,7 +1347,9 @@ void ath12k_dp_partner_cc_init(struct ath12k_base *ab) static u32 ath12k_dp_get_num_spt_pages(struct ath12k_base *ab) { - return ATH12K_NUM_RX_SPT_PAGES(ab) + ATH12K_NUM_TX_SPT_PAGES(ab); + const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; + + return ATH12K_NUM_RX_SPT_PAGES(ab) + ath12k_dp_num_tx_spt_pages(dp_params); } static int ath12k_dp_cc_init(struct ath12k_base *ab) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index a7cd2535866b..7e48379d18a0 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -249,8 +249,6 @@ struct ath12k_pdev_dp { #define ATH12K_SHADOW_DP_TIMER_INTERVAL 20 #define ATH12K_SHADOW_CTRL_TIMER_INTERVAL 10 -#define ATH12K_NUM_POOL_TX_DESC(ab) \ - ((ab)->profile_param->dp_params.num_pool_tx_desc) /* TODO: revisit this count during testing */ #define ATH12K_RX_DESC_COUNT(ab) \ ((ab)->profile_param->dp_params.rx_desc_count) @@ -268,13 +266,7 @@ struct ath12k_pdev_dp { #define ATH12K_NUM_RX_SPT_PAGES(ab) ((ATH12K_RX_DESC_COUNT(ab)) / \ ATH12K_MAX_SPT_ENTRIES) -#define ATH12K_TX_SPT_PAGES_PER_POOL(ab) (ATH12K_NUM_POOL_TX_DESC(ab) / \ - ATH12K_MAX_SPT_ENTRIES) -#define ATH12K_NUM_TX_SPT_PAGES(ab) (ATH12K_TX_SPT_PAGES_PER_POOL(ab) * \ - ATH12K_HW_MAX_QUEUES) - #define ATH12K_TX_SPT_PAGE_OFFSET 0 -#define ATH12K_RX_SPT_PAGE_OFFSET(ab) ATH12K_NUM_TX_SPT_PAGES(ab) /* The SPT pages are divided for RX and TX, first block for RX * and remaining for TX @@ -700,6 +692,30 @@ ath12k_dp_rxdma_monitor_dst_ring_size(const struct ath12k_dp_profile_params *p) return p->rxdma_monitor_dst_ring_size; } +static inline u32 +ath12k_dp_num_pool_tx_desc(const struct ath12k_dp_profile_params *p) +{ + return p->num_pool_tx_desc; +} + +static inline u32 +ath12k_dp_tx_spt_pages_per_pool(const struct ath12k_dp_profile_params *p) +{ + return ath12k_dp_num_pool_tx_desc(p) / ATH12K_MAX_SPT_ENTRIES; +} + +static inline u32 +ath12k_dp_num_tx_spt_pages(const struct ath12k_dp_profile_params *p) +{ + return ath12k_dp_tx_spt_pages_per_pool(p) * ATH12K_HW_MAX_QUEUES; +} + +static inline u32 +ath12k_dp_rx_spt_page_offset(const struct ath12k_dp_profile_params *p) +{ + return ath12k_dp_num_tx_spt_pages(p); +} + void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index d4116ba0da08..196b807210de 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9739,6 +9739,7 @@ static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) static int ath12k_mac_start(struct ath12k *ar) { + const struct ath12k_dp_profile_params *dp_params; struct ath12k_hw *ah = ar->ah; struct ath12k_base *ab = ar->ab; struct ath12k_pdev *pdev = ar->pdev; @@ -9747,6 +9748,8 @@ static int ath12k_mac_start(struct ath12k *ar) lockdep_assert_held(&ah->hw_mutex); lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + dp_params = &ab->profile_param->dp_params; + ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1, pdev->pdev_id); @@ -9795,7 +9798,7 @@ static int ath12k_mac_start(struct ath12k *ar) if (ab->hw_params->supports_cong_ctrl_max_msdus) { ret = ath12k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_SET_CONG_CTRL_MAX_MSDUS, - ATH12K_NUM_POOL_TX_DESC(ab), + ath12k_dp_num_pool_tx_desc(dp_params), pdev->pdev_id); if (ret) { ath12k_err(ab, From fafb9b0450fd6fd3618daf347717a6d6320bea38 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:18:03 +0530 Subject: [PATCH 06/27] wifi: ath12k: convert ATH12K_RX_DESC_COUNT to inline helper Replace ATH12K_RX_DESC_COUNT with ath12k_dp_rx_desc_count() static inline helper, also replace ATH12K_NUM_RX_SPT_PAGES which is calculated from ATH12K_RX_DESC_COUNT into static inline helper functions and pass the DP profile parameters explicitly to improve type safety and make the interface explicit. Also remove the unused ATH12K_NUM_TX_SPT_PAGE_START macro. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-7-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 18 +++++++++++------- drivers/net/wireless/ath/ath12k/dp.h | 24 ++++++++++++------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index e8a82a86bd9b..c08bd780d59d 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -968,7 +968,7 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) { const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; - u32 pool_id, tx_spt_page, tx_spt_pages_per_pool; + u32 pool_id, tx_spt_page, tx_spt_pages_per_pool, num_rx_spt_pages; struct ath12k_tx_desc_info *tx_desc_info, *tmp1; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); struct ath12k_rx_desc_info *desc_info; @@ -983,8 +983,10 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) /* RX Descriptor cleanup */ spin_lock_bh(&dp->rx_desc_lock); + num_rx_spt_pages = ath12k_dp_num_rx_spt_pages(dp_params); + if (dp->rxbaddr) { - for (i = 0; i < ATH12K_NUM_RX_SPT_PAGES(ab); i++) { + for (i = 0; i < num_rx_spt_pages; i++) { if (!dp->rxbaddr[i]) continue; @@ -1166,7 +1168,7 @@ struct ath12k_rx_desc_info *ath12k_dp_get_rx_desc(struct ath12k_dp *dp, dp_params = &dp->ab->profile_param->dp_params; start_ppt_idx = dp->rx_ppt_base + ath12k_dp_rx_spt_page_offset(dp_params); - end_ppt_idx = start_ppt_idx + ATH12K_NUM_RX_SPT_PAGES(dp->ab); + end_ppt_idx = start_ppt_idx + ath12k_dp_num_rx_spt_pages(dp_params); if (ppt_idx < start_ppt_idx || ppt_idx >= end_ppt_idx || @@ -1208,10 +1210,10 @@ EXPORT_SYMBOL(ath12k_dp_get_tx_desc); static int ath12k_dp_cc_desc_init(struct ath12k_base *ab) { const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; + u32 num_rx_spt_pages = ath12k_dp_num_rx_spt_pages(dp_params); struct ath12k_rx_desc_info *rx_descs, **rx_desc_addr; struct ath12k_tx_desc_info *tx_descs, **tx_desc_addr; u32 i, j, pool_id, tx_spt_page, tx_spt_pages_per_pool; - u32 num_rx_spt_pages = ATH12K_NUM_RX_SPT_PAGES(ab); u32 ppt_idx, cookie_ppt_idx, rx_spt_page_offset; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); @@ -1317,7 +1319,7 @@ static int ath12k_dp_cmem_init(struct ath12k_base *ab, case ATH12K_DP_RX_DESC: cmem_base += ATH12K_PPT_ADDR_OFFSET(dp->rx_ppt_base); start = ath12k_dp_rx_spt_page_offset(dp_params); - end = start + ATH12K_NUM_RX_SPT_PAGES(ab); + end = start + ath12k_dp_num_rx_spt_pages(dp_params); break; default: ath12k_err(ab, "invalid descriptor type %d in cmem init\n", type); @@ -1349,7 +1351,8 @@ static u32 ath12k_dp_get_num_spt_pages(struct ath12k_base *ab) { const struct ath12k_dp_profile_params *dp_params = &ab->profile_param->dp_params; - return ATH12K_NUM_RX_SPT_PAGES(ab) + ath12k_dp_num_tx_spt_pages(dp_params); + return ath12k_dp_num_rx_spt_pages(dp_params) + + ath12k_dp_num_tx_spt_pages(dp_params); } static int ath12k_dp_cc_init(struct ath12k_base *ab) @@ -1377,7 +1380,8 @@ static int ath12k_dp_cc_init(struct ath12k_base *ab) return -ENOMEM; } - dp->rx_ppt_base = ab->device_id * ATH12K_NUM_RX_SPT_PAGES(ab); + dp->rx_ppt_base = ab->device_id * + ath12k_dp_num_rx_spt_pages(&ab->profile_param->dp_params); for (i = 0; i < dp->num_spt_pages; i++) { dp->spt_info[i].vaddr = dma_alloc_coherent(ab->dev, diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 7e48379d18a0..c8ca3ab4d5e1 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -249,10 +249,6 @@ struct ath12k_pdev_dp { #define ATH12K_SHADOW_DP_TIMER_INTERVAL 20 #define ATH12K_SHADOW_CTRL_TIMER_INTERVAL 10 -/* TODO: revisit this count during testing */ -#define ATH12K_RX_DESC_COUNT(ab) \ - ((ab)->profile_param->dp_params.rx_desc_count) - #define ATH12K_PAGE_SIZE PAGE_SIZE /* Total 1024 entries in PPT, i.e 4K/4 considering 4K aligned @@ -263,16 +259,8 @@ struct ath12k_pdev_dp { /* Total 512 entries in a SPT, i.e 4K Page/8 */ #define ATH12K_MAX_SPT_ENTRIES 512 -#define ATH12K_NUM_RX_SPT_PAGES(ab) ((ATH12K_RX_DESC_COUNT(ab)) / \ - ATH12K_MAX_SPT_ENTRIES) - #define ATH12K_TX_SPT_PAGE_OFFSET 0 -/* The SPT pages are divided for RX and TX, first block for RX - * and remaining for TX - */ -#define ATH12K_NUM_TX_SPT_PAGE_START(ab) ATH12K_NUM_RX_SPT_PAGES(ab) - #define ATH12K_DP_RX_DESC_MAGIC 0xBABABABA /* 4K aligned address have last 12 bits set to 0, this check is done @@ -716,6 +704,18 @@ ath12k_dp_rx_spt_page_offset(const struct ath12k_dp_profile_params *p) return ath12k_dp_num_tx_spt_pages(p); } +static inline u32 +ath12k_dp_rx_desc_count(const struct ath12k_dp_profile_params *p) +{ + return p->rx_desc_count; +} + +static inline u32 +ath12k_dp_num_rx_spt_pages(const struct ath12k_dp_profile_params *p) +{ + return ath12k_dp_rx_desc_count(p) / ATH12K_MAX_SPT_ENTRIES; +} + void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); From 897d2e0046f941d8956f655f5ffc5d75c788558d Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 20 Aug 2026 11:18:04 +0530 Subject: [PATCH 07/27] wifi: ath12k: convert DP_RX_RELEASE_RING_SIZE to inline helper Replace DP_RX_RELEASE_RING_SIZE macro with ath12k_dp_rx_release_ring_size() static inline helper. Pass the DP profile parameters explicitly to improve type safety and make the interface explicit. No functional change intended. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260820054804.3222296-8-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 2 +- drivers/net/wireless/ath/ath12k/dp.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index c08bd780d59d..cce5386c5bd5 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -488,7 +488,7 @@ static int ath12k_dp_srng_common_setup(struct ath12k_base *ab) ret = ath12k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE, HAL_WBM2SW_REL_ERR_RING_NUM, 0, - DP_RX_RELEASE_RING_SIZE(ab)); + ath12k_dp_rx_release_ring_size(dp_params)); if (ret) { ath12k_warn(ab, "failed to set up rx_rel ring :%d\n", ret); goto err; diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index c8ca3ab4d5e1..ff62773cfa9a 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -206,8 +206,6 @@ struct ath12k_pdev_dp { #define DP_REO_DST_RING_MAX 8 #define DP_REO_DST_RING_SIZE 2048 #define DP_REO_REINJECT_RING_SIZE 32 -#define DP_RX_RELEASE_RING_SIZE(ab) \ - ((ab)->profile_param->dp_params.rx_release_ring_size) #define DP_REO_EXCEPTION_RING_SIZE 128 #define DP_REO_CMD_RING_SIZE 256 #define DP_REO_STATUS_RING_SIZE 2048 @@ -716,6 +714,12 @@ ath12k_dp_num_rx_spt_pages(const struct ath12k_dp_profile_params *p) return ath12k_dp_rx_desc_count(p) / ATH12K_MAX_SPT_ENTRIES; } +static inline u32 +ath12k_dp_rx_release_ring_size(const struct ath12k_dp_profile_params *p) +{ + return p->rx_release_ring_size; +} + void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif); void ath12k_dp_partner_cc_init(struct ath12k_base *ab); int ath12k_dp_pdev_alloc(struct ath12k_base *ab); From 4dfe8e3ac731c3372b5c1f1f73b0676c301c3916 Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Wed, 16 Sep 2026 14:03:20 +0800 Subject: [PATCH 08/27] wifi: ath12k: fix skb leak on paddr mismatch in wifi7 mon RX pop When rxcb->paddr does not match the MSDU-list paddr reported by the link descriptor, ath12k_wifi7_dp_rx_mon_mpdu_pop() sets drop_mpdu = true and continues to the next MSDU. At that point the skb is still attached to the descriptor via desc_info->skb; the local msdu variable only holds a copy of that pointer. The continue skips the rest of the loop body, which would normally DMA-unmap the buffer, free the skb, clear desc_info->skb, and append the descriptor to used_list in the next_msdu block. The descriptor therefore stays in_use with the skb attached forever. The skb is never DMA-unmapped, delivered, freed or replaced, and HW does not write into it either because the descriptor is no longer posted to any SRNG. The descriptor is also never returned to used_list, so ath12k_dp_rx_bufs_replenish() cannot allocate a fresh buffer for it and the RX refill ring gradually drains. The skb is still reachable via dp->rxbaddr[][].skb and is reclaimed at teardown by ath12k_dp_cc_cleanup(), so this is not a kmemleak-style unreachable leak. Nevertheless, both the descriptor slot and the skb memory are wasted for the module lifetime, and under sustained mismatches monitor RX stalls. Remove the continue so drop_mpdu remains true, execution reaches the DMA unmap and dev_kfree_skb_any() below, and the descriptor is returned to used_list via the next_msdu block for replenish. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: 72bfbf19b7da ("wifi: ath12k: add support to reap and process mon dest ring") Signed-off-by: Kang Yang Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260916060325.854-2-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index ded7d56cd79b..ed6686746605 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -2718,7 +2718,6 @@ ath12k_wifi7_dp_rx_mon_mpdu_pop(struct ath12k *ar, int mac_id, i, (unsigned long)rxcb->paddr, (unsigned long)msdu_list.paddr[i]); drop_mpdu = true; - continue; } if (!rxcb->unmapped) { dma_unmap_single(ar->ab->dev, rxcb->paddr, From d8e8eac3fcffc114a79f7d07d7cb73cc16645381 Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Wed, 16 Sep 2026 14:03:21 +0800 Subject: [PATCH 09/27] wifi: ath12k: free pending MSDUs on duplicate mon link descriptor ath12k_wifi7_dp_rx_mon_mpdu_pop() aborts processing when a duplicate monitor link descriptor is detected. Previous iterations may already have linked MSDUs onto *head_msdu and detached the corresponding RX buffers from their descriptors. Returning without completing or freeing the chain leaves those skbs orphaned, resulting in a memory leak. kmemleak reports these skbs in field testing: kmemleak_alloc() __netdev_alloc_skb() ath12k_dp_rx_bufs_replenish() ath12k_wifi7_dp_rx_mon_dest_process() Free the accumulated MSDU chain before returning and clear *head_msdu so callers observe a consistent state. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: 72bfbf19b7da ("wifi: ath12k: add support to reap and process mon dest ring") Signed-off-by: Kang Yang Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260916060325.854-3-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index ed6686746605..08f2fba3e680 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -2675,6 +2675,8 @@ ath12k_wifi7_dp_rx_mon_mpdu_pop(struct ath12k *ar, int mac_id, if (pmon->mon_last_linkdesc_paddr == paddr) { pmon->rx_mon_stats.dup_mon_linkdesc_cnt++; spin_unlock_bh(&pmon->mon_lock); + kfree_skb_list(*head_msdu); + *head_msdu = NULL; return rx_bufs_used; } From 1a10a558555072dba8510ded23a9174b2a00d079 Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Wed, 16 Sep 2026 14:03:22 +0800 Subject: [PATCH 10/27] wifi: ath12k: fix stale tail_msdu pointer returned from mpdu_pop ath12k_wifi7_dp_rx_mon_mpdu_pop() assigns *tail_msdu from the loop cursor msdu at function exit. That cursor reflects the state of the last msdu_list entry processed, not the end of the accumulated MSDU chain: on next_msdu paths (invalid skb, drop_mpdu, first-MSDU rx_desc invalid) it is set to NULL, and on the set_pktlen failure path it becomes a dangling pointer to a freed skb. When these occur on the final iteration, callers see head_msdu populated with a valid chain while tail_msdu is NULL or points to freed memory. The chain end is already tracked by the local variable last, which is updated only after each MSDU has been validated and linked. Use it as the source for tail_msdu so head_msdu and tail_msdu are either both NULL or both point into the same live chain. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: 72bfbf19b7da ("wifi: ath12k: add support to reap and process mon dest ring") Signed-off-by: Kang Yang Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260916060325.854-4-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 08f2fba3e680..8fca777041d1 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -2802,7 +2802,7 @@ next_msdu: if (last) last->next = NULL; - *tail_msdu = msdu; + *tail_msdu = last; if (msdu_cnt == 0) *npackets = 1; From 8b1d2f4c3805b9f1bddd5dc3bd241edd1f9ab2d9 Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Wed, 16 Sep 2026 14:03:23 +0800 Subject: [PATCH 11/27] wifi: ath12k: place dp_mon_mpdu on stack in mon dst reap loop ath12k_wifi7_dp_rx_mon_dest_process() allocates a dp_mon_mpdu with kzalloc(GFP_ATOMIC) for each delivered MPDU. On allocation failure it breaks out of the reap loop without calling ath12k_hal_srng_dst_get_next_entry(), leaving the destination ring tail pointer parked on the current entry -- the same entry will be peek()ed on every subsequent NAPI schedule, stalling monitor RX until the mon_dest_ring_stuck_cnt recovery path resyncs the PPDU ID. The head_msdu chain accumulated for this iteration is also orphaned since the break bypasses both the delivery and the cleanup paths. kmemleak reports these skbs in field testing: kmemleak_alloc() __netdev_alloc_skb() ath12k_dp_rx_bufs_replenish() ath12k_wifi7_dp_rx_mon_dest_process() dp_mon_mpdu is used only within a single reap loop iteration and is passed synchronously to ath12k_wifi7_dp_mon_rx_deliver(); the callee does not retain the pointer. Place it on the stack instead of using kzalloc(GFP_ATOMIC). This eliminates the allocation failure path (and the ring stall it caused) and saves a kzalloc/kfree pair per delivered MPDU. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: 72bfbf19b7da ("wifi: ath12k: add support to reap and process mon dest ring") Signed-off-by: Kang Yang Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260916060325.854-5-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 8fca777041d1..1e29c13ad66d 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -2827,7 +2827,6 @@ ath12k_wifi7_dp_rx_mon_dest_process(struct ath12k *ar, int mac_id, struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); void *ring_entry, *mon_dst_srng; - struct dp_mon_mpdu *tmp_mpdu; LIST_HEAD(rx_desc_used_list); struct hal_srng *srng; @@ -2893,18 +2892,16 @@ ath12k_wifi7_dp_rx_mon_dest_process(struct ath12k *ar, int mac_id, } if (head_msdu && tail_msdu) { - tmp_mpdu = kzalloc_obj(*tmp_mpdu, GFP_ATOMIC); - if (!tmp_mpdu) - break; + struct dp_mon_mpdu tmp_mpdu = { + .head = head_msdu, + .tail = tail_msdu, + .err_bitmap = pmon->err_bitmap, + .decap_format = pmon->decap_format, + }; - tmp_mpdu->head = head_msdu; - tmp_mpdu->tail = tail_msdu; - tmp_mpdu->err_bitmap = pmon->err_bitmap; - tmp_mpdu->decap_format = pmon->decap_format; - ath12k_wifi7_dp_mon_rx_deliver(&ar->dp, tmp_mpdu, + ath12k_wifi7_dp_mon_rx_deliver(&ar->dp, &tmp_mpdu, &pmon->mon_ppdu_info, napi); rx_mon_stats->dest_mpdu_done++; - kfree(tmp_mpdu); } ring_entry = ath12k_hal_srng_dst_get_next_entry(ar->ab, From 97b144b82e2388efef1f78269626d61de0480feb Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Wed, 16 Sep 2026 14:03:24 +0800 Subject: [PATCH 12/27] wifi: ath12k: fix skb leak on monitor PPDU ID wraparound ath12k_dp_mon_comp_ppduid() updates *ppdu_id and returns msdu_ppdu_id to indicate that a PPDU transition was detected. When PPDU IDs wrap around, msdu_ppdu_id can be 0. In that case the function updates *ppdu_id but returns 0, causing ath12k_wifi7_dp_rx_mon_mpdu_pop() to interpret the result as "no PPDU update" and continue processing the MPDU. The MSDUs are then linked into head_msdu while their RX descriptors have already been detached. Later, ath12k_wifi7_dp_rx_mon_dest_process() detects the PPDU mismatch and exits with the partially built MSDU chain, leaking the SKBs. kmemleak reports the leak through: kmemleak_alloc() __netdev_alloc_skb() ath12k_dp_rx_bufs_replenish() ath12k_wifi7_dp_rx_mon_dest_process() Fix this by returning a boolean value from ath12k_dp_mon_comp_ppduid() so the update notification is independent of the actual PPDU ID value. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: 72bfbf19b7da ("wifi: ath12k: add support to reap and process mon dest ring") Signed-off-by: Kang Yang Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260916060325.854-6-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_mon.c | 15 +++++++-------- drivers/net/wireless/ath/ath12k/dp_mon.h | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 1faf1896d0ab..709507d44d8e 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -81,24 +81,23 @@ fail_alloc_skb: } EXPORT_SYMBOL(ath12k_dp_rx_alloc_mon_status_buf); -u32 ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id) +bool ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id) { - u32 ret = 0; - if ((*ppdu_id < msdu_ppdu_id) && ((msdu_ppdu_id - *ppdu_id) < DP_NOT_PPDU_ID_WRAP_AROUND)) { /* Hold on mon dest ring, and reap mon status ring. */ *ppdu_id = msdu_ppdu_id; - ret = msdu_ppdu_id; - } else if ((*ppdu_id > msdu_ppdu_id) && - ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) { + return true; + } + if ((*ppdu_id > msdu_ppdu_id) && + ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) { /* PPDU ID has exceeded the maximum value and will * restart from 0. */ *ppdu_id = msdu_ppdu_id; - ret = msdu_ppdu_id; + return true; } - return ret; + return false; } EXPORT_SYMBOL(ath12k_dp_mon_comp_ppduid); diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h index 162cdcaa57a7..f352655eae9d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.h +++ b/drivers/net/wireless/ath/ath12k/dp_mon.h @@ -100,7 +100,7 @@ struct sk_buff *ath12k_dp_rx_alloc_mon_status_buf(struct ath12k_base *ab, struct dp_rxdma_mon_ring *rx_ring, int *buf_id); -u32 ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id); +bool ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id); int ath12k_dp_mon_parse_status_buf(struct ath12k_pdev_dp *dp_pdev, struct ath12k_mon_data *pmon, From febe1f8cbccc3434790bfc4d94d40c9dbd803065 Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Wed, 16 Sep 2026 14:03:25 +0800 Subject: [PATCH 13/27] wifi: ath12k: avoid double DMA unmap of held monitor RX buffers ath12k_dp_cc_cleanup() assumes that every rx_desc_info entry with a non-NULL skb still has an active DMA mapping and therefore always calls dma_unmap_single() before freeing the skb. This assumption is not true for the monitor RX path. ath12k_wifi7_dp_rx_mon_mpdu_pop() may DMA-unmap a buffer and mark rxcb->unmapped before returning early to hold the MSDU for later reprocessing. In that state desc_info->skb remains populated, so module removal can trigger a second dma_unmap_single() from ath12k_dp_cc_cleanup(). IOMMU reports the issue as: dma_unmap_phys() dma_unmap_page_attrs() ath12k_dp_cc_cleanup() ath12k_dp_cmn_device_deinit() ath12k_core_stop() Skip dma_unmap_single() when rxcb->unmapped is already set and free the skb directly. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: 72bfbf19b7da ("wifi: ath12k: add support to reap and process mon dest ring") Signed-off-by: Kang Yang Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20260916060325.854-7-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index cce5386c5bd5..83f9edfabf69 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1002,10 +1002,11 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) if (!skb) continue; - dma_unmap_single(ab->dev, - ATH12K_SKB_RXCB(skb)->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); + if (!ATH12K_SKB_RXCB(skb)->unmapped) + dma_unmap_single(ab->dev, + ATH12K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); dev_kfree_skb_any(skb); } From 4e286c780d75831a169b0489afd015214838affd Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 30 Jul 2026 14:17:40 +0800 Subject: [PATCH 14/27] wifi: ath12k: use per-device max QMI chunk size When a target memory chunk fails to allocate, ath12k_qmi_alloc_chunk() compares the requested chunk size against ATH12K_QMI_MAX_CHUNK_SIZE to decide whether to ask the firmware to retry with smaller chunks (-EAGAIN) or to give up (-ENOMEM). This threshold is the boundary that tells a first (big) request apart from a retry (small) request, so it has to match the size the firmware actually falls back to on retry. That fallback size is not the same across all firmware, though. On WCN7850 and QCC2072 the firmware splits the request into 512 KB chunks on retry, whereas on QCN9274, IPQ5332 and IPQ5424 the firmware retries with larger 2 MB segments instead of 512 KB. A single hardcoded 2 MB threshold is therefore wrong for the former, and a hardcoded 512 KB threshold would be wrong for the latter. Replace the hardcoded ATH12K_QMI_MAX_CHUNK_SIZE with a per-device qmi_max_chunk_size parameter: SZ_512K for WCN7850 and QCC2072, SZ_2M for QCN9274, IPQ5332 and IPQ5424. No functional change for the latter (the value stays 2 MB); the former now use the correct 512 KB boundary. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260730-ath12k-align-qmi-memory-v1-1-0fef98dda614@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/hw.h | 1 + drivers/net/wireless/ath/ath12k/qmi.c | 3 +-- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index e790de5737db..1e652dc0d247 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -166,6 +166,7 @@ struct ath12k_hw_params { u8 max_radios; bool single_pdev_only:1; u32 qmi_service_ins_id; + u32 qmi_max_chunk_size; bool internal_sleep_clock:1; const struct ath12k_hw_ops *hw_ops; diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 50082505c882..96c82aa46fc4 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -18,7 +18,6 @@ #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 #define HOST_CSTATE_BIT 0x04 #define PLATFORM_CAP_PCIE_GLOBAL_RESET 0x08 -#define ATH12K_QMI_MAX_CHUNK_SIZE 2097152 static const struct qmi_elem_info wlfw_host_mlo_chip_info_s_v01_ei[] = { { @@ -2655,7 +2654,7 @@ static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab, &chunk->paddr, GFP_KERNEL | __GFP_NOWARN); if (!chunk->v.addr) { - if (chunk->size > ATH12K_QMI_MAX_CHUNK_SIZE) { + if (chunk->size > ab->hw_params->qmi_max_chunk_size) { ab->qmi.target_mem_delayed = true; ath12k_warn(ab, "qmi dma allocation failed (%u B type %u), will try later with small size\n", diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index ae8df80928b0..aec7c652808c 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "../debug.h" #include "../core.h" @@ -369,6 +370,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_radios = 1, .single_pdev_only = false, .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9274, + .qmi_max_chunk_size = SZ_2M, .internal_sleep_clock = false, .hw_ops = &qcn9274_ops, @@ -461,6 +463,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_radios = 1, .single_pdev_only = true, .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850, + .qmi_max_chunk_size = SZ_512K, .internal_sleep_clock = true, .hw_ops = &wcn7850_ops, @@ -553,6 +556,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_radios = 2, .single_pdev_only = false, .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9274, + .qmi_max_chunk_size = SZ_2M, .internal_sleep_clock = false, .hw_ops = &qcn9274_ops, @@ -643,6 +647,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_radios = 1, .single_pdev_only = false, .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332, + .qmi_max_chunk_size = SZ_2M, .internal_sleep_clock = false, .hw_ops = &qcn9274_ops, @@ -729,6 +734,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_radios = 1, .single_pdev_only = true, .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850, + .qmi_max_chunk_size = SZ_512K, .internal_sleep_clock = true, .hw_ops = &qcc2072_ops, @@ -822,6 +828,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .max_radios = 1, .single_pdev_only = false, .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332, + .qmi_max_chunk_size = SZ_2M, .internal_sleep_clock = false, .hw_ops = &qcn9274_ops, From a1af8645cf2dcdac5c6b3929e1dcb2274ca9f27e Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 30 Jul 2026 14:17:41 +0800 Subject: [PATCH 15/27] wifi: ath12k: clear chunk paddr and size in ath12k_qmi_free_target_mem_chunk() paddr and size of a chunk are not cleared after the chunk is freed, which is safe now since the code checks vaddr only and it is properly cleared on free. However, leaving stale values can mislead diagnostics and future maintenance. Clear both to reduce such risk, this also aligns with ath12k_qmi_free_mlo_mem_chunk(). Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260730-ath12k-align-qmi-memory-v1-2-0fef98dda614@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/qmi.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 96c82aa46fc4..201e58de0860 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2602,27 +2602,33 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) { struct ath12k_hw_group *ag = ab->ag; + struct target_mem_chunk *chunk; int i, mlo_idx; for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) { - if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) { + chunk = &ab->qmi.target_mem[i]; + + if (chunk->type == MLO_GLOBAL_MEM_REGION_TYPE) { ath12k_qmi_free_mlo_mem_chunk(ab, - &ab->qmi.target_mem[i], + chunk, mlo_idx++); } else { if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) && - ab->qmi.target_mem[i].v.ioaddr) { - iounmap(ab->qmi.target_mem[i].v.ioaddr); - ab->qmi.target_mem[i].v.ioaddr = NULL; + chunk->v.ioaddr) { + iounmap(chunk->v.ioaddr); + chunk->v.ioaddr = NULL; } else { - if (!ab->qmi.target_mem[i].v.addr) + if (!chunk->v.addr) continue; dma_free_coherent(ab->dev, - ab->qmi.target_mem[i].prev_size, - ab->qmi.target_mem[i].v.addr, - ab->qmi.target_mem[i].paddr); - ab->qmi.target_mem[i].v.addr = NULL; + chunk->prev_size, + chunk->v.addr, + chunk->paddr); + chunk->v.addr = NULL; } + + chunk->paddr = 0; + chunk->size = 0; } } From f7f05c443361ebaa503e054e882633913273b3fa Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 30 Jul 2026 14:17:42 +0800 Subject: [PATCH 16/27] wifi: ath12k: align QMI target memory to 64 KB ath12k_qmi_alloc_chunk() allocates the target memory chunks the firmware requests via dma_alloc_coherent() and hands the resulting physical addresses to the firmware. The firmware does not require every chunk to be aligned, but each unaligned chunk consumes extra TLB entries when the firmware maps it, and too many unaligned chunks exhaust the TLB and make the firmware crash. The firmware team recommends aligning the chunks to 64 KB so that the TLB stays within budget. dma_alloc_coherent() only guarantees page alignment, so a chunk can end up unaligned. Align the physical address handed to the firmware to 64 KB. A natural allocation is often already 64 KB aligned, so try the exact size first and keep it when it happens to be aligned; only when it is not, free it and over-allocate by SZ_64K - PAGE_SIZE and round the base up. Since the DMA allocator already guarantees page alignment, that padding is enough to reach the next 64 KB boundary. Record the raw allocation in paddr_unaligned/vaddr_unaligned/total_size so the chunk can still be freed correctly. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260730-ath12k-align-qmi-memory-v1-3-0fef98dda614@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/qmi.c | 79 +++++++++++++++++++++------ drivers/net/wireless/ath/ath12k/qmi.h | 4 ++ 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 201e58de0860..817bd820623d 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -14,6 +14,7 @@ #include #include #include +#include #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 #define HOST_CSTATE_BIT 0x04 @@ -2583,20 +2584,28 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, mlo_chunk->v.ioaddr = NULL; } else if (mlo_chunk->v.addr) { dma_free_coherent(ab->dev, - mlo_chunk->size, - mlo_chunk->v.addr, - mlo_chunk->paddr); + mlo_chunk->total_size, + mlo_chunk->vaddr_unaligned, + mlo_chunk->paddr_unaligned); + mlo_chunk->vaddr_unaligned = NULL; mlo_chunk->v.addr = NULL; } mlo_chunk->paddr = 0; + mlo_chunk->paddr_unaligned = 0; mlo_chunk->size = 0; - if (fixed_mem) + mlo_chunk->total_size = 0; + + if (fixed_mem) { chunk->v.ioaddr = NULL; - else + } else { chunk->v.addr = NULL; + chunk->vaddr_unaligned = NULL; + } chunk->paddr = 0; + chunk->paddr_unaligned = 0; chunk->size = 0; + chunk->total_size = 0; } static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) @@ -2621,14 +2630,17 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) if (!chunk->v.addr) continue; dma_free_coherent(ab->dev, - chunk->prev_size, - chunk->v.addr, - chunk->paddr); + chunk->total_size, + chunk->vaddr_unaligned, + chunk->paddr_unaligned); + chunk->vaddr_unaligned = NULL; chunk->v.addr = NULL; } chunk->paddr = 0; + chunk->paddr_unaligned = 0; chunk->size = 0; + chunk->total_size = 0; } } @@ -2641,6 +2653,10 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab, struct target_mem_chunk *chunk) { + dma_addr_t paddr; + size_t size; + void *vaddr; + /* Firmware reloads in recovery/resume. * In such cases, no need to allocate memory for FW again. */ @@ -2650,29 +2666,56 @@ static int ath12k_qmi_alloc_chunk(struct ath12k_base *ab, goto this_chunk_done; /* cannot reuse the existing chunk */ - dma_free_coherent(ab->dev, chunk->prev_size, - chunk->v.addr, chunk->paddr); + dma_free_coherent(ab->dev, chunk->total_size, + chunk->vaddr_unaligned, chunk->paddr_unaligned); chunk->v.addr = NULL; + chunk->vaddr_unaligned = NULL; + chunk->paddr_unaligned = 0; + chunk->total_size = 0; } - chunk->v.addr = dma_alloc_coherent(ab->dev, - chunk->size, - &chunk->paddr, + /* + * Each unaligned chunk costs the firmware extra TLB entries when + * mapping it, and too many unaligned chunks exhaust the TLB and + * crash the firmware, so align the base to 64 KB. The DMA + * allocator only guarantees page alignment, but a natural + * allocation is often already 64 KB aligned. Try the exact size + * first and keep it when it happens to be aligned; only fall back + * to over-allocating and rounding up when it is not. + */ + size = chunk->size; + vaddr = dma_alloc_coherent(ab->dev, size, &paddr, + GFP_KERNEL | __GFP_NOWARN); + if (vaddr && !IS_ALIGNED(paddr, SZ_64K)) { + dma_free_coherent(ab->dev, size, vaddr, paddr); + + size = chunk->size + SZ_64K - PAGE_SIZE; + vaddr = dma_alloc_coherent(ab->dev, size, &paddr, GFP_KERNEL | __GFP_NOWARN); - if (!chunk->v.addr) { + } + + if (!vaddr) { if (chunk->size > ab->hw_params->qmi_max_chunk_size) { ab->qmi.target_mem_delayed = true; ath12k_warn(ab, - "qmi dma allocation failed (%u B type %u), will try later with small size\n", - chunk->size, + "qmi dma allocation failed (%zu B type %u), will try later with small size\n", + size, chunk->type); ath12k_qmi_free_target_mem_chunk(ab); return -EAGAIN; } - ath12k_warn(ab, "memory allocation failure for %u size: %u\n", - chunk->type, chunk->size); + ath12k_warn(ab, "memory allocation failure for %u size: %zu\n", + chunk->type, size); return -ENOMEM; } + + chunk->vaddr_unaligned = vaddr; + chunk->paddr_unaligned = paddr; + chunk->total_size = size; + + chunk->paddr = ALIGN(paddr, SZ_64K); + chunk->v.addr = (u8 *)vaddr + (chunk->paddr - paddr); + chunk->prev_type = chunk->type; chunk->prev_size = chunk->size; this_chunk_done: diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index 6da10f3cb597..1b1c88906f1e 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -100,6 +100,10 @@ struct target_mem_chunk { void __iomem *ioaddr; void *addr; } v; + + dma_addr_t paddr_unaligned; + void *vaddr_unaligned; + u32 total_size; }; struct target_info { From 64828f091c7de5c1324427b140fb545f93ef073f Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 17 Aug 2026 16:46:34 -0700 Subject: [PATCH 17/27] wifi: ath11k: fix reg_info_store leak in ath11k_service_ready_ext_event() Currently, while processing the WMI Service Ready Ext event, the iterator in ath11k_service_ready_ext_event() can dispatch the HAL Regulatory Capabilities handler ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse() which can allocate ab->reg_info_store. If a subsequent failure occurs during iteration, the ab->reg_info_store allocation is leaked. Directly free and clear the pointer on the error path. Note that the normal cleanup function ath11k_reg_free() cannot be used since other regulatory-related fields are not yet populated at this point. Compile tested only. Fixes: 7004bdceef60 ("wifi: ath11k: store cur_regulatory_info for each radio") Assisted-by: Claude:claude-sonnet-4-6 Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260817-reg_info_store-leak-v1-1-77025a99b074@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index bbca275a8289..08fd6795ed9e 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -5136,6 +5136,15 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab, err: kfree(svc_rdy_ext.mac_phy_caps); + + /* + * During TLV iteration the WMI_TAG_SOC_HAL_REG_CAPABILITIES + * handler may have allocated ab->reg_info_store, so free it + * on error to avoid a memory leak + */ + kfree(ab->reg_info_store); + ab->reg_info_store = NULL; + ath11k_wmi_free_dbring_caps(ab); return ret; } From e1ef274a49873a9545b51e36f1f1d25707372872 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Mon, 3 Aug 2026 09:05:32 +0800 Subject: [PATCH 18/27] wifi: ath12k: remove unused ath12k_dp_rx_h_find_link_peer() ath12k_dp_rx_h_find_link_peer() is no longer referenced anywhere, remove the unused helper and its declaration to avoid dead code. Tested-on: QCC2072 hw1.0 PCI CI_WLAN.COL.1.0.c2-00229.1-QCACOLSWPL_V1_TO_SILICON-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Signed-off-by: Miaoqing Pan Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260803010532.302038-1-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 21 --------------------- drivers/net/wireless/ath/ath12k/dp_rx.h | 3 --- 2 files changed, 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 8bb2b57aed15..cfaf26ae2720 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1204,27 +1204,6 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu } EXPORT_SYMBOL(ath12k_dp_rx_h_undecap); -struct ath12k_dp_link_peer * -ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, - struct hal_rx_desc_data *rx_info) -{ - struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - struct ath12k_dp_link_peer *peer = NULL; - struct ath12k_dp *dp = dp_pdev->dp; - - lockdep_assert_held(&dp->dp_lock); - - peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); - - if (peer) - return peer; - - if (rx_info->addr2_present) - peer = ath12k_dp_link_peer_find_by_addr(dp, rx_info->addr2); - - return peer; -} - static void ath12k_dp_rx_h_rate(struct ath12k_pdev_dp *dp_pdev, struct hal_rx_desc_data *rx_info) { diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 0660db070e92..82700474548a 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -237,9 +237,6 @@ int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev u8 ath12k_dp_rx_h_l3pad(struct ath12k_base *ab, struct hal_rx_desc *desc); -struct ath12k_dp_link_peer * -ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, - struct hal_rx_desc_data *rx_info); u8 ath12k_dp_rx_h_decap_type(struct ath12k_base *ab, struct hal_rx_desc *desc); u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab, From c8f83d3389d1d1b318ac3bd2ae3d60c2862c90b4 Mon Sep 17 00:00:00 2001 From: Pardeep Kaur Date: Wed, 19 Aug 2026 11:02:40 +0530 Subject: [PATCH 19/27] wifi: ath12k: fix out-of-bounds access on TX stats arrays The fw_tx_status[], tx_wbm_rel_source[], and tqm_rel_reason[] arrays are indexed directly by values read from hardware without bounds checks. Values at or beyond MAX_FW_TX_STATUS, HAL_WBM_REL_SRC_MODULE_MAX, or MAX_TQM_RELEASE_REASON respectively would write past the end of the arrays causing memory corruption. Add likely() bounds checks before incrementing each counter. For tx_wbm_rel_source[] and tqm_rel_reason[], add WARN_ON_ONCE() on the out-of-bounds else branch. For fw_tx_status[], no WARN_ON_ONCE is added since the switch statement that follows already calls ath12k_warn() in its default: branch for unknown values, avoiding a double-warn. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Fixes: c5c62287e690 ("wifi: ath12k: Add device dp stats support") Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-2-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 3f6df96de160..4ce0948d429f 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -589,7 +589,8 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_dp *dp, void *desc, wbm_status = le32_get_bits(status_desc->info0, HTT_TX_WBM_COMP_INFO0_STATUS); - dp->device_stats.fw_tx_status[wbm_status]++; + if (likely(wbm_status < MAX_FW_TX_STATUS)) + dp->device_stats.fw_tx_status[wbm_status]++; switch (wbm_status) { case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: @@ -993,11 +994,17 @@ void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id) /* Find the HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE value */ buf_rel_source = le32_get_bits(tx_status->info0, HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE); - dp->device_stats.tx_wbm_rel_source[buf_rel_source]++; + if (likely(buf_rel_source < HAL_WBM_REL_SRC_MODULE_MAX)) + dp->device_stats.tx_wbm_rel_source[buf_rel_source]++; + else + WARN_ON_ONCE(1); rel_status = le32_get_bits(tx_status->info0, HAL_WBM_COMPL_TX_INFO0_TQM_RELEASE_REASON); - dp->device_stats.tqm_rel_reason[rel_status]++; + if (likely(rel_status < MAX_TQM_RELEASE_REASON)) + dp->device_stats.tqm_rel_reason[rel_status]++; + else + WARN_ON_ONCE(1); /* Release descriptor as soon as extracting necessary info * to reduce contention From 90396f4a01fe2e14c2a31540a73fde4c7c1cc8d5 Mon Sep 17 00:00:00 2001 From: Hariharan Ramanathan Date: Wed, 19 Aug 2026 11:02:41 +0530 Subject: [PATCH 20/27] wifi: ath12k: rename wbm_status to htt_status in HTT TX completion The variable wbm_status in ath12k_dp_tx_process_htt_tx_complete() holds a value from the HTT TX WBM completion status field, not a generic WBM status. Rename it to htt_status to accurately reflect the field origin and improve code clarity. Also fix ts.acked assignment inside the HAL_WBM_REL_HTT_TX_COMP_STATUS_OK case to use true directly since the condition is always true within that case branch. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hariharan Ramanathan Co-developed-by: Pardeep Kaur Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-3-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 4ce0948d429f..962abe7f3089 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -582,19 +582,19 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_dp *dp, void *desc, { struct htt_tx_wbm_completion *status_desc; struct ath12k_dp_htt_wbm_tx_status ts = {}; - enum hal_wbm_htt_tx_comp_status wbm_status; + enum hal_wbm_htt_tx_comp_status htt_status; u16 peer_id; status_desc = desc; - wbm_status = le32_get_bits(status_desc->info0, + htt_status = le32_get_bits(status_desc->info0, HTT_TX_WBM_COMP_INFO0_STATUS); - if (likely(wbm_status < MAX_FW_TX_STATUS)) - dp->device_stats.fw_tx_status[wbm_status]++; + if (likely(htt_status < MAX_FW_TX_STATUS)) + dp->device_stats.fw_tx_status[htt_status]++; - switch (wbm_status) { + switch (htt_status) { case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: - ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); + ts.acked = true; ts.ack_rssi = le32_get_bits(status_desc->info2, HTT_TX_WBM_COMP_INFO2_ACK_RSSI); @@ -616,7 +616,7 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_dp *dp, void *desc, */ break; default: - ath12k_warn(dp->ab, "Unknown htt wbm tx status %d\n", wbm_status); + ath12k_warn(dp->ab, "Unknown htt tx status %d\n", htt_status); break; } } From 6fec999446d926e1f5d2c0efc306134f64f29686 Mon Sep 17 00:00:00 2001 From: Hariharan Ramanathan Date: Wed, 19 Aug 2026 11:02:42 +0530 Subject: [PATCH 21/27] wifi: ath12k: add TCL ring TX buffer allocation failure counter When ath12k_dp_tx_assign_buffer() fails, the TX path returns -ENOMEM silently with no visibility into which traffic class is affected. Without a counter, TX descriptor pool exhaustion is invisible during debugging. Add txbuf_na[] indexed by pool_id to ath12k_device_dp_tx_err_stats to track TX descriptor pool allocation failures per traffic class and increment it on assign failure in the TX path. Expose the per-pool counts in debugfs under a new 'TX Descriptor Pool Alloc Failures' section. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hariharan Ramanathan Co-developed-by: Pardeep Kaur Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-4-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs.c | 5 +++++ drivers/net/wireless/ath/ath12k/dp.h | 2 ++ drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index d54995b7adb2..ec49692107a8 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1117,6 +1117,11 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, len += scnprintf(buf + len, size - len, "ring%d: %u\n", i, device_stats->tx_err.desc_na[i]); + len += scnprintf(buf + len, size - len, "\nTX Descriptor Pool Alloc Failures:\n"); + for (i = 0; i < ATH12K_HW_MAX_QUEUES; i++) + len += scnprintf(buf + len, size - len, "pool%d: %u\n", + i, device_stats->tx_err.txbuf_na[i]); + len += scnprintf(buf + len, size - len, "\nMisc Transmit Failures: %d\n", atomic_read(&device_stats->tx_err.misc_fail)); diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index ff62773cfa9a..230a74d48c8b 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -409,6 +409,8 @@ struct ath12k_dp_arch_ops { struct ath12k_device_dp_tx_err_stats { /* TCL Ring Descriptor unavailable */ u32 desc_na[DP_TCL_NUM_RING_MAX]; + /* TX descriptor pool exhausted (per pool_id / traffic class) */ + u32 txbuf_na[ATH12K_HW_MAX_QUEUES]; /* Other failures during dp_tx due to mem allocation failure * idr unavailable etc. */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 962abe7f3089..41abf1865702 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -167,8 +167,10 @@ tcl_ring_sel: tx_ring = &dp->tx_ring[ti.ring_id]; tx_desc = ath12k_dp_tx_assign_buffer(dp, pool_id); - if (!tx_desc) + if (!tx_desc) { + dp->device_stats.tx_err.txbuf_na[pool_id]++; return -ENOMEM; + } dp_link_vif = ath12k_dp_vif_to_dp_link_vif(&ahvif->dp_vif, arvif->link_id); From 26e095b0801ab6d24cf20b37eb377bae3ff39715 Mon Sep 17 00:00:00 2001 From: Hariharan Ramanathan Date: Wed, 19 Aug 2026 11:02:43 +0530 Subject: [PATCH 22/27] wifi: ath12k: add device DP stats reset support via debugfs There is no way to reset device DP stats counters without reloading the driver, making it difficult to isolate issues to a specific time window during debugging. Add a write handler to the device_dp_stats debugfs file so that writing 'reset' clears all device DP stats counters. Change the file mode from 0400 to 0600 to allow write access. Use simple_write_to_buffer() to correctly handle partial writes and non-zero ppos, consistent with ath12k_write_simulate_fw_crash(). No lock is taken around the memset since the counters are updated locklessly in the datapath; taking dp_lock would be misleading as it does not protect device_stats updates. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hariharan Ramanathan Co-developed-by: Pardeep Kaur Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-5-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs.c | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index ec49692107a8..68acb4cb9c4f 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1220,8 +1220,40 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, return ret; } +static ssize_t +ath12k_debugfs_write_device_dp_stats(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k_base *ab = file->private_data; + struct ath12k_dp *dp = ath12k_ab_to_dp(ab); + struct ath12k_device_dp_stats *device_stats = &dp->device_stats; + char buf[20] = {}; + int ret; + + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (ret < 0) + return ret; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + if (!strcmp(buf, "reset")) { + memset(device_stats, 0, sizeof(*device_stats)); + return count; + } + + return -EINVAL; +} + static const struct file_operations fops_device_dp_stats = { .read = ath12k_debugfs_dump_device_dp_stats, + .write = ath12k_debugfs_write_device_dp_stats, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -1232,7 +1264,7 @@ void ath12k_debugfs_pdev_create(struct ath12k_base *ab) debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, &fops_simulate_fw_crash); - debugfs_create_file("device_dp_stats", 0400, ab->debugfs_soc, ab, + debugfs_create_file("device_dp_stats", 0600, ab->debugfs_soc, ab, &fops_device_dp_stats); } From 0f4f1fe45e4b49311e9cd0fffdd3290f57b5afe1 Mon Sep 17 00:00:00 2001 From: Aniruddha Mishra Date: Wed, 19 Aug 2026 11:02:44 +0530 Subject: [PATCH 23/27] wifi: ath12k: add WBM RX error drop statistics Without fine-grained drop counters, diagnosing RX failures in the WBM error path requires intrusive debugging. Introduce per-drop-reason counters to track exactly where and why packets are dropped during WBM RX error handling. Introduce a new dp_stats.h header and define ath12k_wbm_err_drop_reason enum there covering all drop points in the WBM RX error path: descriptor parse failures, SW descriptor retrieval errors, null partner DP, invalid HW link ID, null pdev/ar, CAC running, scatter-gather drops, and invalid NWifi header length. The new header is included by dp.h which is already included by all consumers. Add ath12k_device_dp_rx_wbm_err_stats struct grouping rxdma_error[], reo_error[], and the new drop[] counters, replacing the flat arrays previously in ath12k_device_dp_stats. Add bounds checks before indexing rxdma_error[] and reo_error[] with hardware-supplied err_code values to prevent out-of-bounds writes, using the same likely/WARN_ON_ONCE pattern as the TX stats bounds fixes. Add ath12k_wifi7_dp_rx_wbm_err_free_skb() helper to combine drop stat increment and skb free at each drop site, reducing repetition. Define the helper at the top of dp_rx.c before its first use. Increment WBM_ERR_DROP_RXDMA_GENERIC only inside rxdma_err()'s default: branch so it counts only unhandled RXDMA errors, not every RXDMA drop - consistent with how WBM_ERR_DROP_REO_GENERIC is counted. Expose WBM RX drop counts in the device_dp_stats debugfs file under a new 'WBM Rx Drop Count' section. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aniruddha Mishra Co-developed-by: Pardeep Kaur Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-6-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs.c | 23 ++++++++- drivers/net/wireless/ath/ath12k/dp.h | 10 +++- drivers/net/wireless/ath/ath12k/dp_stats.h | 25 ++++++++++ drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 47 +++++++++++++++---- .../net/wireless/ath/ath12k/wifi7/hal_rx.c | 5 +- 5 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 drivers/net/wireless/ath/ath12k/dp_stats.h diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 68acb4cb9c4f..935da8d06450 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1071,6 +1071,20 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, [HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET] = "PN err", [HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED] = "Desc blocked"}; + static const char *wbm_rx_drop[WBM_ERR_DROP_MAX] = { + [WBM_ERR_DROP_GET_SW_DESC] = "SW desc error", + [WBM_ERR_DROP_DESC_PARSE] = "Desc parse error", + [WBM_ERR_DROP_INV_HW_ID] = "Invalid hw id", + [WBM_ERR_DROP_NULL_PRTNR_DP] = "Null Partner dp", + [WBM_ERR_DROP_NULL_PROC_DP] = "Process Null Partner dp", + [WBM_ERR_DROP_NULL_PDEV] = "Null Pdev", + [WBM_ERR_DROP_NULL_AR] = "Null ar", + [WBM_ERR_DROP_CAC_RUNNING] = "CAC Running", + [WBM_ERR_DROP_SG] = "Scatter Gather", + [WBM_ERR_DROP_INV_NWIFI_HDR] = "Invalid NWifi Hdr len", + [WBM_ERR_DROP_REO_GENERIC] = "REO Generic", + [WBM_ERR_DROP_RXDMA_GENERIC] = "RXDMA Generic"}; + static const char *wbm_rel_src[HAL_WBM_REL_SRC_MODULE_MAX] = { [HAL_WBM_REL_SRC_MODULE_TQM] = "TQM", [HAL_WBM_REL_SRC_MODULE_RXDMA] = "Rxdma", @@ -1095,13 +1109,18 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) len += scnprintf(buf + len, size - len, "%s: %u\n", - rxdma_err[i], device_stats->rxdma_error[i]); + rxdma_err[i], device_stats->wbm_err.rxdma_error[i]); len += scnprintf(buf + len, size - len, "\nREO errors:\n"); for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) len += scnprintf(buf + len, size - len, "%s: %u\n", - reo_err[i], device_stats->reo_error[i]); + reo_err[i], device_stats->wbm_err.reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nWBM Rx Drop Count:\n"); + for (i = 0; i < WBM_ERR_DROP_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + wbm_rx_drop[i], device_stats->wbm_err.drop[i]); len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 230a74d48c8b..3765bcd09c51 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -7,6 +7,7 @@ #ifndef ATH12K_DP_H #define ATH12K_DP_H +#include "dp_stats.h" #include "hw.h" #include "dp_htt.h" #include "dp_cmn.h" @@ -417,13 +418,18 @@ struct ath12k_device_dp_tx_err_stats { atomic_t misc_fail; }; +struct ath12k_device_dp_rx_wbm_err_stats { + u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; + u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; + u32 drop[WBM_ERR_DROP_MAX]; +}; + struct ath12k_device_dp_stats { u32 err_ring_pkts; u32 invalid_rbm; - u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; - u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; u32 hal_reo_error[DP_REO_DST_RING_MAX]; struct ath12k_device_dp_tx_err_stats tx_err; + struct ath12k_device_dp_rx_wbm_err_stats wbm_err; u32 reo_rx[DP_REO_DST_RING_MAX][ATH12K_MAX_DEVICES]; u32 rx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX][ATH12K_MAX_DEVICES]; u32 tqm_rel_reason[MAX_TQM_RELEASE_REASON]; diff --git a/drivers/net/wireless/ath/ath12k/dp_stats.h b/drivers/net/wireless/ath/ath12k/dp_stats.h new file mode 100644 index 000000000000..1a05256dbf74 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/dp_stats.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef ATH12K_DP_STATS_H +#define ATH12K_DP_STATS_H + +enum ath12k_wbm_err_drop_reason { + WBM_ERR_DROP_GET_SW_DESC, + WBM_ERR_DROP_DESC_PARSE, + WBM_ERR_DROP_INV_HW_ID, + WBM_ERR_DROP_NULL_PRTNR_DP, + WBM_ERR_DROP_NULL_PROC_DP, + WBM_ERR_DROP_NULL_PDEV, + WBM_ERR_DROP_NULL_AR, + WBM_ERR_DROP_CAC_RUNNING, + WBM_ERR_DROP_SG, + WBM_ERR_DROP_INV_NWIFI_HDR, + WBM_ERR_DROP_REO_GENERIC, + WBM_ERR_DROP_RXDMA_GENERIC, + WBM_ERR_DROP_MAX, +}; + +#endif /* ATH12K_DP_STATS_H */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 8b722ee0ad8f..721eb25be1a9 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -12,6 +12,15 @@ #include "hal_wcn7850.h" #include "hal_qcc2072.h" +static void +ath12k_wifi7_dp_rx_wbm_err_free_skb(struct ath12k_dp *dp, + struct sk_buff *msdu, + enum ath12k_wbm_err_drop_reason drop_reason) +{ + dp->device_stats.wbm_err.drop[drop_reason]++; + dev_kfree_skb_any(msdu); +} + static u16 ath12k_wifi7_dp_rx_get_peer_id(struct ath12k_dp *dp, enum ath12k_peer_metadata_version ver, __le32 peer_metadata) @@ -642,7 +651,6 @@ ath12k_wifi7_dp_rx_process_received_packets(struct ath12k_dp *dp, dev_kfree_skb_any(msdu); continue; } - ath12k_dp_rx_deliver_msdu(dp_pdev, napi, msdu, &rx_info); } @@ -1614,6 +1622,7 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, /* First buffer will be freed by the caller, so deduct it's length */ msdu_len = msdu_len - (DP_RX_BUFFER_SIZE - hal_rx_desc_sz); ath12k_wifi7_dp_rx_null_q_desc_sg_drop(dp, msdu_len, msdu_list); + dp->device_stats.wbm_err.drop[WBM_ERR_DROP_SG]++; return -EINVAL; } @@ -1649,8 +1658,10 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } - if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, rx_info))) + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, rx_info))) { + dp->device_stats.wbm_err.drop[WBM_ERR_DROP_INV_NWIFI_HDR]++; return -EINVAL; + } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); @@ -1763,7 +1774,10 @@ static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); bool drop = false; - dp->device_stats.rxdma_error[rxcb->err_code]++; + if (likely(rxcb->err_code < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX)) + dp->device_stats.wbm_err.rxdma_error[rxcb->err_code]++; + else + WARN_ON_ONCE(1); switch (rxcb->err_code) { case HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR: @@ -1780,6 +1794,7 @@ static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, /* TODO: Review other rxdma error code to check if anything is * worth reporting to mac80211 */ + dp->device_stats.wbm_err.drop[WBM_ERR_DROP_RXDMA_GENERIC]++; drop = true; break; } @@ -1796,7 +1811,10 @@ static bool ath12k_wifi7_dp_rx_h_reo_err(struct ath12k_pdev_dp *dp_pdev, struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); bool drop = false; - dp->device_stats.reo_error[rxcb->err_code]++; + if (likely(rxcb->err_code < HAL_REO_DEST_RING_ERROR_CODE_MAX)) + dp->device_stats.wbm_err.reo_error[rxcb->err_code]++; + else + WARN_ON_ONCE(1); switch (rxcb->err_code) { case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO: @@ -1813,6 +1831,7 @@ static bool ath12k_wifi7_dp_rx_h_reo_err(struct ath12k_pdev_dp *dp_pdev, /* TODO: Review other errors and process them to mac80211 * as appropriate. */ + dp->device_stats.wbm_err.drop[WBM_ERR_DROP_REO_GENERIC]++; drop = true; break; } @@ -1935,6 +1954,7 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, ret = ath12k_wifi7_hal_wbm_desc_parse_err(dp, rx_desc, &err_info); if (ret) { + dp->device_stats.wbm_err.drop[WBM_ERR_DROP_DESC_PARSE]++; ath12k_warn(ab, "failed to parse rx error in wbm_rel ring desc %d\n", ret); continue; @@ -1946,6 +1966,7 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, if (!desc_info) { desc_info = ath12k_dp_get_rx_desc(dp, err_info.cookie); if (!desc_info) { + dp->device_stats.wbm_err.drop[WBM_ERR_DROP_GET_SW_DESC]++; ath12k_warn(ab, "Invalid cookie in DP WBM rx error descriptor retrieval: 0x%x\n", err_info.cookie); continue; @@ -1961,7 +1982,8 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, device_id = desc_info->device_id; partner_dp = ath12k_dp_hw_grp_to_dp(dp_hw_grp, device_id); if (unlikely(!partner_dp)) { - dev_kfree_skb_any(msdu); + ath12k_wifi7_dp_rx_wbm_err_free_skb(dp, msdu, + WBM_ERR_DROP_NULL_PRTNR_DP); /* In any case continuation bit is set * in the previous record, cleanup scatter_msdu_list @@ -2007,7 +2029,8 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, hw_link_id = ath12k_dp_rx_get_msdu_src_link(partner_dp->hal, msdu_data); if (hw_link_id >= ATH12K_GROUP_MAX_RADIO) { - dev_kfree_skb_any(msdu); + ath12k_wifi7_dp_rx_wbm_err_free_skb(dp, msdu, + WBM_ERR_DROP_INV_HW_ID); /* In any case continuation bit is set * in the previous record, cleanup scatter_msdu_list @@ -2068,7 +2091,8 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, ath12k_dbg(ab, ATH12K_DBG_DATA, "Unable to process WBM error msdu due to invalid hw link id %d device id %d\n", hw_link_id, device_id); - dev_kfree_skb_any(msdu); + ath12k_wifi7_dp_rx_wbm_err_free_skb(dp, msdu, + WBM_ERR_DROP_NULL_PROC_DP); continue; } @@ -2077,18 +2101,21 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, dp_pdev = ath12k_dp_to_pdev_dp(partner_dp, pdev_idx); if (!dp_pdev) { - dev_kfree_skb_any(msdu); + ath12k_wifi7_dp_rx_wbm_err_free_skb(dp, msdu, + WBM_ERR_DROP_NULL_PDEV); continue; } ar = ath12k_pdev_dp_to_ar(dp_pdev); if (!ar || !rcu_dereference(ar->ab->pdevs_active[pdev_idx])) { - dev_kfree_skb_any(msdu); + ath12k_wifi7_dp_rx_wbm_err_free_skb(dp, msdu, + WBM_ERR_DROP_NULL_AR); continue; } if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) { - dev_kfree_skb_any(msdu); + ath12k_wifi7_dp_rx_wbm_err_free_skb(dp, msdu, + WBM_ERR_DROP_CAC_RUNNING); continue; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c index 49c693289709..60e7a36097f1 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c @@ -329,7 +329,10 @@ int ath12k_wifi7_hal_desc_reo_parse_err(struct ath12k_dp *dp, HAL_REO_DEST_RING_INFO0_PUSH_REASON); err_code = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_ERROR_CODE); - dp->device_stats.reo_error[err_code]++; + if (likely(err_code < HAL_REO_DEST_RING_ERROR_CODE_MAX)) + dp->device_stats.wbm_err.reo_error[err_code]++; + else + WARN_ON_ONCE(1); if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED && push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { From 7acba3c0a67cbe65d9bca8437e01065e778ebf8c Mon Sep 17 00:00:00 2001 From: Hariharan Ramanathan Date: Wed, 19 Aug 2026 11:02:45 +0530 Subject: [PATCH 24/27] wifi: ath12k: track per-ring RX sent-to-stack count The device DP stats provide visibility into RX errors and drops but lack a counter for successfully delivered packets. Without this, it is difficult to correlate REO ring activity with actual stack delivery during debugging or performance analysis. Add sent_to_stack[DP_REO_DST_RING_MAX][ATH12K_MAX_DEVICES] to ath12k_device_dp_stats to track successful deliveries per REO ring per device. The counter is attributed to the ring owner's dp and indexed by partner_dp->device_id, keeping it in the same debugfs file as reo_rx[] for direct comparison in MLO configurations. Increment the counter just before each MSDU is handed off to the stack via ath12k_dp_rx_deliver_msdu(). Add a likely()/WARN_ON_ONCE() bounds check on ring_id before indexing sent_to_stack[], consistent with all other new stat arrays in this series. Expose the per-ring per-device counts in the device_dp_stats debugfs file under a new 'REO sent to stack:' section. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hariharan Ramanathan Co-developed-by: Pardeep Kaur Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-7-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs.c | 10 ++++++++++ drivers/net/wireless/ath/ath12k/dp.h | 1 + drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 935da8d06450..5d5597d80417 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1122,6 +1122,16 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, len += scnprintf(buf + len, size - len, "%s: %u\n", wbm_rx_drop[i], device_stats->wbm_err.drop[i]); + len += scnprintf(buf + len, size - len, "\nREO sent to stack:\n"); + for (i = 0; i < DP_REO_DST_RING_MAX; i++) { + len += scnprintf(buf + len, size - len, "ring%d:", i); + for (j = 0; j < ab->ag->num_devices; j++) + len += scnprintf(buf + len, size - len, + "\t%d:%u", j, + device_stats->sent_to_stack[i][j]); + len += scnprintf(buf + len, size - len, "\n"); + } + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); for (i = 0; i < DP_REO_DST_RING_MAX; i++) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 3765bcd09c51..d10157cda4fd 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -438,6 +438,7 @@ struct ath12k_device_dp_stats { u32 tx_enqueued[DP_TCL_NUM_RING_MAX]; u32 tx_completed[DP_TCL_NUM_RING_MAX]; u32 reo_excep_msdu_buf_type; + u32 sent_to_stack[DP_REO_DST_RING_MAX][ATH12K_MAX_DEVICES]; }; struct ath12k_dp { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 721eb25be1a9..b9b8f15dc4e4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -651,6 +651,10 @@ ath12k_wifi7_dp_rx_process_received_packets(struct ath12k_dp *dp, dev_kfree_skb_any(msdu); continue; } + if (likely(ring_id < DP_REO_DST_RING_MAX)) + dp->device_stats.sent_to_stack[ring_id][partner_dp->device_id]++; + else + WARN_ON_ONCE(1); ath12k_dp_rx_deliver_msdu(dp_pdev, napi, msdu, &rx_info); } From 640240adbde5ecbbbc5afab5d72d89d7dd68347f Mon Sep 17 00:00:00 2001 From: Pardeep Kaur Date: Wed, 19 Aug 2026 11:02:46 +0530 Subject: [PATCH 25/27] wifi: ath12k: fix 1-based ring index in REO Rx Received debugfs output The REO Rx Received section in device_dp_stats debugfs uses a 1-based ring index ("Ring1:", "Ring2:", ...) which is inconsistent with the rest of ath12k where rings are indexed from 0. Fix it to use 0-based indexing and lowercase "ring%d:" to match the style used by all other ring counters in the same function. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-8-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 5d5597d80417..5d6710f1160c 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1215,7 +1215,7 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, len += scnprintf(buf + len, size - len, "\nREO Rx Received:\n"); for (i = 0; i < DP_REO_DST_RING_MAX; i++) { - len += scnprintf(buf + len, size - len, "Ring%d:", i + 1); + len += scnprintf(buf + len, size - len, "ring%d:", i); for (j = 0; j < ab->ag->num_devices; j++) { len += scnprintf(buf + len, size - len, From 8637c54300cf71fb7e0f8f402b4393e0b73d0a11 Mon Sep 17 00:00:00 2001 From: Aniruddha Mishra Date: Wed, 19 Aug 2026 11:02:47 +0530 Subject: [PATCH 26/27] wifi: ath12k: add WBM SW desc fallback counter When HW CC is not done, the WBM RX error path falls back to cookie-based descriptor retrieval via ath12k_dp_get_rx_desc(). This fallback path is taken before knowing whether the retrieval will succeed, so it is not a drop event and should not be counted in the drop[] array. Add a dedicated sw_desc_fallback counter in ath12k_device_dp_rx_wbm_err_stats to track how often the HW CC fallback path is taken, distinct from actual packet drops. Expose the counter in the device_dp_stats debugfs file as a separate line. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6.r1-00402-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aniruddha Mishra Co-developed-by: Pardeep Kaur Signed-off-by: Pardeep Kaur Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260819053247.1420393-9-pardeep.kaur@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs.c | 4 ++++ drivers/net/wireless/ath/ath12k/dp.h | 1 + drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 1 + 3 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 5d6710f1160c..12791e267d1a 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1132,6 +1132,10 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, len += scnprintf(buf + len, size - len, "\n"); } + len += scnprintf(buf + len, size - len, + "\nWBM SW desc fallback (HW CC not done): %u\n", + device_stats->wbm_err.sw_desc_fallback); + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); for (i = 0; i < DP_REO_DST_RING_MAX; i++) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index d10157cda4fd..c53eac20b989 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -422,6 +422,7 @@ struct ath12k_device_dp_rx_wbm_err_stats { u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; u32 drop[WBM_ERR_DROP_MAX]; + u32 sw_desc_fallback; }; struct ath12k_device_dp_stats { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index b9b8f15dc4e4..a32c804403e8 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1968,6 +1968,7 @@ int ath12k_wifi7_dp_rx_process_wbm_err(struct ath12k_dp *dp, /* retry manual desc retrieval if hw cc is not done */ if (!desc_info) { + dp->device_stats.wbm_err.sw_desc_fallback++; desc_info = ath12k_dp_get_rx_desc(dp, err_info.cookie); if (!desc_info) { dp->device_stats.wbm_err.drop[WBM_ERR_DROP_GET_SW_DESC]++; From 753baa59527f8e606c49eac25692b8b08918a3a9 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 6 Aug 2026 16:38:35 +0200 Subject: [PATCH 27/27] dt-bindings: net: wireless: qcom,ath10k: Document NVMEM cells Document the NVMEM cells supported by the ath10k driver, the mac-address, pre-calibration data, and calibration data. Since such data may also originate from chipset OTP or be supplied via other device tree structures. All of these cells are optional and can be provided independently, in any combination. Reviewed-by: Bartosz Golaszewski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260806-block-as-nvmem-v10-2-be598b2a5606@oss.qualcomm.com Signed-off-by: Jeff Johnson --- .../bindings/net/wireless/qcom,ath10k.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml index a52cb8a0d409..0cb065265df4 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml @@ -92,6 +92,22 @@ properties: ieee80211-freq-limit: true + nvmem-cells: + minItems: 1 + maxItems: 3 + description: + References to nvmem cells for MAC address and/or calibration data. + Supported cell names are mac-address, calibration, and pre-calibration. + + nvmem-cell-names: + minItems: 1 + maxItems: 3 + items: + enum: + - mac-address + - calibration + - pre-calibration + qcom,calibration-data: $ref: /schemas/types.yaml#/definitions/uint8-array description: