xref: /linux/drivers/net/wireless/ath/ath12k/debugfs_sta.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1 // SPDX-License-Identifier: BSD-3-Clause-Clear
2 /*
3  * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6 #include <linux/vmalloc.h>
7 
8 #include "debugfs_sta.h"
9 #include "core.h"
10 #include "peer.h"
11 #include "debug.h"
12 #include "debugfs_htt_stats.h"
13 #include "debugfs.h"
14 
15 static
ath12k_dbg_sta_dump_rate_stats(u8 * buf,u32 offset,const int size,bool he_rates_avail,const struct ath12k_rx_peer_rate_stats * stats)16 u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size,
17 				   bool he_rates_avail,
18 				   const struct ath12k_rx_peer_rate_stats *stats)
19 {
20 	static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = {
21 					"1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps",
22 					"9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps",
23 					"24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"};
24 	u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS;
25 	int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0;
26 	u32 i, len = offset, max = max_bw * max_gi * max_mcs;
27 	bool found;
28 
29 	len += scnprintf(buf + len, size - len, "\nEHT stats:\n");
30 	for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++)
31 		len += scnprintf(buf + len, size - len,
32 				   "MCS %d: %llu%s", i, stats->be_mcs_count[i],
33 				   (i + 1) % 8 ? "\t" : "\n");
34 
35 	len += scnprintf(buf + len, size - len, "\nHE stats:\n");
36 	for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++)
37 		len += scnprintf(buf + len, size - len,
38 				   "MCS %d: %llu%s", i, stats->he_mcs_count[i],
39 				   (i + 1) % 6 ? "\t" : "\n");
40 
41 	len += scnprintf(buf + len, size - len, "\nVHT stats:\n");
42 	for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++)
43 		len += scnprintf(buf + len, size - len,
44 				   "MCS %d: %llu%s", i, stats->vht_mcs_count[i],
45 				   (i + 1) % 5 ? "\t" : "\n");
46 
47 	len += scnprintf(buf + len, size - len, "\nHT stats:\n");
48 	for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++)
49 		len += scnprintf(buf + len, size - len,
50 				   "MCS %d: %llu%s", i, stats->ht_mcs_count[i],
51 				   (i + 1) % 8 ? "\t" : "\n");
52 
53 	len += scnprintf(buf + len, size - len, "\nLegacy stats:\n");
54 	for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++)
55 		len += scnprintf(buf + len, size - len,
56 				   "%s: %llu%s", legacy_rate_str[i],
57 				   stats->legacy_count[i],
58 				   (i + 1) % 4 ? "\t" : "\n");
59 
60 	len += scnprintf(buf + len, size - len, "\nNSS stats:\n");
61 	for (i = 0; i < HAL_RX_MAX_NSS; i++)
62 		len += scnprintf(buf + len, size - len,
63 				   "%dx%d: %llu ", i + 1, i + 1,
64 				   stats->nss_count[i]);
65 
66 	len += scnprintf(buf + len, size - len,
67 			  "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n",
68 			  stats->gi_count[0],
69 			  stats->gi_count[1],
70 			  stats->gi_count[2],
71 			  stats->gi_count[3]);
72 
73 	len += scnprintf(buf + len, size - len,
74 			   "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n",
75 			   stats->bw_count[0],
76 			   stats->bw_count[1],
77 			   stats->bw_count[2],
78 			   stats->bw_count[3],
79 			   stats->bw_count[4]);
80 
81 	for (i = 0; i < max; i++) {
82 		found = false;
83 
84 		for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) {
85 			if (stats->rx_rate[bw][gi][nss][mcs]) {
86 				found = true;
87 				break;
88 			}
89 		}
90 
91 		if (!found)
92 			goto skip_report;
93 
94 		switch (bw) {
95 		case HAL_RX_BW_20MHZ:
96 			bw_num = 20;
97 			break;
98 		case HAL_RX_BW_40MHZ:
99 			bw_num = 40;
100 			break;
101 		case HAL_RX_BW_80MHZ:
102 			bw_num = 80;
103 			break;
104 		case HAL_RX_BW_160MHZ:
105 			bw_num = 160;
106 			break;
107 		case HAL_RX_BW_320MHZ:
108 			bw_num = 320;
109 			break;
110 		}
111 
112 		len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ",
113 				 bw_num, gi, nss + 1, nss + 1);
114 
115 		for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) {
116 			if (stats->rx_rate[bw][gi][nss][mcs])
117 				len += scnprintf(buf + len, size - len,
118 						 " %d:%llu", mcs,
119 						 stats->rx_rate[bw][gi][nss][mcs]);
120 		}
121 
122 skip_report:
123 		if (nss++ >= max_mcs - 1) {
124 			nss = 0;
125 			if (gi++ >= max_gi - 1) {
126 				gi = 0;
127 				if (bw < max_bw - 1)
128 					bw++;
129 			}
130 		}
131 	}
132 
133 	len += scnprintf(buf + len, size - len, "\n");
134 
135 	return len - offset;
136 }
137 
ath12k_dbg_sta_dump_rx_stats(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)138 static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file,
139 					    char __user *user_buf,
140 					    size_t count, loff_t *ppos)
141 {
142 	struct ieee80211_link_sta *link_sta = file->private_data;
143 	struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta);
144 	const int size = ATH12K_STA_RX_STATS_BUF_SIZE;
145 	struct ath12k_hw *ah = ahsta->ahvif->ah;
146 	struct ath12k_rx_peer_stats *rx_stats;
147 	struct ath12k_link_sta *arsta;
148 	u8 link_id = link_sta->link_id;
149 	int len = 0, i, ret = 0;
150 	bool he_rates_avail;
151 	struct ath12k *ar;
152 
153 	wiphy_lock(ah->hw->wiphy);
154 
155 	if (!(BIT(link_id) & ahsta->links_map)) {
156 		wiphy_unlock(ah->hw->wiphy);
157 		return -ENOENT;
158 	}
159 
160 	arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
161 	if (!arsta || !arsta->arvif->ar) {
162 		wiphy_unlock(ah->hw->wiphy);
163 		return -ENOENT;
164 	}
165 
166 	ar = arsta->arvif->ar;
167 
168 	u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL);
169 	if (!buf) {
170 		ret = -ENOENT;
171 		goto out;
172 	}
173 
174 	spin_lock_bh(&ar->ab->base_lock);
175 
176 	rx_stats = arsta->rx_stats;
177 	if (!rx_stats) {
178 		ret = -ENOENT;
179 		goto unlock;
180 	}
181 
182 	len += scnprintf(buf + len, size - len, "RX peer stats:\n\n");
183 	len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n",
184 			 rx_stats->num_msdu);
185 	len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n",
186 			 rx_stats->tcp_msdu_count);
187 	len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n",
188 			 rx_stats->udp_msdu_count);
189 	len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n",
190 			 rx_stats->other_msdu_count);
191 	len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n",
192 			 rx_stats->ampdu_msdu_count);
193 	len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n",
194 			 rx_stats->non_ampdu_msdu_count);
195 	len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n",
196 			 rx_stats->stbc_count);
197 	len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n",
198 			 rx_stats->beamformed_count);
199 	len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n",
200 			 rx_stats->num_mpdu_fcs_ok);
201 	len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n",
202 			 rx_stats->num_mpdu_fcs_err);
203 
204 	he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false;
205 
206 	len += scnprintf(buf + len, size - len,
207 			 "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n",
208 			 rx_stats->pream_cnt[0], rx_stats->pream_cnt[1],
209 			 rx_stats->pream_cnt[2], rx_stats->pream_cnt[3],
210 			 rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]);
211 	len += scnprintf(buf + len, size - len,
212 			 "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n",
213 			 rx_stats->reception_type[0], rx_stats->reception_type[1],
214 			 rx_stats->reception_type[2], rx_stats->reception_type[3]);
215 
216 	len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):");
217 	for (i = 0; i <= IEEE80211_NUM_TIDS; i++)
218 		len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]);
219 
220 	len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n",
221 			 rx_stats->rx_duration);
222 
223 	len += scnprintf(buf + len, size - len,
224 			 "\nDCM: %llu\nRU26:  %llu\nRU52:  %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n",
225 			 rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0],
226 			 rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2],
227 			 rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4],
228 			 rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]);
229 
230 	len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n");
231 	len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail,
232 					      &rx_stats->pkt_stats);
233 
234 	len += scnprintf(buf + len, size - len, "\n");
235 
236 	len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n");
237 	len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail,
238 					      &rx_stats->byte_stats);
239 
240 unlock:
241 	spin_unlock_bh(&ar->ab->base_lock);
242 
243 	if (len)
244 		ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
245 out:
246 	wiphy_unlock(ah->hw->wiphy);
247 	return ret;
248 }
249 
250 static const struct file_operations fops_rx_stats = {
251 	.read = ath12k_dbg_sta_dump_rx_stats,
252 	.open = simple_open,
253 	.owner = THIS_MODULE,
254 	.llseek = default_llseek,
255 };
256 
ath12k_dbg_sta_reset_rx_stats(struct file * file,const char __user * buf,size_t count,loff_t * ppos)257 static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file,
258 					     const char __user *buf,
259 					     size_t count, loff_t *ppos)
260 {
261 	struct ieee80211_link_sta *link_sta = file->private_data;
262 	struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta);
263 	struct ath12k_hw *ah = ahsta->ahvif->ah;
264 	struct ath12k_rx_peer_stats *rx_stats;
265 	struct ath12k_link_sta *arsta;
266 	u8 link_id = link_sta->link_id;
267 	struct ath12k *ar;
268 	bool reset;
269 	int ret;
270 
271 	ret = kstrtobool_from_user(buf, count, &reset);
272 	if (ret)
273 		return ret;
274 
275 	if (!reset)
276 		return -EINVAL;
277 
278 	wiphy_lock(ah->hw->wiphy);
279 
280 	if (!(BIT(link_id) & ahsta->links_map)) {
281 		ret = -ENOENT;
282 		goto out;
283 	}
284 
285 	arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
286 	if (!arsta || !arsta->arvif->ar) {
287 		ret = -ENOENT;
288 		goto out;
289 	}
290 
291 	ar = arsta->arvif->ar;
292 
293 	spin_lock_bh(&ar->ab->base_lock);
294 
295 	rx_stats = arsta->rx_stats;
296 	if (!rx_stats) {
297 		spin_unlock_bh(&ar->ab->base_lock);
298 		ret = -ENOENT;
299 		goto out;
300 	}
301 
302 	memset(rx_stats, 0, sizeof(*rx_stats));
303 	spin_unlock_bh(&ar->ab->base_lock);
304 
305 	ret = count;
306 out:
307 	wiphy_unlock(ah->hw->wiphy);
308 	return ret;
309 }
310 
311 static const struct file_operations fops_reset_rx_stats = {
312 	.write = ath12k_dbg_sta_reset_rx_stats,
313 	.open = simple_open,
314 	.owner = THIS_MODULE,
315 	.llseek = default_llseek,
316 };
317 
ath12k_debugfs_link_sta_op_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,struct dentry * dir)318 void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw,
319 				    struct ieee80211_vif *vif,
320 				    struct ieee80211_link_sta *link_sta,
321 				    struct dentry *dir)
322 {
323 	struct ath12k *ar;
324 
325 	lockdep_assert_wiphy(hw->wiphy);
326 
327 	ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id);
328 	if (!ar)
329 		return;
330 
331 	if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) {
332 		debugfs_create_file("rx_stats", 0400, dir, link_sta,
333 				    &fops_rx_stats);
334 		debugfs_create_file("reset_rx_stats", 0200, dir, link_sta,
335 				    &fops_reset_rx_stats);
336 	}
337 }
338