Commit 9f6e82d1 authored by Baochen Qiang's avatar Baochen Qiang Committed by Jeff Johnson
Browse files

wifi: ath11k: avoid burning CPU in ath11k_debugfs_fw_stats_request()



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: d5c65159 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Reported-by: default avatarYury Vostrikov <mon@unformed.ru>
Closes: https://lore.kernel.org/all/7324ac7a-8b7a-42a5-aa19-de52138ff638@app.fastmail.com/

 # [1]
Signed-off-by: default avatarBaochen Qiang <quic_bqiang@quicinc.com>
Reviewed-by: default avatarVasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20250220082448.31039-2-quic_bqiang@quicinc.com


Signed-off-by: default avatarJeff Johnson <jeff.johnson@oss.qualcomm.com>
parent 59396366
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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)
+1 −1
Original line number Diff line number Diff line
@@ -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;
+14 −24
Original line number Diff line number Diff line
// 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;
}

+1 −1
Original line number Diff line number Diff line
@@ -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) {
+1 −1
Original line number Diff line number Diff line
@@ -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;
	}