wifi: ath11k: avoid burning CPU in ath11k_debugfs_fw_stats_request()

[ Upstream commit 9f6e82d11bb9692a90d20b10f87345598945c803 ]

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: d5c65159f2 ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Baochen Qiang
2025-02-20 16:24:42 +08:00
committed by Greg Kroah-Hartman
parent 452f557615
commit e57c74d760
5 changed files with 18 additions and 27 deletions

View File

@@ -748,6 +748,7 @@ void ath11k_fw_stats_init(struct ath11k *ar)
INIT_LIST_HEAD(&ar->fw_stats.bcn); INIT_LIST_HEAD(&ar->fw_stats.bcn);
init_completion(&ar->fw_stats_complete); init_completion(&ar->fw_stats_complete);
init_completion(&ar->fw_stats_done);
} }
void ath11k_fw_stats_free(struct ath11k_fw_stats *stats) void ath11k_fw_stats_free(struct ath11k_fw_stats *stats)

View File

@@ -732,7 +732,7 @@ struct ath11k {
u8 alpha2[REG_ALPHA2_LEN + 1]; u8 alpha2[REG_ALPHA2_LEN + 1];
struct ath11k_fw_stats fw_stats; struct ath11k_fw_stats fw_stats;
struct completion fw_stats_complete; struct completion fw_stats_complete;
bool fw_stats_done; struct completion fw_stats_done;
/* protected by conf_mutex */ /* protected by conf_mutex */
bool ps_state_enable; bool ps_state_enable;

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear // SPDX-License-Identifier: BSD-3-Clause-Clear
/* /*
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. * 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> #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) static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)
{ {
spin_lock_bh(&ar->data_lock); spin_lock_bh(&ar->data_lock);
ar->fw_stats_done = false;
ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs); ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs); ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
spin_unlock_bh(&ar->data_lock); 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 */ /* WMI_REQUEST_PDEV_STAT request has been already processed */
if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) { if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
ar->fw_stats_done = true; complete(&ar->fw_stats_done);
return; return;
} }
@@ -138,7 +137,7 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *
&ar->fw_stats.vdevs); &ar->fw_stats.vdevs);
if (is_end) { if (is_end) {
ar->fw_stats_done = true; complete(&ar->fw_stats_done);
num_vdev = 0; num_vdev = 0;
} }
return; return;
@@ -158,7 +157,7 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *
&ar->fw_stats.bcn); &ar->fw_stats.bcn);
if (is_end) { if (is_end) {
ar->fw_stats_done = true; complete(&ar->fw_stats_done);
num_bcn = 0; num_bcn = 0;
} }
} }
@@ -168,21 +167,15 @@ static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
struct stats_request_params *req_param) struct stats_request_params *req_param)
{ {
struct ath11k_base *ab = ar->ab; struct ath11k_base *ab = ar->ab;
unsigned long timeout, time_left; unsigned long time_left;
int ret; int ret;
lockdep_assert_held(&ar->conf_mutex); 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); ath11k_debugfs_fw_stats_reset(ar);
reinit_completion(&ar->fw_stats_complete); reinit_completion(&ar->fw_stats_complete);
reinit_completion(&ar->fw_stats_done);
ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); 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); time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
if (!time_left) if (!time_left)
return -ETIMEDOUT; return -ETIMEDOUT;
for (;;) { /* FW stats can get split when exceeding the stats data buffer limit.
if (time_after(jiffies, timeout)) * In that case, since there is no end marking for the back-to-back
break; * 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; return 0;
} }

View File

@@ -9010,11 +9010,11 @@ static int ath11k_fw_stats_request(struct ath11k *ar,
lockdep_assert_held(&ar->conf_mutex); lockdep_assert_held(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock); spin_lock_bh(&ar->data_lock);
ar->fw_stats_done = false;
ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs); ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
spin_unlock_bh(&ar->data_lock); spin_unlock_bh(&ar->data_lock);
reinit_completion(&ar->fw_stats_complete); reinit_completion(&ar->fw_stats_complete);
reinit_completion(&ar->fw_stats_done);
ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
if (ret) { if (ret) {

View File

@@ -8183,7 +8183,7 @@ static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *sk
*/ */
if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs); list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
ar->fw_stats_done = true; complete(&ar->fw_stats_done);
goto complete; goto complete;
} }