]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: ath11k: update channel list in worker when wait flag is set
authorWen Gong <quic_wgong@quicinc.com>
Fri, 17 Jan 2025 06:17:37 +0000 (14:17 +0800)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Thu, 6 Feb 2025 00:14:18 +0000 (16:14 -0800)
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>
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/reg.c
drivers/net/wireless/ath/ath11k/reg.h
drivers/net/wireless/ath/ath11k/wmi.h

index 85077247b02517677ecea80948b12ef7f0f7f0d3..12dd37c2e90440e3e82c0525de3681fe0bdf71f7 100644 (file)
@@ -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);
 
index 92b14fc43cb65d74a5c61986a28edef2b59610ba..9842a4d65c7b9dfdc25cdd50aa18dd9856cea677 100644 (file)
@@ -686,7 +686,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;
 
@@ -744,6 +744,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;
index 586898e5318fa44e2274ad5c2f81c68f4e65efe0..14ed538365899387878817eb8674f4a76ac533bd 100644 (file)
@@ -6332,6 +6332,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
 {
        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);
@@ -6347,6 +6348,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
        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);
 
@@ -6356,10 +6358,19 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
        }
 
        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);
@@ -10063,6 +10074,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = {
 
 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);
@@ -10462,6 +10474,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
                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);
index 7bfe47ad62a07f10fa6a277d54b8fd0568530b4b..d62a2014315a0856770d4ffd438b5aa32bf4662f 100644 (file)
@@ -124,32 +124,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait)
        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 ath11k *ar, bool wait)
                }
        }
 
+       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')
index 263ea90619483ec8e1c235dd7ae7b2b1f537d706..72b48359401576d012020b360ee5d4742da0dfd4 100644 (file)
@@ -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,
index 508bd496488a64998aae31584d2e0d922d12ebe2..9fcffaa2f383c9348f5f07b139763de50212d856 100644 (file)
@@ -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[];