Skip to content

Commit e367c92

Browse files
Ramya Gnanasekarjeff-t-johnson
authored andcommitted
wifi: ath12k: Request vdev stats from firmware
Add support to request and print vdev stats from firmware through WMI. Sample output: ------------- cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/fw_stats/vdev_stats ath12k VDEV stats ================= VDEV ID 0 VDEV MAC address 00:03:7f:6c:9c:1a beacon snr 96 data snr 255 num rx frames 0 num rts fail 0 num rts success 0 num rx err 0 num rx discard 0 num tx not acked 0 num tx frames [00] 0 num tx frames [01] 0 num tx frames [02] 0 num tx frames [03] 2 num tx frames retries [00] 0 num tx frames retries [01] 0 num tx frames retries [02] 0 num tx frames retries [03] 0 num tx frames failures [00] 0 num tx frames failures [01] 0 num tx frames failures [02] 0 num tx frames failures [03] 0 tx rate history [00] 0x00000000 tx rate history [01] 0x00000000 tx rate history [02] 0x00000000 tx rate history [03] 0x00000000 tx rate history [04] 0x00000000 tx rate history [05] 0x00000000 tx rate history [06] 0x00000000 tx rate history [07] 0x00000000 tx rate history [08] 0x00000000 tx rate history [09] 0x00000000 beacon rssi history [00] 0 beacon rssi history [01] 0 beacon rssi history [02] 0 beacon rssi history [03] 0 beacon rssi history [04] 0 beacon rssi history [05] 0 beacon rssi history [06] 0 beacon rssi history [07] 0 beacon rssi history [08] 0 beacon rssi history [09] 0 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Ramya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com> Reviewed-by: Aditya Kumar Singh <aditya.kumar.singh@oss.qualcomm.com> Link: https://patch.msgid.link/20250124185330.1244585-2-ramya.gnanasekar@oss.qualcomm.com Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
1 parent 1b24394 commit e367c92

File tree

5 files changed

+394
-2
lines changed

5 files changed

+394
-2
lines changed

drivers/net/wireless/ath/ath12k/core.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ struct ath12k_fw_stats {
557557
struct list_head pdevs;
558558
struct list_head vdevs;
559559
struct list_head bcn;
560+
bool fw_stats_done;
560561
};
561562

562563
struct ath12k_dbg_htt_stats {
@@ -728,6 +729,7 @@ struct ath12k {
728729
struct completion mlo_setup_done;
729730
u32 mlo_setup_status;
730731
u8 ftm_msgref;
732+
struct ath12k_fw_stats fw_stats;
731733
};
732734

733735
struct ath12k_hw {
@@ -1078,6 +1080,25 @@ struct ath12k_pdev_map {
10781080
u8 pdev_idx;
10791081
};
10801082

1083+
struct ath12k_fw_stats_vdev {
1084+
struct list_head list;
1085+
1086+
u32 vdev_id;
1087+
u32 beacon_snr;
1088+
u32 data_snr;
1089+
u32 num_tx_frames[WLAN_MAX_AC];
1090+
u32 num_rx_frames;
1091+
u32 num_tx_frames_retries[WLAN_MAX_AC];
1092+
u32 num_tx_frames_failures[WLAN_MAX_AC];
1093+
u32 num_rts_fail;
1094+
u32 num_rts_success;
1095+
u32 num_rx_err;
1096+
u32 num_rx_discard;
1097+
u32 num_tx_not_acked;
1098+
u32 tx_rate_history[MAX_TX_RATE_VALUES];
1099+
u32 beacon_rssi_history[MAX_TX_RATE_VALUES];
1100+
};
1101+
10811102
int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab);
10821103
int ath12k_core_pre_init(struct ath12k_base *ab);
10831104
int ath12k_core_init(struct ath12k_base *ath12k);

drivers/net/wireless/ath/ath12k/debugfs.c

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// SPDX-License-Identifier: BSD-3-Clause-Clear
22
/*
33
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
4-
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4+
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
55
*/
66

77
#include "core.h"
8+
#include "debug.h"
89
#include "debugfs.h"
910
#include "debugfs_htt_stats.h"
1011

@@ -68,6 +69,199 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
6869
*/
6970
}
7071

72+
static void ath12k_fw_stats_vdevs_free(struct list_head *head)
73+
{
74+
struct ath12k_fw_stats_vdev *i, *tmp;
75+
76+
list_for_each_entry_safe(i, tmp, head, list) {
77+
list_del(&i->list);
78+
kfree(i);
79+
}
80+
}
81+
82+
void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
83+
{
84+
spin_lock_bh(&ar->data_lock);
85+
ar->fw_stats.fw_stats_done = false;
86+
ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
87+
spin_unlock_bh(&ar->data_lock);
88+
}
89+
90+
static int ath12k_debugfs_fw_stats_request(struct ath12k *ar,
91+
struct ath12k_fw_stats_req_params *param)
92+
{
93+
struct ath12k_base *ab = ar->ab;
94+
unsigned long timeout, time_left;
95+
int ret;
96+
97+
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
98+
99+
/* FW stats can get split when exceeding the stats data buffer limit.
100+
* In that case, since there is no end marking for the back-to-back
101+
* received 'update stats' event, we keep a 3 seconds timeout in case,
102+
* fw_stats_done is not marked yet
103+
*/
104+
timeout = jiffies + msecs_to_jiffies(3 * 1000);
105+
106+
ath12k_debugfs_fw_stats_reset(ar);
107+
108+
reinit_completion(&ar->fw_stats_complete);
109+
110+
ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
111+
param->vdev_id, param->pdev_id);
112+
113+
if (ret) {
114+
ath12k_warn(ab, "could not request fw stats (%d)\n",
115+
ret);
116+
return ret;
117+
}
118+
119+
time_left = wait_for_completion_timeout(&ar->fw_stats_complete,
120+
1 * HZ);
121+
/* If the wait timed out, return -ETIMEDOUT */
122+
if (!time_left)
123+
return -ETIMEDOUT;
124+
125+
/* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
126+
* when stats data buffer limit is reached. fw_stats_complete
127+
* is completed once host receives first event from firmware, but
128+
* still end might not be marked in the TLV.
129+
* Below loop is to confirm that firmware completed sending all the event
130+
* and fw_stats_done is marked true when end is marked in the TLV
131+
*/
132+
for (;;) {
133+
if (time_after(jiffies, timeout))
134+
break;
135+
136+
spin_lock_bh(&ar->data_lock);
137+
if (ar->fw_stats.fw_stats_done) {
138+
spin_unlock_bh(&ar->data_lock);
139+
break;
140+
}
141+
spin_unlock_bh(&ar->data_lock);
142+
}
143+
return 0;
144+
}
145+
146+
void
147+
ath12k_debugfs_fw_stats_process(struct ath12k *ar,
148+
struct ath12k_fw_stats *stats)
149+
{
150+
struct ath12k_base *ab = ar->ab;
151+
struct ath12k_pdev *pdev;
152+
bool is_end;
153+
static unsigned int num_vdev;
154+
size_t total_vdevs_started = 0;
155+
int i;
156+
157+
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
158+
if (list_empty(&stats->vdevs)) {
159+
ath12k_warn(ab, "empty vdev stats");
160+
return;
161+
}
162+
/* FW sends all the active VDEV stats irrespective of PDEV,
163+
* hence limit until the count of all VDEVs started
164+
*/
165+
rcu_read_lock();
166+
for (i = 0; i < ab->num_radios; i++) {
167+
pdev = rcu_dereference(ab->pdevs_active[i]);
168+
if (pdev && pdev->ar)
169+
total_vdevs_started += pdev->ar->num_started_vdevs;
170+
}
171+
rcu_read_unlock();
172+
173+
is_end = ((++num_vdev) == total_vdevs_started);
174+
175+
list_splice_tail_init(&stats->vdevs,
176+
&ar->fw_stats.vdevs);
177+
178+
if (is_end) {
179+
ar->fw_stats.fw_stats_done = true;
180+
num_vdev = 0;
181+
}
182+
return;
183+
}
184+
}
185+
186+
static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
187+
{
188+
struct ath12k *ar = inode->i_private;
189+
struct ath12k_fw_stats_req_params param;
190+
struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
191+
int ret;
192+
193+
guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
194+
195+
if (!ah)
196+
return -ENETDOWN;
197+
198+
if (ah->state != ATH12K_HW_STATE_ON)
199+
return -ENETDOWN;
200+
201+
void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC);
202+
if (!buf)
203+
return -ENOMEM;
204+
205+
param.pdev_id = ath12k_mac_get_target_pdev_id(ar);
206+
/* VDEV stats is always sent for all active VDEVs from FW */
207+
param.vdev_id = 0;
208+
param.stats_id = WMI_REQUEST_VDEV_STAT;
209+
210+
ret = ath12k_debugfs_fw_stats_request(ar, &param);
211+
if (ret) {
212+
ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
213+
return ret;
214+
}
215+
216+
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
217+
buf);
218+
219+
file->private_data = no_free_ptr(buf);
220+
221+
return 0;
222+
}
223+
224+
static int ath12k_release_vdev_stats(struct inode *inode, struct file *file)
225+
{
226+
kfree(file->private_data);
227+
228+
return 0;
229+
}
230+
231+
static ssize_t ath12k_read_vdev_stats(struct file *file,
232+
char __user *user_buf,
233+
size_t count, loff_t *ppos)
234+
{
235+
const char *buf = file->private_data;
236+
size_t len = strlen(buf);
237+
238+
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
239+
}
240+
241+
static const struct file_operations fops_vdev_stats = {
242+
.open = ath12k_open_vdev_stats,
243+
.release = ath12k_release_vdev_stats,
244+
.read = ath12k_read_vdev_stats,
245+
.owner = THIS_MODULE,
246+
.llseek = default_llseek,
247+
};
248+
249+
static
250+
void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
251+
{
252+
struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",
253+
ar->debug.debugfs_pdev);
254+
255+
/* all stats debugfs files created are under "fw_stats" directory
256+
* created per PDEV
257+
*/
258+
debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
259+
&fops_vdev_stats);
260+
261+
INIT_LIST_HEAD(&ar->fw_stats.vdevs);
262+
init_completion(&ar->fw_stats_complete);
263+
}
264+
71265
void ath12k_debugfs_register(struct ath12k *ar)
72266
{
73267
struct ath12k_base *ab = ar->ab;
@@ -92,6 +286,7 @@ void ath12k_debugfs_register(struct ath12k *ar)
92286
}
93287

94288
ath12k_debugfs_htt_stats_register(ar);
289+
ath12k_debugfs_fw_stats_register(ar);
95290
}
96291

97292
void ath12k_debugfs_unregister(struct ath12k *ar)

drivers/net/wireless/ath/ath12k/debugfs.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
22
/*
33
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
4-
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4+
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
55
*/
66

77
#ifndef _ATH12K_DEBUGFS_H_
@@ -12,6 +12,9 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab);
1212
void ath12k_debugfs_soc_destroy(struct ath12k_base *ab);
1313
void ath12k_debugfs_register(struct ath12k *ar);
1414
void ath12k_debugfs_unregister(struct ath12k *ar);
15+
void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
16+
struct ath12k_fw_stats *stats);
17+
void ath12k_debugfs_fw_stats_reset(struct ath12k *ar);
1518
#else
1619
static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab)
1720
{
@@ -29,6 +32,14 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar)
2932
{
3033
}
3134

35+
static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
36+
struct ath12k_fw_stats *stats)
37+
{
38+
}
39+
40+
static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
41+
{
42+
}
3243
#endif /* CONFIG_ATH12K_DEBUGFS */
3344

3445
#endif /* _ATH12K_DEBUGFS_H_ */

0 commit comments

Comments
 (0)