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:
committed by
Greg Kroah-Hartman
parent
452f557615
commit
e57c74d760
@@ -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)
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user