]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Apr 2025 14:20:48 +0000 (16:20 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Apr 2025 14:20:48 +0000 (16:20 +0200)
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

queue-6.14/arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch [new file with mode: 0644]
queue-6.14/iommufd-fail-replace-if-device-has-not-been-attached.patch [new file with mode: 0644]
queue-6.14/iommufd-make-attach_handle-generic-than-fault-specific.patch [new file with mode: 0644]
queue-6.14/series
queue-6.14/wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch [new file with mode: 0644]

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 (file)
index 0000000..6bd6420
--- /dev/null
@@ -0,0 +1,68 @@
+From a5951389e58d2e816eed3dbec5877de9327fd881 Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+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 <dianders@chromium.org>
+
+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 <dianders@chromium.org>
+Reviewed-by: James Morse <james.morse@arm.com>
+Link: https://lore.kernel.org/r/20250107120555.v4.5.I4a9a527e03f663040721c5401c41de587d015c82@changeid
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..f23510c
--- /dev/null
@@ -0,0 +1,69 @@
+From 55c85fa7579dc2e3f5399ef5bad67a44257c1a48 Mon Sep 17 00:00:00 2001
+From: Yi Liu <yi.l.liu@intel.com>
+Date: Wed, 5 Mar 2025 19:48:42 -0800
+Subject: iommufd: Fail replace if device has not been attached
+
+From: Yi Liu <yi.l.liu@intel.com>
+
+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 <kevin.tian@intel.com>
+Signed-off-by: Yi Liu <yi.l.liu@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..43f41a3
--- /dev/null
@@ -0,0 +1,219 @@
+From fb21b1568adaa76af7a8c853f37c60fba8b28661 Mon Sep 17 00:00:00 2001
+From: Nicolin Chen <nicolinc@nvidia.com>
+Date: Mon, 3 Feb 2025 21:00:54 -0800
+Subject: iommufd: Make attach_handle generic than fault specific
+
+From: Nicolin Chen <nicolinc@nvidia.com>
+
+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 <nicolinc@nvidia.com>
+Reviewed-by: Yi Liu <yi.l.liu@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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)
index d9c30030f0653c1fed0722137399168cfb85f038..d806fc006a023032361384a4a50750ca00f8872b 100644 (file)
@@ -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 (file)
index 0000000..d04a843
--- /dev/null
@@ -0,0 +1,265 @@
+From 02aae8e2f957adc1b15b6b8055316f8a154ac3f5 Mon Sep 17 00:00:00 2001
+From: Wen Gong <quic_wgong@quicinc.com>
+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 <quic_wgong@quicinc.com>
+
+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 <quic_wgong@quicinc.com>
+Co-developed-by: Kang Yang <quic_kangyang@quicinc.com>
+Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
+Reviewed-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Link: https://patch.msgid.link/20250117061737.1921-3-quic_kangyang@quicinc.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Cc: Mario Limonciello <superm1@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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(&params->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(&params->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(&params->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[];