From e9ef9a79747614b0c90ccedc331d41780695e146 Mon Sep 17 00:00:00 2001 From: Adrian Nicolau Date: Wed, 1 Jul 2026 10:49:27 +0300 Subject: [PATCH 1/9] Revert "VER-493: wifi: ath12k: Handle scattered MSDU which is spread across the NAPI (#81)" This reverts commit 2a0c45e4e60aa59c13bcb73c0a0731941cac5be4. --- drivers/net/wireless/ath/ath12k/dp.h | 7 - drivers/net/wireless/ath/ath12k/dp_rx.h | 11 -- drivers/net/wireless/ath/ath12k/hal.c | 36 ---- drivers/net/wireless/ath/ath12k/hal.h | 3 - drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 161 ++++++------------ 5 files changed, 52 insertions(+), 166 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 8bc75ff607b0a..f8cfc7bb29dd7 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -338,13 +338,6 @@ struct ath12k_rx_desc_info { u8 in_use : 1, device_id : 3, reserved : 4; - struct { - __le32 info0; - __le32 peer_meta_data; - } rx_mpdu_info; - struct { - __le32 info0; - } rx_msdu_info; }; struct ath12k_tx_desc_info { diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 4541d5d4c5ae7..fe13e509033a1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -84,17 +84,6 @@ struct ath12k_dp_rx_rfc1042_hdr { __be16 snap_type; } __packed; -struct ath12k_dp_rx_proc_ctx { - int device_id; - struct ath12k_dp *dp; - struct ath12k_dp *partner_dp; - int ring_id; - int hw_link_id; - struct sk_buff_head *msdu_list; - int *num_buffs_reaped; - int *total_msdu_reaped; -}; - static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi) { u32 ret = 0; diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index e531f7d118554..ff0fd07f304d7 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -326,34 +326,6 @@ void *ath12k_hal_srng_dst_peek(struct ath12k_base *ab, struct hal_srng *srng) } EXPORT_SYMBOL(ath12k_hal_srng_dst_peek); -u32 ath12k_hal_srng_dst_get_curr_tp(struct hal_srng *srng) -{ - lockdep_assert_held(&srng->lock); - - return srng->u.dst_ring.tp; -} -EXPORT_SYMBOL(ath12k_hal_srng_dst_get_curr_tp); - -void *ath12k_hal_srng_dst_ring_get_and_update_tp(struct hal_srng *srng, u32 *updated_tp) -{ - void *desc; - - lockdep_assert_held(&srng->lock); - - if (srng->u.dst_ring.tp == srng->u.dst_ring.cached_hp) - return NULL; - - desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; - - srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % - srng->ring_size; - if (updated_tp) - *updated_tp = srng->u.dst_ring.tp; - - return desc; -} -EXPORT_SYMBOL(ath12k_hal_srng_dst_ring_get_and_update_tp); - void *ath12k_hal_srng_dst_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng) { @@ -521,14 +493,6 @@ void *ath12k_hal_srng_src_get_next_reaped(struct ath12k_base *ab, return desc; } -void ath12k_hal_srng_update_tp(struct hal_srng *srng, u32 new_tp) -{ - lockdep_assert_held(&srng->lock); - - srng->u.dst_ring.tp = new_tp; -} -EXPORT_SYMBOL(ath12k_hal_srng_update_tp); - void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng) { u32 hp; diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index 305d0410ac6fa..21c551d8b2481 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -1483,8 +1483,6 @@ int ath12k_hal_srng_get_entrysize(struct ath12k_base *ab, u32 ring_type); int ath12k_hal_srng_get_max_entries(struct ath12k_base *ab, u32 ring_type); void ath12k_hal_srng_get_params(struct ath12k_base *ab, struct hal_srng *srng, struct hal_srng_params *params); -u32 ath12k_hal_srng_dst_get_curr_tp(struct hal_srng *srng); -void *ath12k_hal_srng_dst_ring_get_and_update_tp(struct hal_srng *srng, u32 *updated_tp); void *ath12k_hal_srng_dst_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng); void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng); @@ -1501,7 +1499,6 @@ void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng); int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng, bool sync_hw_ptr); -void ath12k_hal_srng_update_tp(struct hal_srng *srng, u32 new_tp); void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng); void ath12k_hal_srng_access_end(struct ath12k_base *ab, struct hal_srng *srng); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index c4211169cb7bd..c1ec6c567c85c 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -651,31 +651,6 @@ ath12k_wifi7_dp_rx_process_received_packets(struct ath12k_dp *dp, rcu_read_unlock(); } -static void ath12k_dp_rx_process_desc_info(struct ath12k_rx_desc_info *desc_info, - struct ath12k_dp_rx_proc_ctx *ctx) -{ - struct sk_buff *msdu = desc_info->skb; - struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - - dma_unmap_single(ctx->partner_dp->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - ctx->num_buffs_reaped[ctx->device_id]++; - ctx->dp->device_stats.reo_rx[ctx->ring_id][ctx->dp->device_id]++; - rxcb->is_first_msdu = !!(le32_to_cpu(desc_info->rx_msdu_info.info0) & - RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - rxcb->is_last_msdu = !!(le32_to_cpu(desc_info->rx_msdu_info.info0) & - RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - rxcb->is_continuation = !!(le32_to_cpu(desc_info->rx_msdu_info.info0) & - RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); - rxcb->hw_link_id = ctx->hw_link_id; - rxcb->peer_id = ath12k_wifi7_dp_rx_get_peer_id(ctx->dp, - ctx->dp->peer_metadata_ver, - desc_info->rx_mpdu_info.peer_meta_data); - rxcb->tid = le32_get_bits(desc_info->rx_mpdu_info.info0, RX_MPDU_DESC_INFO0_TID); - __skb_queue_tail(ctx->msdu_list, msdu); - (*ctx->total_msdu_reaped)++; -} - int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, struct napi_struct *napi, int budget) { @@ -683,10 +658,10 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, struct ath12k_base *ab = dp->ab; struct ath12k_hal *hal = dp->hal; struct ath12k_dp_hw_group *dp_hw_grp = &ag->dp_hw_grp; - struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES], rx_desc_scat_list; + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; struct ath12k_hw_link *hw_links = ag->hw_links; int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; - struct ath12k_rx_desc_info *desc_info, *tmp, *scat_desc_info; + struct ath12k_rx_desc_info *desc_info; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; struct hal_reo_dest_ring *desc; struct ath12k_dp *partner_dp; @@ -696,27 +671,24 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, u8 hw_link_id, device_id; struct hal_srng *srng; struct sk_buff *msdu; - struct ath12k_dp_rx_proc_ctx ctx; + bool done = false; u64 desc_va; - u32 last_tp, updated_tp; - bool scat_buf = false, is_continuation, scat_buf_err = false; __skb_queue_head_init(&msdu_list); for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) INIT_LIST_HEAD(&rx_desc_used_list[device_id]); - INIT_LIST_HEAD(&rx_desc_scat_list); - srng = &hal->srng_list[dp->reo_dst_ring[ring_id].ring_id]; spin_lock_bh(&srng->lock); +try_again: ath12k_hal_srng_access_begin(ab, srng); - updated_tp = ath12k_hal_srng_dst_get_curr_tp(srng); - - while ((desc = ath12k_hal_srng_dst_ring_get_and_update_tp(srng, &last_tp))) { + while ((desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { + struct rx_mpdu_desc *mpdu_info; + struct rx_msdu_desc *msdu_info; enum hal_reo_dest_ring_push_reason push_reason; u32 cookie; @@ -735,9 +707,6 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, if (unlikely(!partner_dp)) { if (desc_info->skb) { dev_kfree_skb_any(desc_info->skb); - if (scat_buf) - scat_buf_err = true; - desc_info->skb = NULL; } @@ -757,92 +726,66 @@ int ath12k_wifi7_dp_rx_process(struct ath12k_dp *dp, int ring_id, if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) ath12k_warn(ab, "Check HW CC implementation"); + msdu = desc_info->skb; + desc_info->skb = NULL; + + list_add_tail(&desc_info->list, &rx_desc_used_list[device_id]); + + rxcb = ATH12K_SKB_RXCB(msdu); + dma_unmap_single(partner_dp->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + num_buffs_reaped[device_id]++; + dp->device_stats.reo_rx[ring_id][dp->device_id]++; + push_reason = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_PUSH_REASON); if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { - msdu = desc_info->skb; - rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(partner_dp->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - dev_kfree_skb_any(desc_info->skb); - if (scat_buf) - scat_buf_err = true; + dev_kfree_skb_any(msdu); dp->device_stats.hal_reo_error[ring_id]++; - list_add_tail(&desc_info->list, &rx_desc_used_list[device_id]); continue; } - desc_info->rx_mpdu_info.info0 = desc->rx_mpdu_info.info0; - desc_info->rx_mpdu_info.peer_meta_data = - desc->rx_mpdu_info.peer_meta_data; - desc_info->rx_msdu_info.info0 = desc->rx_msdu_info.info0; - is_continuation = !!(le32_to_cpu(desc->rx_msdu_info.info0) & - RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); - if (!is_continuation) { - if (unlikely(scat_buf && scat_buf_err)) { - list_for_each_entry_safe(scat_desc_info, tmp, - &rx_desc_scat_list, list) { - msdu = scat_desc_info->skb; - rxcb = ATH12K_SKB_RXCB(msdu); - list_del(&scat_desc_info->list); - list_add_tail(&scat_desc_info->list, - &rx_desc_used_list[device_id]); - dma_unmap_single(partner_dp->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - dev_kfree_skb_any(msdu); - scat_desc_info->skb = NULL; - } - - msdu = desc_info->skb; - rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(partner_dp->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - dev_kfree_skb_any(msdu); - scat_buf_err = false; - } else { - ctx.dp = dp; - ctx.partner_dp = partner_dp; - ctx.ring_id = ring_id; - ctx.device_id = device_id; - ctx.hw_link_id = hw_link_id; - ctx.msdu_list = &msdu_list; - ctx.num_buffs_reaped = num_buffs_reaped; - ctx.total_msdu_reaped = &total_msdu_reaped; - - if (scat_buf) { - list_for_each_entry_safe(scat_desc_info, tmp, - &rx_desc_scat_list, - list) { - list_del(&scat_desc_info->list); - list_add_tail(&scat_desc_info->list, - &rx_desc_used_list[device_id]); - ath12k_dp_rx_process_desc_info( - scat_desc_info, &ctx); - scat_desc_info->skb = NULL; - } - } - - ath12k_dp_rx_process_desc_info(desc_info, &ctx); - desc_info->skb = NULL; - scat_buf = false; - updated_tp = last_tp; - list_add_tail(&desc_info->list, - &rx_desc_used_list[device_id]); - } + msdu_info = &desc->rx_msdu_info; + mpdu_info = &desc->rx_mpdu_info; + + rxcb->is_first_msdu = !!(le32_to_cpu(msdu_info->info0) & + RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + rxcb->is_last_msdu = !!(le32_to_cpu(msdu_info->info0) & + RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + rxcb->is_continuation = !!(le32_to_cpu(msdu_info->info0) & + RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); + rxcb->hw_link_id = hw_link_id; + rxcb->peer_id = ath12k_wifi7_dp_rx_get_peer_id(dp, dp->peer_metadata_ver, + mpdu_info->peer_meta_data); + rxcb->tid = le32_get_bits(mpdu_info->info0, + RX_MPDU_DESC_INFO0_TID); + + __skb_queue_tail(&msdu_list, msdu); + + if (!rxcb->is_continuation) { + total_msdu_reaped++; + done = true; } else { - list_add_tail(&desc_info->list, &rx_desc_scat_list); - scat_buf = true; + done = false; } if (total_msdu_reaped >= budget) break; } - ath12k_hal_srng_update_tp(srng, updated_tp); + /* Hw might have updated the head pointer after we cached it. + * In this case, even though there are entries in the ring we'll + * get rx_desc NULL. Give the read another try with updated cached + * head pointer so that we can reap complete MPDU in the current + * rx processing. + */ + if (!done && ath12k_hal_srng_dst_num_free(ab, srng, true)) { + ath12k_hal_srng_access_end(ab, srng); + goto try_again; + } ath12k_hal_srng_access_end(ab, srng); From 0f23aa8873ea3595f659e8d1dc91a45754eeaef7 Mon Sep 17 00:00:00 2001 From: Nicolas Escande Date: Tue, 7 Apr 2026 11:54:26 +0200 Subject: [PATCH 2/9] wifi: ath12k: avoid dynamic alloc when parsing wmi tb On each WMI message received from the hardware, we alloc a temporary array of WMI_TAG_MAX entries of type void *. This array is then populated with pointers of parsed structs depending on the WMI type, and then freed. This alloc can fail when memory pressure in the system is high enough. Given the fact that it is scheduled in softirq with the system_bh_wq, we should not be able to parse more than one WMI message per CPU at any time. So instead lets move to a per cpu allocated array, that is reused across calls. This memory is allocated as needed and refcounted to exist only as long as one struct ath12k_base lives. ath12k_wmi_tlv_parse_alloc() and ath12k_wmi_tlv_parse() are merged together as it no longer allocs mem but returns the existing per-cpu one. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00218-QCAHKSWPL_SILICONZ-1 Signed-off-by: Nicolas Escande Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260407095426.3285574-1-nico.escande@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 6 + drivers/net/wireless/ath/ath12k/wmi.c | 217 +++++++++---------------- drivers/net/wireless/ath/ath12k/wmi.h | 3 + 3 files changed, 87 insertions(+), 139 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 63a170375e54a..cd73129503d66 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -2296,6 +2296,7 @@ void ath12k_core_deinit(struct ath12k_base *ab) void ath12k_core_free(struct ath12k_base *ab) { timer_delete_sync(&ab->rx_replenish_retry); + ath12k_wmi_free(); destroy_workqueue(ab->workqueue_aux); destroy_workqueue(ab->workqueue); kfree(ab); @@ -2320,6 +2321,9 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, if (!ab->workqueue_aux) goto err_free_wq; + if (ath12k_wmi_alloc() < 0) + goto err_free_wq_aux; + mutex_init(&ab->core_lock); spin_lock_init(&ab->base_lock); init_completion(&ab->reset_complete); @@ -2354,6 +2358,8 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, return ab; +err_free_wq_aux: + destroy_workqueue(ab->workqueue_aux); err_free_wq: destroy_workqueue(ab->workqueue); err_sc_free: diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 58702903c797c..a1063f1c198a0 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "core.h" #include "debugfs.h" #include "debug.h" @@ -134,6 +136,10 @@ struct wmi_pdev_set_obss_bitmap_arg { const char *label; }; +static DEFINE_MUTEX(ath12k_wmi_mutex); +static refcount_t ath12k_wmi_refcount; +static void __percpu *ath12k_wmi_tb; + static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 }, [WMI_TAG_ARRAY_UINT32] = { .min_len = 0 }, @@ -289,29 +295,19 @@ static int ath12k_wmi_tlv_iter_parse(struct ath12k_base *ab, u16 tag, u16 len, return 0; } -static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb, - const void *ptr, size_t len) -{ - return ath12k_wmi_tlv_iter(ar, ptr, len, ath12k_wmi_tlv_iter_parse, - (void *)tb); -} - static const void ** -ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab, - struct sk_buff *skb, gfp_t gfp) +ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb) { const void **tb; int ret; - tb = kcalloc(WMI_TAG_MAX, sizeof(*tb), gfp); - if (!tb) - return ERR_PTR(-ENOMEM); + tb = this_cpu_ptr(ath12k_wmi_tb); + memset(tb, 0, WMI_TAG_MAX * sizeof(*tb)); - ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len); - if (ret) { - kfree(tb); + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tlv_iter_parse, (void *)tb); + if (ret) return ERR_PTR(ret); - } return tb; } @@ -3914,9 +3910,10 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_obss_color_collision_event *ev; struct ath12k_link_vif *arvif; u32 vdev_id, evt_type; + const void **tb; u64 bitmap; - const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n", PTR_ERR(tb)); @@ -5725,7 +5722,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf const struct wmi_vdev_start_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5735,13 +5732,11 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev start resp ev"); - kfree(tb); return -EPROTO; } *vdev_rsp = *ev; - kfree(tb); return 0; } @@ -5820,7 +5815,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n"); - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -5830,7 +5825,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch reg chan list ext update ev\n"); - kfree(tb); return -EPROTO; } @@ -5860,7 +5854,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, if (num_2g_reg_rules > MAX_REG_RULES || num_5g_reg_rules > MAX_REG_RULES) { ath12k_warn(ab, "Num reg rules for 2G/5G exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n", num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES); - kfree(tb); return -EINVAL; } @@ -5870,7 +5863,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n", i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES); - kfree(tb); return -EINVAL; } @@ -5895,14 +5887,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n", i); - kfree(tb); return -EINVAL; } } if (!total_reg_rules) { ath12k_warn(ab, "No reg rules available\n"); - kfree(tb); return -EINVAL; } @@ -6004,7 +5994,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_2g_ptr) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n"); return -ENOMEM; } @@ -6038,7 +6027,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_5g_ptr) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n"); return -ENOMEM; } @@ -6057,7 +6045,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_6g_ap_ptr[i]) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 6g ap rules\n"); return -ENOMEM; } @@ -6072,7 +6059,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ext_wmi_reg_rule); if (!reg_info->reg_rules_6g_client_ptr[j][i]) { - kfree(tb); ath12k_warn(ab, "Unable to Allocate memory for 6g client rules\n"); return -ENOMEM; } @@ -6107,7 +6093,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n"); - kfree(tb); return 0; } @@ -6118,7 +6103,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * const struct wmi_peer_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6128,7 +6113,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer delete resp ev"); - kfree(tb); return -EPROTO; } @@ -6138,7 +6122,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff * ether_addr_copy(peer_del_resp->peer_macaddr.addr, ev->peer_macaddr.addr); - kfree(tb); return 0; } @@ -6150,7 +6133,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, const struct wmi_vdev_delete_resp_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6160,13 +6143,11 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev delete resp ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); - kfree(tb); return 0; } @@ -6178,7 +6159,7 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, const struct wmi_bcn_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6188,14 +6169,12 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab, ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch bcn tx status ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); *tx_status = le32_to_cpu(ev->tx_status); - kfree(tb); return 0; } @@ -6206,7 +6185,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ const struct wmi_vdev_stopped_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6216,13 +6195,11 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_ ev = tb[WMI_TAG_VDEV_STOPPED_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev stop ev"); - kfree(tb); return -EPROTO; } *vdev_id = le32_to_cpu(ev->vdev_id); - kfree(tb); return 0; } @@ -6361,7 +6338,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, const struct wmi_mgmt_tx_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6371,7 +6348,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch mgmt tx compl ev"); - kfree(tb); return -EPROTO; } @@ -6381,7 +6357,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab, param->ppdu_id = ev->ppdu_id; param->ack_rssi = ev->ack_rssi; - kfree(tb); return 0; } @@ -6544,7 +6519,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_scan_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6554,7 +6529,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_SCAN_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch scan ev"); - kfree(tb); return -EPROTO; } @@ -6566,7 +6540,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb, scan_evt_param->vdev_id = ev->vdev_id; scan_evt_param->tsf_timestamp = ev->tsf_timestamp; - kfree(tb); return 0; } @@ -6577,7 +6550,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf const struct wmi_peer_sta_kickout_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6587,7 +6560,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer sta kickout ev"); - kfree(tb); return -EPROTO; } @@ -6595,7 +6567,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf arg->reason = le32_to_cpu(ev->reason); arg->rssi = le32_to_cpu(ev->rssi); - kfree(tb); return 0; } @@ -6606,7 +6577,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_roam_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6616,7 +6587,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_ROAM_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch roam ev"); - kfree(tb); return -EPROTO; } @@ -6624,7 +6594,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb, roam_ev->reason = ev->reason; roam_ev->rssi = ev->rssi; - kfree(tb); return 0; } @@ -6658,7 +6627,7 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6668,7 +6637,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_CHAN_INFO_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch chan info ev"); - kfree(tb); return -EPROTO; } @@ -6685,7 +6653,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz; ch_info_ev->vdev_id = ev->vdev_id; - kfree(tb); return 0; } @@ -6697,7 +6664,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, const struct wmi_pdev_bss_chan_info_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6707,7 +6674,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev bss chan info ev"); - kfree(tb); return -EPROTO; } @@ -6725,7 +6691,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb, bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low; bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high; - kfree(tb); return 0; } @@ -6737,7 +6702,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk const struct wmi_vdev_install_key_compl_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6747,7 +6712,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch vdev install key compl ev"); - kfree(tb); return -EPROTO; } @@ -6757,7 +6721,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk arg->key_flags = le32_to_cpu(ev->key_flags); arg->status = le32_to_cpu(ev->status); - kfree(tb); return 0; } @@ -6768,7 +6731,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff const struct wmi_peer_assoc_conf_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6778,14 +6741,12 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch peer assoc conf ev"); - kfree(tb); return -EPROTO; } peer_assoc_conf->vdev_id = le32_to_cpu(ev->vdev_id); peer_assoc_conf->macaddr = ev->peer_macaddr.addr; - kfree(tb); return 0; } @@ -6803,7 +6764,7 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s const void **tb; int ret, i; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -6812,7 +6773,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; if (!ev) { - kfree(tb); ath12k_warn(ab, "failed to fetch 11d new cc ev"); return -EPROTO; } @@ -6825,8 +6785,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s ab->new_alpha2[0], ab->new_alpha2[1]); - kfree(tb); - for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; @@ -8606,7 +8564,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, const struct wmi_pdev_ctl_failsafe_chk_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8616,7 +8574,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev ctl failsafe check ev"); - kfree(tb); return; } @@ -8630,8 +8587,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab, if (ev->ctl_failsafe_status != 0) ath12k_warn(ab, "pdev ctl failsafe failure status %d", ev->ctl_failsafe_status); - - kfree(tb); } static void @@ -8703,7 +8658,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, const u32 *vdev_ids; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8715,7 +8670,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, if (!ev || !vdev_ids) { ath12k_warn(ab, "failed to fetch pdev csa switch count ev"); - kfree(tb); return; } @@ -8725,8 +8679,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab, ev->num_vdevs); ath12k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids); - - kfree(tb); } static void @@ -8738,7 +8690,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff struct ath12k *ar; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8749,7 +8701,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff if (!ev) { ath12k_warn(ab, "failed to fetch pdev dfs radar detected ev"); - kfree(tb); return; } @@ -8788,8 +8739,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff exit: rcu_read_unlock(); - - kfree(tb); } static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, @@ -8800,7 +8749,7 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, int ret; u16 length; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); @@ -8811,14 +8760,11 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, ev = tb[WMI_TAG_ARRAY_BYTE]; if (!ev) { ath12k_warn(ab, "failed to fetch ftm msg\n"); - kfree(tb); return; } length = skb->len - TLV_HDR_SIZE; ath12k_tm_process_event(ab, cmd_id, ev, length); - kfree(tb); - tb = NULL; } static void @@ -8831,7 +8777,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, int temp; u32 pdev_id; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb)); return; @@ -8840,15 +8786,12 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch pdev temp ev\n"); - kfree(tb); return; } temp = a_sle32_to_cpu(ev->temp); pdev_id = le32_to_cpu(ev->pdev_id); - kfree(tb); - ath12k_dbg(ab, ATH12K_DBG_WMI, "pdev temperature ev temp %d pdev_id %u\n", temp, pdev_id); @@ -8875,7 +8818,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, const struct wmi_fils_discovery_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -8887,15 +8830,12 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab, ev = tb[WMI_TAG_HOST_SWFDA_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch FILS discovery event\n"); - kfree(tb); return; } ath12k_warn(ab, "FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n", ev->vdev_id, ev->fils_tt, ev->tbtt); - - kfree(tb); } static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, @@ -8905,7 +8845,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, const struct wmi_probe_resp_tx_status_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, @@ -8918,7 +8858,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, if (!ev) { ath12k_warn(ab, "failed to fetch probe response transmission status event"); - kfree(tb); return; } @@ -8926,8 +8865,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab, ath12k_warn(ab, "Probe response transmission failed for vdev_id %u, status %u\n", ev->vdev_id, ev->tx_status); - - kfree(tb); } static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, @@ -8939,7 +8876,7 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, struct ath12k *ar; int ret, vdev_id; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse P2P NoA TLV: %d\n", ret); @@ -8949,10 +8886,8 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, ev = tb[WMI_TAG_P2P_NOA_EVENT]; noa = tb[WMI_TAG_P2P_NOA_INFO]; - if (!ev || !noa) { - ret = -EPROTO; - goto out; - } + if (!ev || !noa) + return -EPROTO; vdev_id = __le32_to_cpu(ev->vdev_id); @@ -8975,8 +8910,6 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab, unlock: rcu_read_unlock(); -out: - kfree(tb); return ret; } @@ -8987,7 +8920,7 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -8995,10 +8928,8 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, } ev = tb[WMI_TAG_RFKILL_EVENT]; - if (!ev) { - kfree(tb); + if (!ev) return; - } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi tlv rfkill state change gpio %d type %d radio_state %d\n", @@ -9011,7 +8942,6 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab, spin_unlock_bh(&ab->base_lock); queue_work(ab->workqueue, &ab->rfkill_work); - kfree(tb); } static void @@ -9027,7 +8957,7 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab, const struct wmi_twt_enable_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse wmi twt enable status event tlv: %d\n", @@ -9038,15 +8968,12 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab, ev = tb[WMI_TAG_TWT_ENABLE_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch twt enable wmi event\n"); - goto exit; + return; } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt enable event pdev id %u status %u\n", le32_to_cpu(ev->pdev_id), le32_to_cpu(ev->status)); - -exit: - kfree(tb); } static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, @@ -9056,7 +8983,7 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, const struct wmi_twt_disable_event *ev; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse wmi twt disable status event tlv: %d\n", @@ -9067,15 +8994,12 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, ev = tb[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch twt disable wmi event\n"); - goto exit; + return; } ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt disable event pdev id %d status %u\n", le32_to_cpu(ev->pdev_id), le32_to_cpu(ev->status)); - -exit: - kfree(tb); } static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab, @@ -9148,7 +9072,7 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse tlv: %d\n", ret); @@ -9158,7 +9082,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch gtk offload status ev"); - kfree(tb); return; } @@ -9168,7 +9091,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, rcu_read_unlock(); ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n", le32_to_cpu(ev->vdev_id)); - kfree(tb); return; } @@ -9184,8 +9106,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab, (void *)&replay_ctr_be, GFP_ATOMIC); rcu_read_unlock(); - - kfree(tb); } static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, @@ -9197,7 +9117,7 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, const void **tb; int ret, i; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse mlo setup complete event tlv: %d\n", @@ -9208,7 +9128,6 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT]; if (!ev) { ath12k_warn(ab, "failed to fetch mlo setup complete event\n"); - kfree(tb); return; } @@ -9227,14 +9146,11 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab, if (!ar) { ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n", ev->pdev_id, ev->status); - goto out; + return; } ar->mlo_setup_status = le32_to_cpu(ev->status); complete(&ar->mlo_setup_done); - -out: - kfree(tb); } static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, @@ -9244,7 +9160,7 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, const void **tb; int ret; - tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + tb = ath12k_wmi_tlv_parse(ab, skb); if (IS_ERR(tb)) { ret = PTR_ERR(tb); ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret); @@ -9252,13 +9168,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, } ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE]; - if (!ev) { + if (!ev) ath12k_warn(ab, "failed to fetch teardown complete event\n"); - kfree(tb); - return; - } - - kfree(tb); } #ifdef CONFIG_ATH12K_DEBUGFS @@ -11362,3 +11273,31 @@ int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, dev_kfree_skb(skb); return ret; } + +int ath12k_wmi_alloc(void) +{ + guard(mutex)(&ath12k_wmi_mutex); + + if (!ath12k_wmi_tb) { + ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *), + __alignof__(void *)); + if (!ath12k_wmi_tb) + return -ENOMEM; + + refcount_set(&ath12k_wmi_refcount, 1); + } else { + refcount_inc(&ath12k_wmi_refcount); + } + + return 0; +} + +void ath12k_wmi_free(void) +{ + guard(mutex)(&ath12k_wmi_mutex); + + if (refcount_dec_and_test(&ath12k_wmi_refcount)) { + free_percpu(ath12k_wmi_tb); + ath12k_wmi_tb = NULL; + } +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 45cd3d5bff3eb..f27197f912bc7 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -6599,4 +6599,7 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, struct ath12k_reg_tpc_power_info *param); int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab, struct wmi_mlo_link_set_active_arg *param); +int ath12k_wmi_alloc(void); +void ath12k_wmi_free(void); + #endif From 5645ca9de610835d7da499d94d1d35aa0addf50a Mon Sep 17 00:00:00 2001 From: Kwan Lai Chee Hou Date: Thu, 7 May 2026 09:53:35 +0800 Subject: [PATCH 3/9] wifi: ath12k: fix incorrect HT/VHT/HE/EHT MCS reporting in monitor mode In monitor mode, the driver incorrectly assigns the legacy rate to the rate_idx field of the radiotap header for HT/VHT/HE/EHT frames, ignoring the actual MCS value parsed from the hardware. This causes packet analyzers (like Wireshark) to display incorrect MCS values (e.g., legacy base rates instead of the true MCS). Fix this by assigning ppdu_info->mcs as the default rate_mcs in ath12k_dp_mon_fill_rx_rate(), and remove rate_idx assignments in ath12k_dp_mon_update_radiotap() to preserve the previously calculated MCS values (including the HT NSS offset). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ Fixes: 5393dcb45209 ("wifi: ath12k: change the status update in the monitor Rx") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220864 Signed-off-by: Kwan Lai Chee Hou Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260507015336.14636-1-laicheehou9@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_mon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 737287a9aa462..44c5cff75f169 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -115,13 +115,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k_pdev_dp *dp_pdev, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev); @@ -471,13 +472,10 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_rx_update_radiotap_he(ppduinfo, ptr); - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->vht_flags) { rxs->encoding = RX_ENC_VHT; - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->ht_flags) { rxs->encoding = RX_ENC_HT; - rxs->rate_idx = ppduinfo->rate; } else { struct ath12k *ar; From 7be1ae178c12ce3820ed845487402908007a28b0 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:11:08 +0800 Subject: [PATCH 4/9] wifi: ath12k: fix memory leak in ath12k_wifi7_dp_rx_h_verify_tkip_mic() In ath12k_wifi7_dp_rx_h_verify_tkip_mic(), the call to ath12k_dp_rx_check_nwifi_hdr_len_valid() may return false when the NWIFI header length is invalid, causing the function to abort early with -EINVAL. When this happens, the error propagates to ath12k_wifi7_dp_rx_h_defrag(), which clears first_frag by setting it to NULL. As a result, the corresponding MSDU is no longer referenced by the defragmentation path and is never freed. This leads to a memory leak for the affected MSDU on this error path. Proper cleanup is required to ensure the MSDU is released when header validation fails during TKIP MIC verification. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 9a0dddfb30f1 ("wifi: ath12k: Fix invalid data access in ath12k_dp_rx_h_undecap_nwifi") Signed-off-by: Miaoqing Pan Reviewed-by: Tamizh Chelvam Raja Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512021108.2031651-1-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index c1ec6c567c85c..f9a8bdab8f1dd 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1049,8 +1049,10 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, skb_pull(msdu, hal_rx_desc_sz); if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, - rx_info))) + rx_info))) { + dev_kfree_skb_any(msdu); return -EINVAL; + } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, From a26abbd08f0dff0103274da6338b38f3dad95725 Mon Sep 17 00:00:00 2001 From: Hangtian Zhu Date: Tue, 12 May 2026 10:57:32 +0800 Subject: [PATCH 5/9] wifi: ath12k: allow peer_id 0 in dp peer lookup For some chipsets, firmware can report HTT_T2H_MSG_TYPE_PEER_MAP2 with peer_id 0 as a valid value for mapping ath12k_dp_link_peer to ath12k_dp_peer. ath12k_dp_peer_find_by_peerid() currently treats peer_id 0 as invalid. When firmware assigns peer_id 0, peer lookup fails. As a result, DHCP OFFER packets are dropped in __ieee80211_rx_handle_packet() because pubsta is NULL. ath12k_dp_rx_deliver_msdu() <- rx_info->peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL ieee80211_rx_napi <- pubsta NULL ieee80211_rx_list __ieee80211_rx_handle_packet <- pubsta NULL, skb undelivered The following error in the TX completion path is caused by the same issue: ath12k_wifi7_pci 0000:04:00.0: dp_tx: failed to find the peer with peer_id 0 The error message is triggered by: ath12k_wifi7_dp_tx_complete_msdu ath12k_dp_link_peer_find_by_peerid <- ts->peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL ath12k_dp_tx_htt_tx_complete_buf ath12k_dp_link_peer_find_by_peerid <- peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL Fix this by allowing peer_id 0 in ath12k_dp_peer_find_by_peerid() and rejecting only values >= ATH12K_DP_PEER_ID_INVALID. Also update peer_id 0 handling in monitor path: Always call ath12k_dp_link_peer_find_by_peerid() in ath12k_dp_rx_h_find_link_peer() to fetch the peer, including when peer_id is 0. Always store peer_id in ppdu_info->peer_id in ath12k_wifi7_dp_mon_rx_parse_status_tlv(), including peer_id 0. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hangtian Zhu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512025732.1297849-1-hangtian.zhu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.c | 2 +- drivers/net/wireless/ath/ath12k/dp_rx.c | 3 +-- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index 2e66872b55723..caa56942f0334 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -419,7 +419,7 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "ath12k dp peer find by peerid index called without rcu lock"); - if (!peer_id || peer_id >= ATH12K_DP_PEER_ID_INVALID) + if (peer_id >= ATH12K_DP_PEER_ID_INVALID) return NULL; index = ath12k_dp_peer_get_peerid_index(dp, peer_id); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 78d61f7a98ac4..422f90293cf12 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1205,8 +1205,7 @@ ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *ms lockdep_assert_held(&dp->dp_lock); - if (rxcb->peer_id) - peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) return peer; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 074df2ab6bbf6..795fc4553a64e 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -1779,8 +1779,7 @@ ath12k_wifi7_dp_mon_rx_parse_status_tlv(struct ath12k_pdev_dp *dp_pdev, info[1] = __le32_to_cpu(mpdu_start->info1); peer_id = u32_get_bits(info[1], HAL_RX_MPDU_START_INFO1_PEERID); - if (peer_id) - ppdu_info->peer_id = peer_id; + ppdu_info->peer_id = peer_id; ppdu_info->mpdu_len += u32_get_bits(info[1], HAL_RX_MPDU_START_INFO2_MPDU_LEN); From f6e4a5248f0f060a6a58d306f82b0a31d1feb0d2 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:04 -0700 Subject: [PATCH 6/9] wifi: ath12k: fix inconsistent arvif state in vdev_create error paths ath12k_mac_vdev_create() has three error path issues that leave arvif in an inconsistent state: 1. When ath12k_wmi_vdev_create() fails, the function returns directly without clearing arvif->ar, which was already set before the WMI call. Subsequent code checking arvif->ar to determine vdev readiness will see a non-NULL value despite no vdev existing in firmware. 2. When ath12k_wmi_send_peer_delete_cmd() fails in err_peer_del, the code jumped to err: skipping the DP peer cleanup and vdev rollback, leaving num_created_vdevs, vdev maps and arvif list membership live. 3. When ath12k_wait_for_peer_delete_done() fails, the code jumped to err_vdev_del: skipping the DP peer cleanup. Fix by changing the ath12k_wmi_vdev_create() failure to goto err instead of returning directly, routing both err_peer_del failure paths through err_dp_peer_del: for proper DP peer and vdev rollback, and consolidating the arvif state cleanup at err:. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 477cabfdb776 ("wifi: ath12k: modify link arvif creation and removal for MLO") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-2-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index cb965f001bed0..5cc0ac60d84ca 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10410,7 +10410,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ab, "failed to create WMI vdev %d: %d\n", arvif->vdev_id, ret); - return ret; + goto err; } ar->num_created_vdevs++; @@ -10577,13 +10577,13 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", arvif->vdev_id, arvif->bssid); - goto err; + goto err_dp_peer_del; } ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arvif->bssid); if (ret) - goto err_vdev_del; + goto err_dp_peer_del; ar->num_peers--; } @@ -10600,8 +10600,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; - arvif->is_created = false; - arvif->ar = NULL; ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); @@ -10610,6 +10608,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) spin_unlock_bh(&ar->data_lock); err: + arvif->is_created = false; arvif->ar = NULL; return ret; } From b1e67a84cc5f0de6b1d7896239c31e8246a2ffe9 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:05 -0700 Subject: [PATCH 7/9] wifi: ath12k: fix NULL deref in change_sta_links for unready link _ieee80211_set_active_links() calls _ieee80211_link_use_channel() for each newly-added link and WARN_ON_ONCE()s if it fails. The call uses assign_on_failure=true, which allows mac80211 to continue despite driver failures, but when a mac80211-level channel validation fails (e.g., combinations check, DFS, or no available radio), drv_assign_vif_chanctx() is never reached. Since ath12k_mac_vdev_create() is only called from that path, arvif->is_created remains false and arvif->ar remains NULL for the failed link. The subsequent drv_change_sta_links() call reaches ath12k_mac_op_change_sta_links(), which allocates an arsta and sets ahsta->links_map |= BIT(link_id) for the broken link before checking whether the link is ready. When the vdev was never created, only station_add() is skipped, but the link remains in links_map. Any subsequent operation iterating links_map and dereferencing arvif->ar without a NULL check will crash. Two observed examples are NULL deref in ath12k_mac_ml_station_remove() on disconnect and in ath12k_mac_op_set_key() when wpa_supplicant installs PTK keys. BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] Call trace: ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] ath12k_mac_op_sta_state+0xb60/0x1720 [ath12k] drv_sta_state+0x100/0xbd8 [mac80211] __sta_info_destroy_part2+0x148/0x178 [mac80211] ieee80211_set_disassoc+0x500/0x678 [mac80211] BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] Call trace: ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] drv_set_key+0x70/0x100 [mac80211] ieee80211_key_enable_hw_accel+0x78/0x260 [mac80211] ieee80211_add_key+0x16c/0x2ac [mac80211] nl80211_new_key+0x138/0x280 [cfg80211] Fix this by checking arvif->is_created before calling ath12k_mac_alloc_assign_link_sta(). This prevents the broken link from entering links_map, so all subsequent operations iterating the bitmap are protected. The reliability of arvif->is_created across all error paths is ensured by the preceding patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: a27fa6148dac ("wifi: ath12k: support change_sta_links() mac80211 op") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-3-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 5cc0ac60d84ca..39a86a1951a8d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8170,16 +8170,16 @@ int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, continue; arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arvif || !arvif->is_created) + continue; - if (!arvif || !arsta) { + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arsta) { ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); continue; } ar = arvif->ar; - if (!ar) - continue; ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) { From 700181302ffc8241a337969605317660b13b7d06 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 9 Jun 2026 10:10:47 +0800 Subject: [PATCH 8/9] wifi: ath12k: fix EAPOL TX failure caused by stale tcl_metadata bits On WCN7850, after the following sequence: 1. load ath12k and connect to a non-MLO AP 2. disconnect and connect to an MLO AP 3. disconnect and reconnect to the non-MLO AP the third connection always fails with a 4-Way handshake timeout. The supplicant transmits message 2 of 4 four times in response to AP retries of message 1, but the AP never sees any of them. ath12k_dp_vdev_tx_attach() composes dp_link_vif->tcl_metadata using |=, but dp_link_vif is embedded in struct ath12k_dp_vif and its slots are reused across vif/peer teardown and setup. Since tcl_metadata is never cleared on detach, vdev_id bits from a previous attach remain set when the same link slot is reused with a different vdev_id. In this specific issue, the same link slot is used for vdev_id 0, then vdev_id 1, then vdev_id 0 again, the OR yields tcl_metadata == 0x9, which encodes vdev_id 1 in the HTT_TCL_META_DATA_VDEV_ID field even though ti.vdev_id is 0. Firmware then routes the EAPOL frame to the wrong vdev and the AP never receives message 2. Use plain assignment instead of |= so the field is fully recomputed from the current arvif on every attach. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: af66c7640cf9 ("wifi: ath12k: Refactor ath12k_vif structure") Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609-ath12k-fix-eapol-tcl-metadata-v1-1-d47e6f90d4ee@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index ab54c8a84d3e0..53ddb0d188012 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -946,11 +946,11 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) dp_link_vif = ath12k_dp_vif_to_dp_link_vif(&ahvif->dp_vif, link_id); - dp_link_vif->tcl_metadata |= u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | - u32_encode_bits(arvif->vdev_id, - HTT_TCL_META_DATA_VDEV_ID) | - u32_encode_bits(ar->pdev->pdev_id, - HTT_TCL_META_DATA_PDEV_ID); + dp_link_vif->tcl_metadata = u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(arvif->vdev_id, + HTT_TCL_META_DATA_VDEV_ID) | + u32_encode_bits(ar->pdev->pdev_id, + HTT_TCL_META_DATA_PDEV_ID); /* set HTT extension valid bit to 0 by default */ dp_link_vif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; From f14e2abab0ad77d4fa13064d0dbd81375a4681e9 Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 4 Jun 2026 15:28:31 +0530 Subject: [PATCH 9/9] wifi: ath12k: enable IEEE80211_VHT_EXT_NSS_BW_CAPABLE when NSS ratio is reported When firmware reports NSS ratio support, SUPPORTS_VHT_EXT_NSS_BW is enabled in ath12k. However, IEEE80211_VHT_EXT_NSS_BW_CAPABLE must also be set to make the advertisement valid. According to IEEE Std 802.11-2024, Subclause 9.4.2.156.3 (Supported VHT-MCS and NSS Set subfields), the VHT Extended NSS BW Capable bit indicates whether a STA is capable of interpreting the Extended NSS BW Support subfield of the VHT capabilities information field. Advertising extended NSS BW support without setting this capability bit is therefore invalid. Without this change, mac80211 detects the inconsistency and logs: ieee80211 phy0: copying sband (band 1) due to VHT EXT NSS BW flag This indicates that mac80211 implicitly aligns IEEE80211_VHT_EXT_NSS_BW_CAPABLE during ieee80211_register_hw(). Explicitly setting the bit in ath12k avoids this fixup and ensures capabilities are advertised correctly by the driver. This change follows the same approach as the existing ath11k fix. https://lore.kernel.org/all/20211013073704.15888-1-wgong@codeaurora.org/ Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Fixes: 18ab9d038fad ("wifi: ath12k: add support for 160 MHz bandwidth") Signed-off-by: Wen Gong Signed-off-by: Maharaja Kennadyrajan Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260604095831.2674298-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 39a86a1951a8d..7ec7638eb0653 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8515,6 +8515,10 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask, vht_cap.vht_supported = 1; vht_cap.cap = ar->pdev->cap.vht_cap; + if (ar->pdev->cap.nss_ratio_enabled) + vht_cap.vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); + ath12k_set_vht_txbf_cap(ar, &vht_cap.cap); /* 80P80 is not supported */