From: Greg Kroah-Hartman Date: Thu, 17 Apr 2025 14:20:48 +0000 (+0200) Subject: 6.14-stable patches X-Git-Tag: v6.12.24~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=921514db95bada03a6ad6f1b66324eaab00fb949;p=thirdparty%2Fkernel%2Fstable-queue.git 6.14-stable patches added patches: arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch iommufd-fail-replace-if-device-has-not-been-attached.patch iommufd-make-attach_handle-generic-than-fault-specific.patch wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch --- diff --git a/queue-6.14/arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch b/queue-6.14/arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch new file mode 100644 index 0000000000..6bd64209af --- /dev/null +++ b/queue-6.14/arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch @@ -0,0 +1,68 @@ +From a5951389e58d2e816eed3dbec5877de9327fd881 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Tue, 7 Jan 2025 12:06:02 -0800 +Subject: arm64: errata: Add newer ARM cores to the spectre_bhb_loop_affected() lists + +From: Douglas Anderson + +commit a5951389e58d2e816eed3dbec5877de9327fd881 upstream. + +When comparing to the ARM list [1], it appears that several ARM cores +were missing from the lists in spectre_bhb_loop_affected(). Add them. + +NOTE: for some of these cores it may not matter since other ways of +clearing the BHB may be used (like the CLRBHB instruction or ECBHB), +but it still seems good to have all the info from ARM's whitepaper +included. + +[1] https://developer.arm.com/Arm%20Security%20Center/Spectre-BHB + +Fixes: 558c303c9734 ("arm64: Mitigate spectre style branch history side channels") +Cc: stable@vger.kernel.org +Signed-off-by: Douglas Anderson +Reviewed-by: James Morse +Link: https://lore.kernel.org/r/20250107120555.v4.5.I4a9a527e03f663040721c5401c41de587d015c82@changeid +Signed-off-by: Catalin Marinas +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/kernel/proton-pack.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +--- a/arch/arm64/kernel/proton-pack.c ++++ b/arch/arm64/kernel/proton-pack.c +@@ -876,6 +876,14 @@ static u8 spectre_bhb_loop_affected(void + { + u8 k = 0; + ++ static const struct midr_range spectre_bhb_k132_list[] = { ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_X3), ++ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2), ++ }; ++ static const struct midr_range spectre_bhb_k38_list[] = { ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A715), ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A720), ++ }; + static const struct midr_range spectre_bhb_k32_list[] = { + MIDR_ALL_VERSIONS(MIDR_CORTEX_A78), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A78AE), +@@ -889,6 +897,7 @@ static u8 spectre_bhb_loop_affected(void + }; + static const struct midr_range spectre_bhb_k24_list[] = { + MIDR_ALL_VERSIONS(MIDR_CORTEX_A76), ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A76AE), + MIDR_ALL_VERSIONS(MIDR_CORTEX_A77), + MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), + MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_GOLD), +@@ -904,7 +913,11 @@ static u8 spectre_bhb_loop_affected(void + {}, + }; + +- if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k32_list)) ++ if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k132_list)) ++ k = 132; ++ else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k38_list)) ++ k = 38; ++ else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k32_list)) + k = 32; + else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k24_list)) + k = 24; diff --git a/queue-6.14/iommufd-fail-replace-if-device-has-not-been-attached.patch b/queue-6.14/iommufd-fail-replace-if-device-has-not-been-attached.patch new file mode 100644 index 0000000000..f23510ce40 --- /dev/null +++ b/queue-6.14/iommufd-fail-replace-if-device-has-not-been-attached.patch @@ -0,0 +1,69 @@ +From 55c85fa7579dc2e3f5399ef5bad67a44257c1a48 Mon Sep 17 00:00:00 2001 +From: Yi Liu +Date: Wed, 5 Mar 2025 19:48:42 -0800 +Subject: iommufd: Fail replace if device has not been attached + +From: Yi Liu + +commit 55c85fa7579dc2e3f5399ef5bad67a44257c1a48 upstream. + +The current implementation of iommufd_device_do_replace() implicitly +assumes that the input device has already been attached. However, there +is no explicit check to verify this assumption. If another device within +the same group has been attached, the replace operation might succeed, +but the input device itself may not have been attached yet. + +As a result, the input device might not be tracked in the +igroup->device_list, and its reserved IOVA might not be added. Despite +this, the caller might incorrectly assume that the device has been +successfully replaced, which could lead to unexpected behavior or errors. + +To address this issue, add a check to ensure that the input device has +been attached before proceeding with the replace operation. This check +will help maintain the integrity of the device tracking system and prevent +potential issues arising from incorrect assumptions about the device's +attachment status. + +Fixes: e88d4ec154a8 ("iommufd: Add iommufd_device_replace()") +Link: https://patch.msgid.link/r/20250306034842.5950-1-yi.l.liu@intel.com +Cc: stable@vger.kernel.org +Reviewed-by: Kevin Tian +Signed-off-by: Yi Liu +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/device.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/iommu/iommufd/device.c ++++ b/drivers/iommu/iommufd/device.c +@@ -354,6 +354,17 @@ iommufd_device_attach_reserved_iova(stru + + /* The device attach/detach/replace helpers for attach_handle */ + ++/* Check if idev is attached to igroup->hwpt */ ++static bool iommufd_device_is_attached(struct iommufd_device *idev) ++{ ++ struct iommufd_device *cur; ++ ++ list_for_each_entry(cur, &idev->igroup->device_list, group_item) ++ if (cur == idev) ++ return true; ++ return false; ++} ++ + static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt, + struct iommufd_device *idev) + { +@@ -592,6 +603,11 @@ iommufd_device_do_replace(struct iommufd + rc = -EINVAL; + goto err_unlock; + } ++ ++ if (!iommufd_device_is_attached(idev)) { ++ rc = -EINVAL; ++ goto err_unlock; ++ } + + if (hwpt == igroup->hwpt) { + mutex_unlock(&idev->igroup->lock); diff --git a/queue-6.14/iommufd-make-attach_handle-generic-than-fault-specific.patch b/queue-6.14/iommufd-make-attach_handle-generic-than-fault-specific.patch new file mode 100644 index 0000000000..43f41a3cc1 --- /dev/null +++ b/queue-6.14/iommufd-make-attach_handle-generic-than-fault-specific.patch @@ -0,0 +1,219 @@ +From fb21b1568adaa76af7a8c853f37c60fba8b28661 Mon Sep 17 00:00:00 2001 +From: Nicolin Chen +Date: Mon, 3 Feb 2025 21:00:54 -0800 +Subject: iommufd: Make attach_handle generic than fault specific + +From: Nicolin Chen + +commit fb21b1568adaa76af7a8c853f37c60fba8b28661 upstream. + +"attach_handle" was added exclusively for the iommufd_fault_iopf_handler() +used by IOPF/PRI use cases. Now, both the MSI and PASID series require to +reuse the attach_handle for non-fault cases. + +Add a set of new attach/detach/replace helpers that does the attach_handle +allocation/releasing/replacement in the common path and also handles those +fault specific routines such as iopf enabling/disabling and auto response. + +This covers both non-fault and fault cases in a clean way, replacing those +inline helpers in the header. The following patch will clean up those old +helpers in the fault.c file. + +Link: https://patch.msgid.link/r/32687df01c02291d89986a9fca897bbbe2b10987.1738645017.git.nicolinc@nvidia.com +Signed-off-by: Nicolin Chen +Reviewed-by: Yi Liu +Signed-off-by: Jason Gunthorpe +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iommu/iommufd/device.c | 105 ++++++++++++++++++++++++++++++++ + drivers/iommu/iommufd/fault.c | 8 +- + drivers/iommu/iommufd/iommufd_private.h | 33 +--------- + 3 files changed, 113 insertions(+), 33 deletions(-) + +--- a/drivers/iommu/iommufd/device.c ++++ b/drivers/iommu/iommufd/device.c +@@ -352,6 +352,111 @@ iommufd_device_attach_reserved_iova(stru + return 0; + } + ++/* The device attach/detach/replace helpers for attach_handle */ ++ ++static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt, ++ struct iommufd_device *idev) ++{ ++ struct iommufd_attach_handle *handle; ++ int rc; ++ ++ lockdep_assert_held(&idev->igroup->lock); ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) ++ return -ENOMEM; ++ ++ if (hwpt->fault) { ++ rc = iommufd_fault_iopf_enable(idev); ++ if (rc) ++ goto out_free_handle; ++ } ++ ++ handle->idev = idev; ++ rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group, ++ &handle->handle); ++ if (rc) ++ goto out_disable_iopf; ++ ++ return 0; ++ ++out_disable_iopf: ++ if (hwpt->fault) ++ iommufd_fault_iopf_disable(idev); ++out_free_handle: ++ kfree(handle); ++ return rc; ++} ++ ++static struct iommufd_attach_handle * ++iommufd_device_get_attach_handle(struct iommufd_device *idev) ++{ ++ struct iommu_attach_handle *handle; ++ ++ lockdep_assert_held(&idev->igroup->lock); ++ ++ handle = ++ iommu_attach_handle_get(idev->igroup->group, IOMMU_NO_PASID, 0); ++ if (IS_ERR(handle)) ++ return NULL; ++ return to_iommufd_handle(handle); ++} ++ ++static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt, ++ struct iommufd_device *idev) ++{ ++ struct iommufd_attach_handle *handle; ++ ++ handle = iommufd_device_get_attach_handle(idev); ++ iommu_detach_group_handle(hwpt->domain, idev->igroup->group); ++ if (hwpt->fault) { ++ iommufd_auto_response_faults(hwpt, handle); ++ iommufd_fault_iopf_disable(idev); ++ } ++ kfree(handle); ++} ++ ++static int iommufd_hwpt_replace_device(struct iommufd_device *idev, ++ struct iommufd_hw_pagetable *hwpt, ++ struct iommufd_hw_pagetable *old) ++{ ++ struct iommufd_attach_handle *handle, *old_handle = ++ iommufd_device_get_attach_handle(idev); ++ int rc; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (!handle) ++ return -ENOMEM; ++ ++ if (hwpt->fault && !old->fault) { ++ rc = iommufd_fault_iopf_enable(idev); ++ if (rc) ++ goto out_free_handle; ++ } ++ ++ handle->idev = idev; ++ rc = iommu_replace_group_handle(idev->igroup->group, hwpt->domain, ++ &handle->handle); ++ if (rc) ++ goto out_disable_iopf; ++ ++ if (old->fault) { ++ iommufd_auto_response_faults(hwpt, old_handle); ++ if (!hwpt->fault) ++ iommufd_fault_iopf_disable(idev); ++ } ++ kfree(old_handle); ++ ++ return 0; ++ ++out_disable_iopf: ++ if (hwpt->fault && !old->fault) ++ iommufd_fault_iopf_disable(idev); ++out_free_handle: ++ kfree(handle); ++ return rc; ++} ++ + int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt, + struct iommufd_device *idev) + { +--- a/drivers/iommu/iommufd/fault.c ++++ b/drivers/iommu/iommufd/fault.c +@@ -17,7 +17,7 @@ + #include "../iommu-priv.h" + #include "iommufd_private.h" + +-static int iommufd_fault_iopf_enable(struct iommufd_device *idev) ++int iommufd_fault_iopf_enable(struct iommufd_device *idev) + { + struct device *dev = idev->dev; + int ret; +@@ -50,7 +50,7 @@ static int iommufd_fault_iopf_enable(str + return ret; + } + +-static void iommufd_fault_iopf_disable(struct iommufd_device *idev) ++void iommufd_fault_iopf_disable(struct iommufd_device *idev) + { + mutex_lock(&idev->iopf_lock); + if (!WARN_ON(idev->iopf_enabled == 0)) { +@@ -98,8 +98,8 @@ int iommufd_fault_domain_attach_dev(stru + return ret; + } + +-static void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt, +- struct iommufd_attach_handle *handle) ++void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt, ++ struct iommufd_attach_handle *handle) + { + struct iommufd_fault *fault = hwpt->fault; + struct iopf_group *group, *next; +--- a/drivers/iommu/iommufd/iommufd_private.h ++++ b/drivers/iommu/iommufd/iommufd_private.h +@@ -504,35 +504,10 @@ int iommufd_fault_domain_replace_dev(str + struct iommufd_hw_pagetable *hwpt, + struct iommufd_hw_pagetable *old); + +-static inline int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt, +- struct iommufd_device *idev) +-{ +- if (hwpt->fault) +- return iommufd_fault_domain_attach_dev(hwpt, idev); +- +- return iommu_attach_group(hwpt->domain, idev->igroup->group); +-} +- +-static inline void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt, +- struct iommufd_device *idev) +-{ +- if (hwpt->fault) { +- iommufd_fault_domain_detach_dev(hwpt, idev); +- return; +- } +- +- iommu_detach_group(hwpt->domain, idev->igroup->group); +-} +- +-static inline int iommufd_hwpt_replace_device(struct iommufd_device *idev, +- struct iommufd_hw_pagetable *hwpt, +- struct iommufd_hw_pagetable *old) +-{ +- if (old->fault || hwpt->fault) +- return iommufd_fault_domain_replace_dev(idev, hwpt, old); +- +- return iommu_group_replace_domain(idev->igroup->group, hwpt->domain); +-} ++int iommufd_fault_iopf_enable(struct iommufd_device *idev); ++void iommufd_fault_iopf_disable(struct iommufd_device *idev); ++void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt, ++ struct iommufd_attach_handle *handle); + + static inline struct iommufd_viommu * + iommufd_get_viommu(struct iommufd_ucmd *ucmd, u32 id) diff --git a/queue-6.14/series b/queue-6.14/series index d9c30030f0..d806fc006a 100644 --- a/queue-6.14/series +++ b/queue-6.14/series @@ -441,3 +441,7 @@ spi-fsl-qspi-use-devm-function-instead-of-driver-remove.patch spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch thermal-drivers-mediatek-lvts-disable-monitor-mode-during-suspend.patch thermal-drivers-mediatek-lvts-disable-stage-3-thermal-threshold.patch +wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch +arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch +iommufd-make-attach_handle-generic-than-fault-specific.patch +iommufd-fail-replace-if-device-has-not-been-attached.patch diff --git a/queue-6.14/wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch b/queue-6.14/wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch new file mode 100644 index 0000000000..d04a843228 --- /dev/null +++ b/queue-6.14/wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch @@ -0,0 +1,265 @@ +From 02aae8e2f957adc1b15b6b8055316f8a154ac3f5 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Fri, 17 Jan 2025 14:17:37 +0800 +Subject: wifi: ath11k: update channel list in worker when wait flag is set + +From: Wen Gong + +commit 02aae8e2f957adc1b15b6b8055316f8a154ac3f5 upstream. + +With previous patch "wifi: ath11k: move update channel list from update +reg worker to reg notifier", ath11k_reg_update_chan_list() will be +called during reg_process_self_managed_hint(). + +reg_process_self_managed_hint() will hold rtnl_lock all the time. +But ath11k_reg_update_chan_list() may increase the occupation time of +rtnl_lock, because when wait flag is set, wait_for_completion_timeout() +will be called during 11d/hw scan. + +Should minimize the occupation time of rtnl_lock as much as possible +to avoid interfering with rest of the system. So move the update channel +list operation to a new worker, so that wait_for_completion_timeout() +won't be called and will not increase the occupation time of rtnl_lock. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Wen Gong +Co-developed-by: Kang Yang +Signed-off-by: Kang Yang +Reviewed-by: Aditya Kumar Singh +Link: https://patch.msgid.link/20250117061737.1921-3-quic_kangyang@quicinc.com +Signed-off-by: Jeff Johnson +Cc: Mario Limonciello +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath11k/core.c | 1 + drivers/net/wireless/ath/ath11k/core.h | 5 + + drivers/net/wireless/ath/ath11k/mac.c | 14 +++++ + drivers/net/wireless/ath/ath11k/reg.c | 85 ++++++++++++++++++++++----------- + drivers/net/wireless/ath/ath11k/reg.h | 3 - + drivers/net/wireless/ath/ath11k/wmi.h | 1 + 6 files changed, 81 insertions(+), 28 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -2056,6 +2056,7 @@ void ath11k_core_halt(struct ath11k *ar) + ath11k_mac_scan_finish(ar); + ath11k_mac_peer_cleanup_all(ar); + cancel_delayed_work_sync(&ar->scan.timeout); ++ cancel_work_sync(&ar->channel_update_work); + cancel_work_sync(&ar->regd_update_work); + cancel_work_sync(&ab->update_11d_work); + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -685,7 +685,7 @@ struct ath11k { + struct mutex conf_mutex; + /* protects the radio specific data like debug stats, ppdu_stats_info stats, + * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, +- * channel context data, survey info, test mode data. ++ * channel context data, survey info, test mode data, channel_update_queue. + */ + spinlock_t data_lock; + +@@ -743,6 +743,9 @@ struct ath11k { + struct completion bss_survey_done; + + struct work_struct regd_update_work; ++ struct work_struct channel_update_work; ++ /* protected with data_lock */ ++ struct list_head channel_update_queue; + + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6283,6 +6283,7 @@ static void ath11k_mac_op_stop(struct ie + { + struct ath11k *ar = hw->priv; + struct htt_ppdu_stats_info *ppdu_stats, *tmp; ++ struct scan_chan_list_params *params; + int ret; + + ath11k_mac_drain_tx(ar); +@@ -6298,6 +6299,7 @@ static void ath11k_mac_op_stop(struct ie + mutex_unlock(&ar->conf_mutex); + + cancel_delayed_work_sync(&ar->scan.timeout); ++ cancel_work_sync(&ar->channel_update_work); + cancel_work_sync(&ar->regd_update_work); + cancel_work_sync(&ar->ab->update_11d_work); + +@@ -6307,10 +6309,19 @@ static void ath11k_mac_op_stop(struct ie + } + + spin_lock_bh(&ar->data_lock); ++ + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { + list_del(&ppdu_stats->list); + kfree(ppdu_stats); + } ++ ++ while ((params = list_first_entry_or_null(&ar->channel_update_queue, ++ struct scan_chan_list_params, ++ list))) { ++ list_del(¶ms->list); ++ kfree(params); ++ } ++ + spin_unlock_bh(&ar->data_lock); + + rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); +@@ -10014,6 +10025,7 @@ static const struct wiphy_iftype_ext_cap + + static void __ath11k_mac_unregister(struct ath11k *ar) + { ++ cancel_work_sync(&ar->channel_update_work); + cancel_work_sync(&ar->regd_update_work); + + ieee80211_unregister_hw(ar->hw); +@@ -10413,6 +10425,8 @@ int ath11k_mac_allocate(struct ath11k_ba + init_completion(&ar->thermal.wmi_sync); + + INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); ++ INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work); ++ INIT_LIST_HEAD(&ar->channel_update_queue); + INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); +--- a/drivers/net/wireless/ath/ath11k/reg.c ++++ b/drivers/net/wireless/ath/ath11k/reg.c +@@ -124,32 +124,7 @@ int ath11k_reg_update_chan_list(struct a + struct channel_param *ch; + enum nl80211_band band; + int num_channels = 0; +- int i, ret, left; +- +- if (wait && ar->state_11d != ATH11K_11D_IDLE) { +- left = wait_for_completion_timeout(&ar->completed_11d_scan, +- ATH11K_SCAN_TIMEOUT_HZ); +- if (!left) { +- ath11k_dbg(ar->ab, ATH11K_DBG_REG, +- "failed to receive 11d scan complete: timed out\n"); +- ar->state_11d = ATH11K_11D_IDLE; +- } +- ath11k_dbg(ar->ab, ATH11K_DBG_REG, +- "11d scan wait left time %d\n", left); +- } +- +- if (wait && +- (ar->scan.state == ATH11K_SCAN_STARTING || +- ar->scan.state == ATH11K_SCAN_RUNNING)) { +- left = wait_for_completion_timeout(&ar->scan.completed, +- ATH11K_SCAN_TIMEOUT_HZ); +- if (!left) +- ath11k_dbg(ar->ab, ATH11K_DBG_REG, +- "failed to receive hw scan complete: timed out\n"); +- +- ath11k_dbg(ar->ab, ATH11K_DBG_REG, +- "hw scan wait left time %d\n", left); +- } ++ int i, ret = 0; + + if (ar->state == ATH11K_STATE_RESTARTING) + return 0; +@@ -231,6 +206,16 @@ int ath11k_reg_update_chan_list(struct a + } + } + ++ if (wait) { ++ spin_lock_bh(&ar->data_lock); ++ list_add_tail(¶ms->list, &ar->channel_update_queue); ++ spin_unlock_bh(&ar->data_lock); ++ ++ queue_work(ar->ab->workqueue, &ar->channel_update_work); ++ ++ return 0; ++ } ++ + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + +@@ -811,6 +796,54 @@ ret: + return new_regd; + } + ++void ath11k_regd_update_chan_list_work(struct work_struct *work) ++{ ++ struct ath11k *ar = container_of(work, struct ath11k, ++ channel_update_work); ++ struct scan_chan_list_params *params; ++ struct list_head local_update_list; ++ int left; ++ ++ INIT_LIST_HEAD(&local_update_list); ++ ++ spin_lock_bh(&ar->data_lock); ++ list_splice_tail_init(&ar->channel_update_queue, &local_update_list); ++ spin_unlock_bh(&ar->data_lock); ++ ++ while ((params = list_first_entry_or_null(&local_update_list, ++ struct scan_chan_list_params, ++ list))) { ++ if (ar->state_11d != ATH11K_11D_IDLE) { ++ left = wait_for_completion_timeout(&ar->completed_11d_scan, ++ ATH11K_SCAN_TIMEOUT_HZ); ++ if (!left) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "failed to receive 11d scan complete: timed out\n"); ++ ar->state_11d = ATH11K_11D_IDLE; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "reg 11d scan wait left time %d\n", left); ++ } ++ ++ if ((ar->scan.state == ATH11K_SCAN_STARTING || ++ ar->scan.state == ATH11K_SCAN_RUNNING)) { ++ left = wait_for_completion_timeout(&ar->scan.completed, ++ ATH11K_SCAN_TIMEOUT_HZ); ++ if (!left) ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "failed to receive hw scan complete: timed out\n"); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_REG, ++ "reg hw scan wait left time %d\n", left); ++ } ++ ++ ath11k_wmi_send_scan_chan_list_cmd(ar, params); ++ list_del(¶ms->list); ++ kfree(params); ++ } ++} ++ + static bool ath11k_reg_is_world_alpha(char *alpha) + { + if (alpha[0] == '0' && alpha[1] == '0') +--- a/drivers/net/wireless/ath/ath11k/reg.h ++++ b/drivers/net/wireless/ath/ath11k/reg.h +@@ -1,7 +1,7 @@ + /* SPDX-License-Identifier: BSD-3-Clause-Clear */ + /* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. +- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. ++ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + + #ifndef ATH11K_REG_H +@@ -33,6 +33,7 @@ void ath11k_reg_init(struct ath11k *ar); + void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info); + void ath11k_reg_free(struct ath11k_base *ab); + void ath11k_regd_update_work(struct work_struct *work); ++void ath11k_regd_update_chan_list_work(struct work_struct *work); + struct ieee80211_regdomain * + ath11k_reg_build_regd(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, bool intersect, +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3817,6 +3817,7 @@ struct wmi_stop_scan_cmd { + }; + + struct scan_chan_list_params { ++ struct list_head list; + u32 pdev_id; + u16 nallchans; + struct channel_param ch_param[];