]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: ath11k: avoid burning CPU in ath11k_debugfs_fw_stats_request()
authorBaochen Qiang <quic_bqiang@quicinc.com>
Thu, 20 Feb 2025 08:24:42 +0000 (16:24 +0800)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Sat, 7 Jun 2025 14:36:02 +0000 (07:36 -0700)
We get report [1] that CPU is running a hot loop in
ath11k_debugfs_fw_stats_request():

94.60%     0.00%  i3status         [kernel.kallsyms]                 [k] do_syscall_64
        |
         --94.60%--do_syscall_64
                   |
                    --94.55%--__sys_sendmsg
                              ___sys_sendmsg
                              ____sys_sendmsg
                              netlink_sendmsg
                              netlink_unicast
                              genl_rcv
                              netlink_rcv_skb
                              genl_rcv_msg
                              |
                               --94.55%--genl_family_rcv_msg_dumpit
                                         __netlink_dump_start
                                         netlink_dump
                                         genl_dumpit
                                         nl80211_dump_station
                                         |
                                          --94.55%--ieee80211_dump_station
                                                    sta_set_sinfo
                                                    |
                                                     --94.55%--ath11k_mac_op_sta_statistics
                                                               ath11k_debugfs_get_fw_stats
                                                               |
                                                                --94.55%--ath11k_debugfs_fw_stats_request
                                                                          |
                                                                          |--41.73%--_raw_spin_lock_bh
                                                                          |
                                                                          |--22.74%--__local_bh_enable_ip
                                                                          |
                                                                          |--9.22%--_raw_spin_unlock_bh
                                                                          |
                                                                           --6.66%--srso_alias_safe_ret

This is because, if for whatever reason ar->fw_stats_done is not set by
ath11k_update_stats_event(), ath11k_debugfs_fw_stats_request() won't yield
CPU before an up to 3s timeout.

Change to completion mechanism to avoid CPU burning.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.37

Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Reported-by: Yury Vostrikov <mon@unformed.ru>
Closes: https://lore.kernel.org/all/7324ac7a-8b7a-42a5-aa19-de52138ff638@app.fastmail.com/ # [1]
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20250220082448.31039-2-quic_bqiang@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/debugfs.c
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/wmi.c

index 2e9f8a5e61e4c0cba476403764f42541948edd30..c8c5008be7f26a8502050796979d980c8b992279 100644 (file)
@@ -990,6 +990,7 @@ void ath11k_fw_stats_init(struct ath11k *ar)
        INIT_LIST_HEAD(&ar->fw_stats.bcn);
 
        init_completion(&ar->fw_stats_complete);
+       init_completion(&ar->fw_stats_done);
 }
 
 void ath11k_fw_stats_free(struct ath11k_fw_stats *stats)
index 339d4fca1ed5fbddd8f386589529f0febba98ecb..41bb81d00189c34979cada3711c6f0d0068699dd 100644 (file)
@@ -784,7 +784,7 @@ struct ath11k {
        u8 alpha2[REG_ALPHA2_LEN + 1];
        struct ath11k_fw_stats fw_stats;
        struct completion fw_stats_complete;
-       bool fw_stats_done;
+       struct completion fw_stats_done;
 
        /* protected by conf_mutex */
        bool ps_state_enable;
index bf192529e3fe26a91e72105a77b4c6f849b905ec..1d03e3aab011d5f77916808ffa9a9de66bacac96 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/vmalloc.h>
@@ -96,7 +96,6 @@ void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
 static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)
 {
        spin_lock_bh(&ar->data_lock);
-       ar->fw_stats_done = false;
        ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
        ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
        spin_unlock_bh(&ar->data_lock);
@@ -114,7 +113,7 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *
        /* WMI_REQUEST_PDEV_STAT request has been already processed */
 
        if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
-               ar->fw_stats_done = true;
+               complete(&ar->fw_stats_done);
                return;
        }
 
@@ -138,7 +137,7 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *
                                      &ar->fw_stats.vdevs);
 
                if (is_end) {
-                       ar->fw_stats_done = true;
+                       complete(&ar->fw_stats_done);
                        num_vdev = 0;
                }
                return;
@@ -158,7 +157,7 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *
                                      &ar->fw_stats.bcn);
 
                if (is_end) {
-                       ar->fw_stats_done = true;
+                       complete(&ar->fw_stats_done);
                        num_bcn = 0;
                }
        }
@@ -168,21 +167,15 @@ static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
                                           struct stats_request_params *req_param)
 {
        struct ath11k_base *ab = ar->ab;
-       unsigned long timeout, time_left;
+       unsigned long time_left;
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       /* FW stats can get split when exceeding the stats data buffer limit.
-        * In that case, since there is no end marking for the back-to-back
-        * received 'update stats' event, we keep a 3 seconds timeout in case,
-        * fw_stats_done is not marked yet
-        */
-       timeout = jiffies + secs_to_jiffies(3);
-
        ath11k_debugfs_fw_stats_reset(ar);
 
        reinit_completion(&ar->fw_stats_complete);
+       reinit_completion(&ar->fw_stats_done);
 
        ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
 
@@ -193,21 +186,18 @@ static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
        }
 
        time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
-
        if (!time_left)
                return -ETIMEDOUT;
 
-       for (;;) {
-               if (time_after(jiffies, timeout))
-                       break;
+       /* FW stats can get split when exceeding the stats data buffer limit.
+        * In that case, since there is no end marking for the back-to-back
+        * received 'update stats' event, we keep a 3 seconds timeout in case,
+        * fw_stats_done is not marked yet
+        */
+       time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
+       if (!time_left)
+               return -ETIMEDOUT;
 
-               spin_lock_bh(&ar->data_lock);
-               if (ar->fw_stats_done) {
-                       spin_unlock_bh(&ar->data_lock);
-                       break;
-               }
-               spin_unlock_bh(&ar->data_lock);
-       }
        return 0;
 }
 
index 08d7b136851fabb0a1c1032abcf9c01550e9f4f5..9ab501cd4f47acf06400894114580d62f2fb282b 100644 (file)
@@ -9390,11 +9390,11 @@ static int ath11k_fw_stats_request(struct ath11k *ar,
        lockdep_assert_held(&ar->conf_mutex);
 
        spin_lock_bh(&ar->data_lock);
-       ar->fw_stats_done = false;
        ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
        spin_unlock_bh(&ar->data_lock);
 
        reinit_completion(&ar->fw_stats_complete);
+       reinit_completion(&ar->fw_stats_done);
 
        ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
        if (ret) {
index d7f852bebf4aa23a4bf85661cfdf38d919e12e0d..27cb0bb06b93c8906367555de8354679f0621195 100644 (file)
@@ -8189,7 +8189,7 @@ static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *sk
         */
        if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
                list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
-               ar->fw_stats_done = true;
+               complete(&ar->fw_stats_done);
                goto complete;
        }