From e56c9d429ba004d5416b03091fd7656187cdcaf1 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Mon, 28 Sep 2026 18:45:25 +0800 Subject: [PATCH 01/18] wifi: ath11k: fix unbalanced IRQ enable/disable during suspend/resume Commit f7a74e131d3f ("wifi: ath11k: disable interrupts during firmware crash recovery") added an unconditional ath11k_hif_irq_disable()/ ath11k_hif_ce_irq_disable() pair inside ath11k_core_reconfigure_on_crash(), guarded by "if (!ab->is_reset)", to stop the DP NAPI from touching rings that have just been torn down. That fixes AHB device recovery, but breaks PCI device suspend/resume. During suspend, IRQs are disabled once in ath11k_core_suspend_late(). Then during resume, ath11k_core_reconfigure_on_crash() sees ab->is_reset clear and disables them a second time. Those IRQs are enabled only once during the following reinitialization, leaving the IRQ lines masked. As a result, HTC control responses never arrive, and resume fails: failed to receive control response completion, polling.. failed to connect to HTT: -110 failed to reconfigure driver on crash recovery failed to resume core: -110 Fix this by disabling the IRQs unconditionally at the top of ath11k_core_reconfigure_on_crash(), and instead drop the disable calls from the two places that already disable IRQs before this function can run: ath11k_core_suspend_late() and ath11k_core_reset(). This keeps a single disable per recovery/suspend cycle regardless of which path (recovery or suspend) triggers it, so the refcount on PCI parts stays balanced while AHB parts keep getting the disable that the original commit needed. Fixes: f7a74e131d3f ("wifi: ath11k: disable interrupts during firmware crash recovery") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/linux-wireless/a068a857-a549-4cc2-ae0f-3ef119d3a859@samsung.com/ Tested-by: Marek Szyprowski Tested-by: Julius Bairaktaris Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260928-ath11k-unbalanced-irq-enable-disable-v1-1-75166f534763@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/core.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index d2ed6a0ea7e3..1ad0a47653de 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1275,9 +1275,6 @@ int ath11k_core_suspend_late(struct ath11k_base *ab) if (ab->actual_pm_policy == ATH11K_PM_WOW) return 0; - ath11k_hif_irq_disable(ab); - ath11k_hif_ce_irq_disable(ab); - ath11k_hif_power_down(ab, true); return 0; @@ -2333,17 +2330,9 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) int ret; mutex_lock(&ab->core_lock); + ath11k_hif_irq_disable(ab); + ath11k_hif_ce_irq_disable(ab); ath11k_thermal_unregister(ab); - - /* - * ath11k_core_reset() already disabled the interrupts on the reset - * path; only the firmware crash path reaches here with them live. - */ - if (!ab->is_reset) { - ath11k_hif_irq_disable(ab); - ath11k_hif_ce_irq_disable(ab); - } - ath11k_dp_pdev_free(ab); ath11k_cfr_deinit(ab); ath11k_spectral_deinit(ab); @@ -2605,9 +2594,6 @@ static void ath11k_core_reset(struct work_struct *work) time_left = wait_for_completion_timeout(&ab->recovery_start, ATH11K_RECOVER_START_TIMEOUT_HZ); - ath11k_hif_irq_disable(ab); - ath11k_hif_ce_irq_disable(ab); - ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); From 13b15eabe2c541b80406166964cdc8ccb5e9c32a Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 17:59:13 +0800 Subject: [PATCH 02/18] wifi: ath12k: Reserve space for a string terminator ath12k_write_htt_stats_type() accepts count == size, which fills the zero-initialized buffer without a terminating NUL. sscanf() then reads beyond the buffer. Reject input that leaves no room for the trailing NUL. Fixes: 8c7a5031a6b0 ("wifi: ath12k: Fix buffer overflow in debugfs") Reviewed-by: Dan Carpenter Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Signed-off-by: Jiale Yao Link: https://patch.msgid.link/20261003095913.575108-1-yaojiale02@163.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index b772181a496e..f84f1828275a 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -6190,7 +6190,7 @@ static ssize_t ath12k_write_htt_stats_type(struct file *file, const int size = 32; int num_args; - if (count > size) + if (count >= size) return -EINVAL; char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); From 81394dfb9a7905472957628b560ae894eae3a30f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Fri, 11 Sep 2026 14:31:44 -0700 Subject: [PATCH 03/18] wifi: ath9k: delete channel-context timers on deinit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ath9k_deinit_channel_context() cancels chanctx_work but does not delete the offchannel and sched timers set up by ath9k_init_channel_context(). If either timer fires after deinit (e.g. during driver unload or suspend), it accesses sc->sc_ah which may already be freed by ath9k_hw_deinit(), causing a use-after-free. Delete both timers with timer_shutdown_sync() before cancelling the work item. Assisted-by: LLM Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Cc: stable@vger.kernel.org # v6.2+ Fixes: 705d0bf83dbe ("ath9k: Add a routine for initializing channel contexts") Link: https://patch.msgid.link/20260911213144.130192-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/channel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 8b27d8cc086a..cfb1c90eb4b6 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1363,6 +1363,8 @@ void ath9k_init_channel_context(struct ath_softc *sc) void ath9k_deinit_channel_context(struct ath_softc *sc) { + timer_shutdown_sync(&sc->sched.timer); + timer_shutdown_sync(&sc->offchannel.timer); cancel_work_sync(&sc->chanctx_work); } From 59411703660919197234fa0ec80ee1a5bcbe4276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nerijus=20Bend=C5=BEi=C5=ABnas?= Date: Tue, 8 Sep 2026 21:03:08 +0300 Subject: [PATCH 04/18] wifi: ath9k: refuse spectral scan control while the hardware is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spectral_scan_ctl debugfs file drives PHY register writes, and nothing checks whether the hardware is up. While the interface is down the chip is asleep, and on the AR9271 those writes go over WMI to a sleeping target and hang the firmware until the device is re-enumerated. Refuse the write with -EBUSY while ATH_OP_INVALID is set. The reset control already refuses on the same flag. Assisted-by: LLM Signed-off-by: Nerijus Bendžiūnas Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260908180308.788642-1-nerijus.bendziunas@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/common-spectral.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index ca01a07f6630..beedabee7d47 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -805,6 +805,15 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, buf[len] = '\0'; + /* + * Every command below drives PHY register writes. Refuse them while + * the hardware is disabled (the interface is down): on the AR9271 + * that traffic hangs the firmware until the device is re-enumerated. + * The reset control refuses on the same flag. + */ + if (test_bit(ATH_OP_INVALID, &common->op_flags)) + return -EBUSY; + if (strncmp("trigger", buf, 7) == 0) { ath9k_cmn_spectral_scan_trigger(common, spec_priv); } else if (strncmp("background", buf, 10) == 0) { From 129324b60343dec19d45dcf87736f129b18b95bb Mon Sep 17 00:00:00 2001 From: Albert Esteve Date: Mon, 14 Sep 2026 15:05:19 +0200 Subject: [PATCH 05/18] wifi: ath10k: clear QMI if failed during init A failed ath10k_qmi_init() leaves a dangling pointer (ar_snoc->qmi) in the cleanup path. Clear the pointer on the way out for hygiene and consistency with deinit(). Signed-off-by: Albert Esteve Reviewed-by: Brian Norris Link: https://patch.msgid.link/20260914-fix-ath10k-dangling-v1-1-15c5089682b1@redhat.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/qmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 0d74548a5f34..08a64f737fb4 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -1128,6 +1128,7 @@ int ath10k_qmi_init(struct ath10k *ar, u32 msa_size) err: kfree(qmi); + ar_snoc->qmi = NULL; return ret; } From 4b2f8de21708c02203a31e2e83f8240dbdc702e8 Mon Sep 17 00:00:00 2001 From: Vitor Soares Date: Tue, 22 Sep 2026 21:06:42 +0100 Subject: [PATCH 06/18] wifi: ath12k: advertise interface MAC address pool ath12k supports creating vdevs with addresses provided by mac80211. Userspace can already make concurrent interfaces work by explicitly assigning different locally administered addresses. However, ath12k does not advertise an address list, so when a second interface is created without an explicit address, mac80211 has nothing to pick from and falls back to the permanent address. If the first interface is already running with that address, bringing up the second one fails with -ENOTUNIQ. As in ath11k, provide a list of usable addresses so ieee80211_assign_perm_addr() can hand each new interface an unused one. Keep the first entry as the wiphy's permanent MAC address. Generate the remaining entries as locally administered variants by changing only the upper nibble of the first octet, which naturally limits the pool to 16 unique addresses. The cfg80211 definition of wiphy->addresses does not specify where the addresses must come from. Some ath12k platforms use it as an interface MAC address pool, while others use it for firmware-provided per-radio addresses. Gate the generated address pool behind advertise_iface_mac_pool and enable it for WCN7850 and QCC2072. The flag is read from the first radio, as all radios behind a wiphy are expected to have the same setting. Other platforms keep the existing behavior. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Assisted-by: LLM Signed-off-by: Vitor Soares Tested-by: Kang Yang Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260922200643.1133491-2-ivitro@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/hw.h | 1 + drivers/net/wireless/ath/ath12k/mac.c | 54 ++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wifi7/hw.c | 6 +++ 3 files changed, 61 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 1e652dc0d247..52558dd126a0 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -239,6 +239,7 @@ struct ath12k_hw_params { } client; bool host_alloc_ml_id; + bool advertise_iface_mac_pool; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 196b807210de..db6856ec6c14 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -14746,6 +14746,56 @@ static void ath12k_mac_cleanup_unregister(struct ath12k *ar) kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); } +static void ath12k_mac_cleanup_mac_address_list(struct ath12k_hw *ah) +{ + kfree(ah->hw->wiphy->addresses); + ah->hw->wiphy->addresses = NULL; + ah->hw->wiphy->n_addresses = 0; +} + +static void ath12k_mac_setup_mac_address_list(struct ath12k_hw *ah, + const u8 *mac_addr) +{ + struct mac_address *addresses; + struct ath12k *ar; + u16 n_addresses; + int i; + + /* + * wiphy->addresses is per wiphy, so the generated interface MAC + * pool is advertised for the whole ath12k_hw. The hw_params flag is + * expected to be consistent across all radios in an ath12k_hw, so + * use the first radio as the source. + */ + ar = ath12k_ah_to_ar(ah, 0); + if (!ar->ab->hw_params->advertise_iface_mac_pool) + return; + + /* + * Only the upper nibble of the first octet is varied, so at most 16 + * addresses can be derived from one base. + */ + n_addresses = min_t(u16, TARGET_NUM_VDEVS(ar->ab), 16); + if (n_addresses <= 1) + return; + + addresses = kzalloc_objs(*addresses, n_addresses); + if (!addresses) + return; + + ether_addr_copy(addresses[0].addr, mac_addr); + for (i = 1; i < n_addresses; i++) { + ether_addr_copy(addresses[i].addr, mac_addr); + /* set Local Administered Address bit */ + addresses[i].addr[0] |= 0x2; + + addresses[i].addr[0] += i << 4; + } + + ah->hw->wiphy->addresses = addresses; + ah->hw->wiphy->n_addresses = n_addresses; +} + static void ath12k_mac_hw_unregister(struct ath12k_hw *ah) { struct ieee80211_hw *hw = ah->hw; @@ -14765,6 +14815,7 @@ static void ath12k_mac_hw_unregister(struct ath12k_hw *ah) ath12k_mac_cleanup_unregister(ar); ath12k_mac_cleanup_iface_combinations(ah); + ath12k_mac_cleanup_mac_address_list(ah); SET_IEEE80211_DEV(hw, NULL); } @@ -14881,6 +14932,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->available_antennas_tx = antennas_tx; SET_IEEE80211_PERM_ADDR(hw, mac_addr); + ath12k_mac_setup_mac_address_list(ah, mac_addr); SET_IEEE80211_DEV(hw, ab->dev); ret = ath12k_mac_setup_iface_combinations(ah); @@ -15123,6 +15175,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ath12k_mac_cleanup_unregister(ar); } + ath12k_mac_cleanup_mac_address_list(ah); + SET_IEEE80211_DEV(hw, NULL); return ret; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index aec7c652808c..b1c17cc197df 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -447,6 +447,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { }, .host_alloc_ml_id = true, + .advertise_iface_mac_pool = false, }, { .name = "wcn7850 hw2.0", @@ -542,6 +543,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { }, .host_alloc_ml_id = false, + .advertise_iface_mac_pool = true, }, { .name = "qcn9274 hw2.0", @@ -633,6 +635,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { }, .host_alloc_ml_id = true, + .advertise_iface_mac_pool = false, }, { .name = "ipq5332 hw1.0", @@ -718,6 +721,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { }, .host_alloc_ml_id = true, + .advertise_iface_mac_pool = false, }, { .name = "qcc2072 hw1.0", @@ -814,6 +818,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { }, .host_alloc_ml_id = false, + .advertise_iface_mac_pool = true, }, { .name = "ipq5424 hw1.0", @@ -902,6 +907,7 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { }, .host_alloc_ml_id = true, + .advertise_iface_mac_pool = false, }, }; From 86f2537877d6029487811bc3e0cd9e086a196000 Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 18:19:43 +0800 Subject: [PATCH 07/18] wifi: ath12k: validate MAC/PHY capability count before saving Firmware supplies the hardware mode count and the PHY bitmap for each mode in the service-ready-ext event. ath12k_wmi_save_all_mac_phy_info() uses those bitmaps to populate svc_ext_info->mac_phy_info, but the destination is a fixed array of ATH12K_MAX_MAC_PHY_CAP elements. If the total number of advertised PHY entries exceeds that limit, the loop writes beyond mac_phy_info and corrupts adjacent memory. ath12k_wmi_mac_phy_caps_parse() allocates the source array for tot_phy_id entries and rejects excess capability TLVs. However, if the event contains fewer capability TLVs than advertised, the remaining allocated entries stay zeroed and are saved as invalid PHY information. After parsing the MAC/PHY capability TLVs, validate the destination capacity and reject an incomplete capability list before saving any entries. Fixes: 062ade23991e ("wifi: ath12k: parse and save hardware mode info from WMI_SERVICE_READY_EXT_EVENTID event for later use") Cc: stable@vger.kernel.org Signed-off-by: Jiale Yao Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20261003101944.596546-1-yaojiale02@163.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wmi.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 3149b6376c94..75b259990c79 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -5050,6 +5050,7 @@ static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data) { + struct ath12k_svc_ext_info *svc_ext_info = &ab->wmi_ab.svc_ext_info; struct ath12k_wmi_pdev *wmi_handle = &ab->wmi_ab.wmi[0]; struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext = data; int ret; @@ -5094,6 +5095,22 @@ static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab, return ret; } + if (svc_rdy_ext->tot_phy_id > + ARRAY_SIZE(svc_ext_info->mac_phy_info)) { + ath12k_warn(ab, "too many PHY entries %u (max %zu)\n", + svc_rdy_ext->tot_phy_id, + ARRAY_SIZE(svc_ext_info->mac_phy_info)); + return -EINVAL; + } + + if (svc_rdy_ext->n_mac_phy_caps < svc_rdy_ext->tot_phy_id) { + ath12k_warn(ab, + "not enough MAC/PHY caps %u, expected %u\n", + svc_rdy_ext->n_mac_phy_caps, + svc_rdy_ext->tot_phy_id); + return -EINVAL; + } + ath12k_wmi_save_all_mac_phy_info(ab, svc_rdy_ext); svc_rdy_ext->mac_phy_done = true; From 97aab70a03df478ffefcf890ed4be258c0240bcb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 8 Oct 2026 16:56:45 +0200 Subject: [PATCH 08/18] wifi: ath6kl: don't take RTNL in del_virtual_intf cfg80211 already holds the RTNL here, so this will just deadlock. Remove the extra RTNL acquisition. Oddly enough this was all true even back when the locking here was added, so this is just a revert of a broken ~13 year old commit. Fixes: bc52aab380c7 ("ath6kl: Protect ath6kl_cfg80211_vif_cleanup using rtnl_locks") Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20261008165646.48866f0521af.I1c166be830384473f67677eeb187afa7e54106ea@changeid Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a9a35476fecd..65b7c68a9297 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1560,9 +1560,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag)); - rtnl_lock(); ath6kl_cfg80211_vif_cleanup(vif); - rtnl_unlock(); return 0; } From 643f0bf936176c114d5631da7547e991b5d99f10 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Wed, 7 Oct 2026 14:53:32 +0530 Subject: [PATCH 09/18] wifi: ath12k: Make RXDMA buffer ring size configurable per memory profile Currently, the RXDMA buffer ring size is hardcoded to 4096 and shared across all memory profiles. This prevents individual profiles from tuning the ring size based on their memory and performance requirements. Add the RXDMA buffer ring size to the memory profile configuration and implement ath12k_dp_rxdma_buf_ring_size() static inline function to access the configured value. This allows different memory profiles to select an appropriate ring size based on available system resources. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261007-mpp_ws-v1-1-739b1fb87d24@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 2 ++ drivers/net/wireless/ath/ath12k/dp.h | 8 +++++++- drivers/net/wireless/ath/ath12k/dp_rx.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 0c72948f3f56..917a124a4df0 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -54,6 +54,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { .num_pool_tx_desc = 32768, .rx_desc_count = 12288, .rx_release_ring_size = 16384, + .rxdma_buf_ring_size = 4096, }, }, [ATH12K_QMI_MEMORY_MODE_LOW_512_M] = { @@ -68,6 +69,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { .num_pool_tx_desc = 16384, .rx_desc_count = 6144, .rx_release_ring_size = 8192, + .rxdma_buf_ring_size = 4096, }, }, }; diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index c53eac20b989..108e27dd5797 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -31,6 +31,7 @@ struct ath12k_dp_profile_params { u32 num_pool_tx_desc; u32 rx_desc_count; u32 rx_release_ring_size; + u32 rxdma_buf_ring_size; }; #define DP_MON_PURGE_TIMEOUT_MS 100 @@ -210,7 +211,6 @@ struct ath12k_pdev_dp { #define DP_REO_EXCEPTION_RING_SIZE 128 #define DP_REO_CMD_RING_SIZE 256 #define DP_REO_STATUS_RING_SIZE 2048 -#define DP_RXDMA_BUF_RING_SIZE 4096 #define DP_RX_MAC_BUF_RING_SIZE 4096 #define DP_RXDMA_REFILL_RING_SIZE 2048 #define DP_RXDMA_ERR_DST_RING_SIZE 1024 @@ -730,6 +730,12 @@ ath12k_dp_rx_release_ring_size(const struct ath12k_dp_profile_params *p) return p->rx_release_ring_size; } +static inline u32 +ath12k_dp_rxdma_buf_ring_size(const struct ath12k_dp_profile_params *p) +{ + return p->rxdma_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 cfaf26ae2720..36be543b7c50 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1699,7 +1699,7 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab) ret = ath12k_dp_srng_setup(ab, &dp->rx_refill_buf_ring.refill_buf_ring, HAL_RXDMA_BUF, 0, 0, - DP_RXDMA_BUF_RING_SIZE); + ath12k_dp_rxdma_buf_ring_size(dp_params)); if (ret) { ath12k_warn(ab, "failed to setup rx_refill_buf_ring\n"); return ret; From 15f7603b69a83a0acc7fcc8992ecbea6110f9424 Mon Sep 17 00:00:00 2001 From: Pavankumar Nandeshwar Date: Wed, 7 Oct 2026 14:53:33 +0530 Subject: [PATCH 10/18] wifi: ath12k: Increase RXDMA buffer ring size for default memory profile The default RXDMA buffer ring size of 4096 entries can become a bottleneck under high RX traffic load, resulting in reduced receive throughput due to limited buffer availability. Increase the RXDMA buffer ring size to 8192 entries for the default memory profile to provide additional RX buffering and improve RX packet processing efficiency. This increases memory consumption by approximately 9-10 MiB and improves RX throughput by ~20% with the default memory profile (ATH12K_QMI_MEMORY_MODE_DEFAULT). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Signed-off-by: Pavankumar Nandeshwar Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261007-mpp_ws-v1-2-739b1fb87d24@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 917a124a4df0..3ed98eb746b5 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -54,7 +54,7 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { .num_pool_tx_desc = 32768, .rx_desc_count = 12288, .rx_release_ring_size = 16384, - .rxdma_buf_ring_size = 4096, + .rxdma_buf_ring_size = 8192, }, }, [ATH12K_QMI_MEMORY_MODE_LOW_512_M] = { From d26b64fbb844fa00d9c95b7e1ae7fa8f546952db Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 13 Apr 2026 16:58:46 -0700 Subject: [PATCH 11/18] wifi: ath12k: Remove unused HAL_RX_REO_QUEUE_INFO macros Many of the HAL_RX_REO_QUEUE_INFO macros are unused, so remove them. No functional change. Compile Tested only. Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260413-hal_rx_reo_queue_info2_msdu_count-v1-1-c4c92ccf77b9@oss.qualcomm.com Signed-off-by: Jeff Johnson --- .../net/wireless/ath/ath12k/wifi7/hal_desc.h | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h index e1ab47b44433..822de3a4abc8 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h @@ -2100,41 +2100,15 @@ enum hal_rx_reo_queue_pn_size { #define HAL_RX_REO_QUEUE_INFO0_VLD BIT(0) #define HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER GENMASK(2, 1) -#define HAL_RX_REO_QUEUE_INFO0_DIS_DUP_DETECTION BIT(3) -#define HAL_RX_REO_QUEUE_INFO0_SOFT_REORDER_EN BIT(4) #define HAL_RX_REO_QUEUE_INFO0_AC GENMASK(6, 5) -#define HAL_RX_REO_QUEUE_INFO0_BAR BIT(7) #define HAL_RX_REO_QUEUE_INFO0_RETRY BIT(8) -#define HAL_RX_REO_QUEUE_INFO0_CHECK_2K_MODE BIT(9) -#define HAL_RX_REO_QUEUE_INFO0_OOR_MODE BIT(10) #define HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE GENMASK(20, 11) #define HAL_RX_REO_QUEUE_INFO0_PN_CHECK BIT(21) -#define HAL_RX_REO_QUEUE_INFO0_EVEN_PN BIT(22) -#define HAL_RX_REO_QUEUE_INFO0_UNEVEN_PN BIT(23) -#define HAL_RX_REO_QUEUE_INFO0_PN_HANDLE_ENABLE BIT(24) #define HAL_RX_REO_QUEUE_INFO0_PN_SIZE GENMASK(26, 25) #define HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG BIT(27) #define HAL_RX_REO_QUEUE_INFO1_SVLD BIT(0) #define HAL_RX_REO_QUEUE_INFO1_SSN GENMASK(12, 1) -#define HAL_RX_REO_QUEUE_INFO1_CURRENT_IDX GENMASK(22, 13) -#define HAL_RX_REO_QUEUE_INFO1_SEQ_2K_ERR BIT(23) -#define HAL_RX_REO_QUEUE_INFO1_PN_ERR BIT(24) -#define HAL_RX_REO_QUEUE_INFO1_PN_VALID BIT(31) - -#define HAL_RX_REO_QUEUE_INFO2_MPDU_COUNT GENMASK(6, 0) -#define HAL_RX_REO_QUEUE_INFO2_MSDU_COUNT (31, 7) - -#define HAL_RX_REO_QUEUE_INFO3_TIMEOUT_COUNT GENMASK(9, 4) -#define HAL_RX_REO_QUEUE_INFO3_FWD_DUE_TO_BAR_CNT GENMASK(15, 10) -#define HAL_RX_REO_QUEUE_INFO3_DUPLICATE_COUNT GENMASK(31, 16) - -#define HAL_RX_REO_QUEUE_INFO4_FRAME_IN_ORD_COUNT GENMASK(23, 0) -#define HAL_RX_REO_QUEUE_INFO4_BAR_RECVD_COUNT GENMASK(31, 24) - -#define HAL_RX_REO_QUEUE_INFO5_LATE_RX_MPDU_COUNT GENMASK(11, 0) -#define HAL_RX_REO_QUEUE_INFO5_WINDOW_JUMP_2K GENMASK(15, 12) -#define HAL_RX_REO_QUEUE_INFO5_HOLE_COUNT GENMASK(31, 16) struct hal_rx_reo_queue { struct hal_desc_header desc_hdr; From 5949d1910f13c20f6ebcd26eec9e0c02cb4951aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Roux?= Date: Tue, 6 Oct 2026 08:27:28 +0200 Subject: [PATCH 12/18] wifi: ath12k: skip hardware rfkill setup on Microsoft Surface devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the Microsoft Surface Pro 11 and Surface Laptop 7 (X1E80100), the WCN7850 hardware rfkill input reads as "radio off" and the OS has no way to control it. ath12k enables hardware rfkill handling, so Wi-Fi stays hard-blocked: iwd: Error bringing interface 4 up: Operation not possible due to RF-kill ACPI platforms can opt out through ath12k_acpi_get_disable_rfkill(), but DT platforms cannot. A DT property was proposed for this and rejected, because it describes what the OS should do rather than the hardware. The suggested alternative was to handle these machines in the driver until a connector binding can describe a broken rfkill line. Add a table of board compatibles and hardware revisions on which hardware rfkill is known to be unusable, and skip the rfkill setup there. Other affected machines then only need a new table entry. Tested on a Surface Pro 11 (microsoft,denali) with next-20260929, booted with the unmodified mainline DTB: the interface is no longer blocked and connects. The Surface Laptop 7 entries (microsoft,romulus13 and microsoft,romulus15) are based on reports from Jérôme de Bretagne and Dale Whinham. I have not tested them. Link: https://lore.kernel.org/all/2ixwny6hzumlqmok5cjwutvtk7nzmx4t3dhxc44ao4l2u755pj@n6m7jyxf2o62/ Suggested-by: Manivannan Sadhasivam Assisted-by: LLM Signed-off-by: François Roux Tested-by: Dale Whinham Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20261006062728.45007-1-franzelfranzel@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 3ed98eb746b5..c9df99bc54fd 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -74,6 +74,35 @@ ath12k_mem_profile_based_param ath12k_mem_profile_based_param[] = { }, }; +/* + * On these machines the OS cannot control the WCN7850 hardware rfkill + * input, which reads as "radio off", so hardware rfkill must stay + * disabled. Ideally this would come from the connector node of the board + * DT, but that binding does not exist yet. + */ +static const struct ath12k_core_broken_rfkill_table { + enum ath12k_hw_rev hw_rev; + const char *compatible; +} ath12k_core_broken_rfkill_table[] = { + { ATH12K_HW_WCN7850_HW20, "microsoft,denali" }, + { ATH12K_HW_WCN7850_HW20, "microsoft,romulus13" }, + { ATH12K_HW_WCN7850_HW20, "microsoft,romulus15" }, + { /* Sentinel */ } +}; + +static bool ath12k_core_rfkill_is_broken(struct ath12k_base *ab) +{ + const struct ath12k_core_broken_rfkill_table *entry; + + for (entry = ath12k_core_broken_rfkill_table; entry->compatible; entry++) { + if (ab->hw_rev == entry->hw_rev && + of_machine_is_compatible(entry->compatible)) + return true; + } + + return false; +} + static int ath12k_core_rfkill_config(struct ath12k_base *ab) { struct ath12k *ar; @@ -85,6 +114,9 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) if (ath12k_acpi_get_disable_rfkill(ab)) return 0; + if (ath12k_core_rfkill_is_broken(ab)) + return 0; + for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; From ebc938ba1d5ff33652013b99c7b9a74f05690336 Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Thu, 1 Oct 2026 10:24:23 +0530 Subject: [PATCH 13/18] wifi: ath12k: Fix NAPI resource leaks in AHB external IRQ paths Currently, NAPI instances and their associated netdevs allocated for AHB external IRQ groups are not released when external IRQ configuration fails. They are also not freed when arch_init() fails after ath12k_ahb_config_irq() succeeds during probe, or when the driver is unloaded. Fix this by adding a common cleanup helper to release the NAPI instances and associated netdevs. Call it from the external IRQ configuration error path, probe error path, and driver cleanup path. Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Fixes: 6cee30f0da75 ("wifi: ath12k: add AHB driver support for IPQ5332") Fixes: 614c23e24ee8 ("wifi: ath12k: Support arch-specific DP device allocation") Signed-off-by: Aaradhana Sahu Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261001045423.493121-1-aaradhana.sahu@oss.qualcomm.com [renamed label err_free_irq] Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/ahb.c | 41 ++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 85360365aa4b..2c55aae06dd0 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -191,6 +191,26 @@ static void __ath12k_ahb_ext_irq_disable(struct ath12k_base *ab) } } +static void ath12k_ahb_free_napi_for_group(struct ath12k_ext_irq_grp *irq_grp) +{ + netif_napi_del(&irq_grp->napi); + free_netdev(irq_grp->napi_ndev); + irq_grp->napi_ndev = NULL; +} + +static void ath12k_ahb_free_napi(struct ath12k_base *ab, int num_ext_irq_grp) +{ + int i; + + for (i = 0; i < num_ext_irq_grp; i++) + ath12k_ahb_free_napi_for_group(&ab->ext_irq_grp[i]); +} + +static void ath12k_ahb_free_ext_napi(struct ath12k_base *ab) +{ + ath12k_ahb_free_napi(ab, ATH12K_EXT_IRQ_GRP_NUM_MAX); +} + static void ath12k_ahb_ext_grp_enable(struct ath12k_ext_irq_grp *irq_grp) { int i; @@ -622,8 +642,10 @@ static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) irq_grp->grp_id = i; irq_grp->napi_ndev = alloc_netdev_dummy(0); - if (!irq_grp->napi_ndev) - return -ENOMEM; + if (!irq_grp->napi_ndev) { + ret = -ENOMEM; + goto free_napi_ndev; + } netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, ath12k_ahb_ext_grp_napi_poll); @@ -675,12 +697,19 @@ static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) ath12k_ahb_ext_interrupt_handler, IRQF_TRIGGER_RISING, irq_name[irq_idx], irq_grp); - if (ret) + if (ret) { ath12k_warn(ab, "failed request_irq for %d\n", irq); + ath12k_ahb_free_napi_for_group(irq_grp); + goto free_napi_ndev; + } } } return 0; + +free_napi_ndev: + ath12k_ahb_free_napi(ab, i); + return ret; } static int ath12k_ahb_config_irq(struct ath12k_base *ab) @@ -1353,7 +1382,7 @@ static int ath12k_ahb_probe(struct platform_device *pdev) ret = ab_ahb->device_family_ops->arch_init(ab); if (ret) { ath12k_err(ab, "AHB arch_init failed %d\n", ret); - goto err_rproc_deconfigure; + goto err_free_ext_napi; } ret = ath12k_core_init(ab); @@ -1367,6 +1396,9 @@ static int ath12k_ahb_probe(struct platform_device *pdev) err_deinit_arch: ab_ahb->device_family_ops->arch_deinit(ab); +err_free_ext_napi: + ath12k_ahb_free_ext_napi(ab); + err_rproc_deconfigure: mutex_lock(&ath12k_rproc_info_lock); ath12k_ahb_deconfigure_rproc(ab); @@ -1409,6 +1441,7 @@ static void ath12k_ahb_free_resources(struct ath12k_base *ab) struct platform_device *pdev = ab->pdev; struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + ath12k_ahb_free_ext_napi(ab); ath12k_hal_srng_deinit(ab); ath12k_ce_free_pipes(ab); ath12k_ahb_resource_deinit(ab); From 1c1a208e65a08191901cfded3bbccc4a54e4a75c Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 17:20:58 +0800 Subject: [PATCH 14/18] wifi: ath10k: avoid IDR mutation during TX cleanup ath10k_htt_flush_tx_queue() walks pending_tx with idr_for_each(). Its callback calls ath10k_txrx_tx_unref(), which removes the current entry from pending_tx through ath10k_htt_tx_free_msdu_id(). idr_for_each() keeps radix-tree iterator state across the callback. Removing the current entry can therefore invalidate that state and make the remaining walk unsafe. Use idr_for_each_entry(), which starts a fresh lookup after each callback. The current entry can then be removed safely, while the following idr_destroy() continues to release the IDR itself. Fixes: 89d6d83565e9 ("ath10k: use idr api for msdu_ids") Signed-off-by: Jiale Yao Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261003092104.520135-2-yaojiale02@163.com [changed ath10k_htt_tx_clean_up_pending() signature] Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/htt_tx.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index e46f579d745d..efaea150025b 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -505,9 +505,8 @@ int ath10k_htt_tx_start(struct ath10k_htt *htt) return ret; } -static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx) +static void ath10k_htt_tx_clean_up_pending(int msdu_id, struct ath10k *ar) { - struct ath10k *ar = ctx; struct ath10k_htt *htt = &ar->htt; struct htt_tx_done tx_done = {}; @@ -517,8 +516,6 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx) tx_done.status = HTT_TX_COMPL_STATE_DISCARD; ath10k_txrx_tx_unref(htt, &tx_done); - - return 0; } void ath10k_htt_tx_destroy(struct ath10k_htt *htt) @@ -535,8 +532,13 @@ void ath10k_htt_tx_destroy(struct ath10k_htt *htt) static void ath10k_htt_flush_tx_queue(struct ath10k_htt *htt) { + struct sk_buff *msdu; + int msdu_id; + ath10k_htc_stop_hl(htt->ar); - idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); + + idr_for_each_entry(&htt->pending_tx, msdu, msdu_id) + ath10k_htt_tx_clean_up_pending(msdu_id, htt->ar); } void ath10k_htt_tx_stop(struct ath10k_htt *htt) From 3e7f5d362b2b3a21b3adb4b860aad14b69a896ad Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 17:20:59 +0800 Subject: [PATCH 15/18] wifi: ath11k: avoid IDR mutation during pending mgmt TX cleanup The pending management TX cleanup walks txmgmt_idr with idr_for_each(), and its callback removes the current entry. This can invalidate the radix-tree iterator retained by idr_for_each(). Use idr_for_each_entry() so every iteration starts with a fresh lookup. Continue to remove each entry under txmgmt_idr_lock before freeing its skb, preventing concurrent TX completion from finding a stale skb pointer. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Jiale Yao Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261003092104.520135-3-yaojiale02@163.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/core.c | 3 +-- drivers/net/wireless/ath/ath11k/mac.c | 12 ++++++------ drivers/net/wireless/ath/ath11k/mac.h | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 1ad0a47653de..766ae27306f4 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -2448,8 +2448,7 @@ void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) complete(&ar->thermal.wmi_sync); wake_up(&ar->dp.tx_empty_waitq); - idr_for_each(&ar->txmgmt_idr, - ath11k_mac_tx_mgmt_pending_free, ar); + ath11k_mac_tx_mgmt_pending_free(ar); idr_destroy(&ar->txmgmt_idr); wake_up(&ar->txmgmt_empty_waitq); diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index c1fa42edd363..1ee32c7dc494 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6167,13 +6167,13 @@ static void ath11k_mac_tx_mgmt_free(struct ath11k *ar, int buf_id) ath11k_mgmt_over_wmi_tx_drop(ar, msdu); } -int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx) +void ath11k_mac_tx_mgmt_pending_free(struct ath11k *ar) { - struct ath11k *ar = ctx; + struct sk_buff *msdu; + int buf_id; - ath11k_mac_tx_mgmt_free(ar, buf_id); - - return 0; + idr_for_each_entry(&ar->txmgmt_idr, msdu, buf_id) + ath11k_mac_tx_mgmt_free(ar, buf_id); } static int ath11k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) @@ -10418,7 +10418,7 @@ static void __ath11k_mac_unregister(struct ath11k *ar) ieee80211_unregister_hw(ar->hw); - idr_for_each(&ar->txmgmt_idr, ath11k_mac_tx_mgmt_pending_free, ar); + ath11k_mac_tx_mgmt_pending_free(ar); idr_destroy(&ar->txmgmt_idr); kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 59f83c7175fd..127cb476b44b 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -162,7 +162,7 @@ struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id) void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); -int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); +void ath11k_mac_tx_mgmt_pending_free(struct ath11k *ar); u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy); From dc29226dada34a6d19af77b4b51e90f8a3a8de34 Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 17:21:00 +0800 Subject: [PATCH 16/18] wifi: ath11k: avoid IDR mutation during vif mgmt TX cleanup ath11k_mac_op_remove_interface() walks txmgmt_idr with idr_for_each(), and its callback removes each entry belonging to the interface. Removing the current entry can invalidate the radix-tree iterator retained by idr_for_each(). Move the walk into a helper that uses idr_for_each_entry(). It performs a fresh lookup for every iteration, so each matching entry can be removed through the locked removal helper without retaining iterator state across the removal. Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Jiale Yao Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261003092104.520135-4-yaojiale02@163.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/mac.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 1ee32c7dc494..1a237adaa37d 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6176,16 +6176,18 @@ void ath11k_mac_tx_mgmt_pending_free(struct ath11k *ar) ath11k_mac_tx_mgmt_free(ar, buf_id); } -static int ath11k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) +static void ath11k_mac_vif_txmgmt_cleanup(struct ath11k *ar, + struct ieee80211_vif *vif) { - struct ieee80211_vif *vif = ctx; - struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB((struct sk_buff *)skb); - struct ath11k *ar = skb_cb->ar; + struct ath11k_skb_cb *skb_cb; + struct sk_buff *skb; + int buf_id; - if (skb_cb->vif == vif) - ath11k_mac_tx_mgmt_free(ar, buf_id); - - return 0; + idr_for_each_entry(&ar->txmgmt_idr, skb, buf_id) { + skb_cb = ATH11K_SKB_CB(skb); + if (skb_cb->vif == vif) + ath11k_mac_tx_mgmt_free(ar, buf_id); + } } static int ath11k_mac_mgmt_tx_wmi(struct ath11k *ar, struct ath11k_vif *arvif, @@ -7417,8 +7419,7 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, ath11k_peer_cleanup(ar, arvif->vdev_id); - idr_for_each(&ar->txmgmt_idr, - ath11k_mac_vif_txmgmt_idr_remove, vif); + ath11k_mac_vif_txmgmt_cleanup(ar, vif); for (i = 0; i < ab->hw_params.hal_params->num_tx_rings; i++) { spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); From 94c7b50351fd30e1dfe1b4051197a1445d8547e1 Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 17:21:01 +0800 Subject: [PATCH 17/18] wifi: ath12k: avoid IDR mutation during pending mgmt TX cleanup The pending management TX cleanup walks txmgmt_idr with idr_for_each(), and its callback removes the current entry. This can invalidate the radix-tree iterator retained by idr_for_each(). Use idr_for_each_entry() so every iteration starts with a fresh lookup. Continue to remove each entry under txmgmt_idr_lock before freeing its skb, preventing concurrent TX completion from finding a stale skb pointer. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Jiale Yao Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261003092104.520135-5-yaojiale02@163.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 3 +-- drivers/net/wireless/ath/ath12k/mac.c | 12 ++++++------ drivers/net/wireless/ath/ath12k/mac.h | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index c9df99bc54fd..0110f78400de 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1571,8 +1571,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) complete_all(&ar->thermal.wmi_sync); wake_up(&ar->dp.tx_empty_waitq); - idr_for_each(&ar->txmgmt_idr, - ath12k_mac_tx_mgmt_pending_free, ar); + ath12k_mac_tx_mgmt_pending_free(ar); idr_destroy(&ar->txmgmt_idr); wake_up(&ar->txmgmt_empty_waitq); diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index db6856ec6c14..56c3cc8506e1 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9194,13 +9194,13 @@ static void ath12k_mac_tx_mgmt_free(struct ath12k *ar, int buf_id) ath12k_mgmt_over_wmi_tx_drop(ar, msdu); } -int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx) +void ath12k_mac_tx_mgmt_pending_free(struct ath12k *ar) { - struct ath12k *ar = ctx; + struct sk_buff *msdu; + int buf_id; - ath12k_mac_tx_mgmt_free(ar, buf_id); - - return 0; + idr_for_each_entry(&ar->txmgmt_idr, msdu, buf_id) + ath12k_mac_tx_mgmt_free(ar, buf_id); } static int ath12k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) @@ -14738,7 +14738,7 @@ static struct wiphy_iftype_ext_capab ath12k_iftypes_ext_capa[] = { static void ath12k_mac_cleanup_unregister(struct ath12k *ar) { - idr_for_each(&ar->txmgmt_idr, ath12k_mac_tx_mgmt_pending_free, ar); + ath12k_mac_tx_mgmt_pending_free(ar); idr_destroy(&ar->txmgmt_idr); kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index aba98afd4365..ae44ebfd9bd9 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -173,7 +173,7 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) void ath12k_mac_drain_tx(struct ath12k *ar); void ath12k_mac_peer_cleanup_all(struct ath12k *ar); void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah); -int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); +void ath12k_mac_tx_mgmt_pending_free(struct ath12k *ar); enum rate_info_bw ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw); enum ath12k_supported_bw ath12k_mac_mac80211_bw_to_ath12k_bw(enum rate_info_bw bw); enum hal_encrypt_type ath12k_dp_tx_get_encrypt_type(u32 cipher); From 4aa45334eb23d40bf31c43405daea9b73469f2a6 Mon Sep 17 00:00:00 2001 From: Jiale Yao Date: Sat, 3 Oct 2026 17:21:02 +0800 Subject: [PATCH 18/18] wifi: ath12k: avoid IDR mutation during vif mgmt TX cleanup ath12k_mac_vdev_delete() walks txmgmt_idr with idr_for_each(), and its callback removes each entry belonging to the interface. Removing the current entry can invalidate the radix-tree iterator retained by idr_for_each(). Move the walk into a helper that uses idr_for_each_entry(). It performs a fresh lookup for every iteration, so each matching entry can be removed through the locked removal helper without retaining iterator state across the removal. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Jiale Yao Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20261003092104.520135-6-yaojiale02@163.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 56c3cc8506e1..01f8c6572f65 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9203,16 +9203,18 @@ void ath12k_mac_tx_mgmt_pending_free(struct ath12k *ar) ath12k_mac_tx_mgmt_free(ar, buf_id); } -static int ath12k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx) +static void ath12k_mac_vif_txmgmt_cleanup(struct ath12k *ar, + struct ieee80211_vif *vif) { - struct ieee80211_vif *vif = ctx; - struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); - struct ath12k *ar = skb_cb->ar; + struct ath12k_skb_cb *skb_cb; + struct sk_buff *skb; + int buf_id; - if (skb_cb->vif == vif) - ath12k_mac_tx_mgmt_free(ar, buf_id); - - return 0; + idr_for_each_entry(&ar->txmgmt_idr, skb, buf_id) { + skb_cb = ATH12K_SKB_CB(skb); + if (skb_cb->vif == vif) + ath12k_mac_tx_mgmt_free(ar, buf_id); + } } static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arvif, @@ -10977,8 +10979,7 @@ static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ath12k_link_vif *arv ath12k_peer_cleanup(ar, arvif->vdev_id); ath12k_ahvif_put_link_cache(ahvif, arvif->link_id); - idr_for_each(&ar->txmgmt_idr, - ath12k_mac_vif_txmgmt_idr_remove, vif); + ath12k_mac_vif_txmgmt_cleanup(ar, vif); ath12k_mac_vif_unref(ath12k_ab_to_dp(ab), vif);