1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
5 #include <linux/netdevice.h>
6 #include <linux/types.h>
7 #include <linux/skbuff.h>
8 #include <linux/debugfs.h>
9 #include <linux/ieee80211.h>
10 #include <linux/export.h>
11 #include <net/mac80211.h>
12 #include "rc80211_minstrel.h"
13 #include "rc80211_minstrel_ht.h"
16 minstrel_stats_read(struct file
*file
, char __user
*buf
, size_t len
, loff_t
*ppos
)
18 struct minstrel_debugfs_info
*ms
;
20 ms
= file
->private_data
;
21 return simple_read_from_buffer(buf
, len
, ppos
, ms
->buf
, ms
->len
);
25 minstrel_stats_release(struct inode
*inode
, struct file
*file
)
27 kfree(file
->private_data
);
32 minstrel_ht_stats_dump(struct minstrel_ht_sta
*mi
, int i
, char *p
)
34 const struct mcs_group
*mg
;
35 unsigned int j
, tp_max
, tp_avg
, eprob
, tx_time
;
40 if (!mi
->supported
[i
])
43 mg
= &minstrel_mcs_groups
[i
];
46 if (gflags
& IEEE80211_TX_RC_40_MHZ_WIDTH
)
48 else if (gflags
& IEEE80211_TX_RC_80_MHZ_WIDTH
)
50 if (gflags
& IEEE80211_TX_RC_SHORT_GI
)
53 for (j
= 0; j
< MCS_GROUP_RATES
; j
++) {
54 struct minstrel_rate_stats
*mrs
= &mi
->groups
[i
].rates
[j
];
55 static const int bitrates
[4] = { 10, 20, 55, 110 };
56 int idx
= i
* MCS_GROUP_RATES
+ j
;
57 unsigned int duration
;
59 if (!(mi
->supported
[i
] & BIT(j
)))
62 if (gflags
& IEEE80211_TX_RC_MCS
) {
63 p
+= sprintf(p
, "HT%c0 ", htmode
);
64 p
+= sprintf(p
, "%cGI ", gimode
);
65 p
+= sprintf(p
, "%d ", mg
->streams
);
66 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
67 p
+= sprintf(p
, "VHT%c0 ", htmode
);
68 p
+= sprintf(p
, "%cGI ", gimode
);
69 p
+= sprintf(p
, "%d ", mg
->streams
);
71 p
+= sprintf(p
, "CCK ");
72 p
+= sprintf(p
, "%cP ", j
< 4 ? 'L' : 'S');
73 p
+= sprintf(p
, "1 ");
76 *(p
++) = (idx
== mi
->max_tp_rate
[0]) ? 'A' : ' ';
77 *(p
++) = (idx
== mi
->max_tp_rate
[1]) ? 'B' : ' ';
78 *(p
++) = (idx
== mi
->max_tp_rate
[2]) ? 'C' : ' ';
79 *(p
++) = (idx
== mi
->max_tp_rate
[3]) ? 'D' : ' ';
80 *(p
++) = (idx
== mi
->max_prob_rate
) ? 'P' : ' ';
82 if (gflags
& IEEE80211_TX_RC_MCS
) {
83 p
+= sprintf(p
, " MCS%-2u", (mg
->streams
- 1) * 8 + j
);
84 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
85 p
+= sprintf(p
, " MCS%-1u/%1u", j
, mg
->streams
);
87 int r
= bitrates
[j
% 4];
89 p
+= sprintf(p
, " %2u.%1uM", r
/ 10, r
% 10);
92 p
+= sprintf(p
, " %3u ", idx
);
94 /* tx_time[rate(i)] in usec */
95 duration
= mg
->duration
[j
];
96 duration
<<= mg
->shift
;
97 tx_time
= DIV_ROUND_CLOSEST(duration
, 1000);
98 p
+= sprintf(p
, "%6u ", tx_time
);
100 tp_max
= minstrel_ht_get_tp_avg(mi
, i
, j
, MINSTREL_FRAC(100, 100));
101 tp_avg
= minstrel_ht_get_tp_avg(mi
, i
, j
, mrs
->prob_avg
);
102 eprob
= MINSTREL_TRUNC(mrs
->prob_avg
* 1000);
104 p
+= sprintf(p
, "%4u.%1u %4u.%1u %3u.%1u"
107 tp_max
/ 10, tp_max
% 10,
108 tp_avg
/ 10, tp_avg
% 10,
109 eprob
/ 10, eprob
% 10,
113 (unsigned long long)mrs
->succ_hist
,
114 (unsigned long long)mrs
->att_hist
);
121 minstrel_ht_stats_open(struct inode
*inode
, struct file
*file
)
123 struct minstrel_ht_sta_priv
*msp
= inode
->i_private
;
124 struct minstrel_ht_sta
*mi
= &msp
->ht
;
125 struct minstrel_debugfs_info
*ms
;
131 inode
->i_private
= &msp
->legacy
;
132 ret
= minstrel_stats_open(inode
, file
);
133 inode
->i_private
= msp
;
137 ms
= kmalloc(32768, GFP_KERNEL
);
141 file
->private_data
= ms
;
144 p
+= sprintf(p
, "\n");
146 " best ____________rate__________ ____statistics___ _____last____ ______sum-of________\n");
148 "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n");
150 p
= minstrel_ht_stats_dump(mi
, MINSTREL_CCK_GROUP
, p
);
151 for (i
= 0; i
< MINSTREL_CCK_GROUP
; i
++)
152 p
= minstrel_ht_stats_dump(mi
, i
, p
);
153 for (i
++; i
< ARRAY_SIZE(mi
->groups
); i
++)
154 p
= minstrel_ht_stats_dump(mi
, i
, p
);
156 p
+= sprintf(p
, "\nTotal packet count:: ideal %d "
158 max(0, (int) mi
->total_packets
- (int) mi
->sample_packets
),
160 if (mi
->avg_ampdu_len
)
161 p
+= sprintf(p
, "Average # of aggregated frames per A-MPDU: %d.%d\n",
162 MINSTREL_TRUNC(mi
->avg_ampdu_len
),
163 MINSTREL_TRUNC(mi
->avg_ampdu_len
* 10) % 10);
164 ms
->len
= p
- ms
->buf
;
165 WARN_ON(ms
->len
+ sizeof(*ms
) > 32768);
167 return nonseekable_open(inode
, file
);
170 static const struct file_operations minstrel_ht_stat_fops
= {
171 .owner
= THIS_MODULE
,
172 .open
= minstrel_ht_stats_open
,
173 .read
= minstrel_stats_read
,
174 .release
= minstrel_stats_release
,
179 minstrel_ht_stats_csv_dump(struct minstrel_ht_sta
*mi
, int i
, char *p
)
181 const struct mcs_group
*mg
;
182 unsigned int j
, tp_max
, tp_avg
, eprob
, tx_time
;
187 if (!mi
->supported
[i
])
190 mg
= &minstrel_mcs_groups
[i
];
193 if (gflags
& IEEE80211_TX_RC_40_MHZ_WIDTH
)
195 else if (gflags
& IEEE80211_TX_RC_80_MHZ_WIDTH
)
197 if (gflags
& IEEE80211_TX_RC_SHORT_GI
)
200 for (j
= 0; j
< MCS_GROUP_RATES
; j
++) {
201 struct minstrel_rate_stats
*mrs
= &mi
->groups
[i
].rates
[j
];
202 static const int bitrates
[4] = { 10, 20, 55, 110 };
203 int idx
= i
* MCS_GROUP_RATES
+ j
;
204 unsigned int duration
;
206 if (!(mi
->supported
[i
] & BIT(j
)))
209 if (gflags
& IEEE80211_TX_RC_MCS
) {
210 p
+= sprintf(p
, "HT%c0,", htmode
);
211 p
+= sprintf(p
, "%cGI,", gimode
);
212 p
+= sprintf(p
, "%d,", mg
->streams
);
213 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
214 p
+= sprintf(p
, "VHT%c0,", htmode
);
215 p
+= sprintf(p
, "%cGI,", gimode
);
216 p
+= sprintf(p
, "%d,", mg
->streams
);
218 p
+= sprintf(p
, "CCK,");
219 p
+= sprintf(p
, "%cP,", j
< 4 ? 'L' : 'S');
220 p
+= sprintf(p
, "1,");
223 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[0]) ? "A" : ""));
224 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[1]) ? "B" : ""));
225 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[2]) ? "C" : ""));
226 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[3]) ? "D" : ""));
227 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_prob_rate
) ? "P" : ""));
229 if (gflags
& IEEE80211_TX_RC_MCS
) {
230 p
+= sprintf(p
, ",MCS%-2u,", (mg
->streams
- 1) * 8 + j
);
231 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
232 p
+= sprintf(p
, ",MCS%-1u/%1u,", j
, mg
->streams
);
234 int r
= bitrates
[j
% 4];
235 p
+= sprintf(p
, ",%2u.%1uM,", r
/ 10, r
% 10);
238 p
+= sprintf(p
, "%u,", idx
);
240 duration
= mg
->duration
[j
];
241 duration
<<= mg
->shift
;
242 tx_time
= DIV_ROUND_CLOSEST(duration
, 1000);
243 p
+= sprintf(p
, "%u,", tx_time
);
245 tp_max
= minstrel_ht_get_tp_avg(mi
, i
, j
, MINSTREL_FRAC(100, 100));
246 tp_avg
= minstrel_ht_get_tp_avg(mi
, i
, j
, mrs
->prob_avg
);
247 eprob
= MINSTREL_TRUNC(mrs
->prob_avg
* 1000);
249 p
+= sprintf(p
, "%u.%u,%u.%u,%u.%u,%u,%u,"
251 tp_max
/ 10, tp_max
% 10,
252 tp_avg
/ 10, tp_avg
% 10,
253 eprob
/ 10, eprob
% 10,
257 (unsigned long long)mrs
->succ_hist
,
258 (unsigned long long)mrs
->att_hist
);
259 p
+= sprintf(p
, "%d,%d,%d.%d\n",
260 max(0, (int) mi
->total_packets
-
261 (int) mi
->sample_packets
),
263 MINSTREL_TRUNC(mi
->avg_ampdu_len
),
264 MINSTREL_TRUNC(mi
->avg_ampdu_len
* 10) % 10);
271 minstrel_ht_stats_csv_open(struct inode
*inode
, struct file
*file
)
273 struct minstrel_ht_sta_priv
*msp
= inode
->i_private
;
274 struct minstrel_ht_sta
*mi
= &msp
->ht
;
275 struct minstrel_debugfs_info
*ms
;
281 inode
->i_private
= &msp
->legacy
;
282 ret
= minstrel_stats_csv_open(inode
, file
);
283 inode
->i_private
= msp
;
287 ms
= kmalloc(32768, GFP_KERNEL
);
292 file
->private_data
= ms
;
296 p
= minstrel_ht_stats_csv_dump(mi
, MINSTREL_CCK_GROUP
, p
);
297 for (i
= 0; i
< MINSTREL_CCK_GROUP
; i
++)
298 p
= minstrel_ht_stats_csv_dump(mi
, i
, p
);
299 for (i
++; i
< ARRAY_SIZE(mi
->groups
); i
++)
300 p
= minstrel_ht_stats_csv_dump(mi
, i
, p
);
302 ms
->len
= p
- ms
->buf
;
303 WARN_ON(ms
->len
+ sizeof(*ms
) > 32768);
305 return nonseekable_open(inode
, file
);
308 static const struct file_operations minstrel_ht_stat_csv_fops
= {
309 .owner
= THIS_MODULE
,
310 .open
= minstrel_ht_stats_csv_open
,
311 .read
= minstrel_stats_read
,
312 .release
= minstrel_stats_release
,
317 minstrel_ht_add_sta_debugfs(void *priv
, void *priv_sta
, struct dentry
*dir
)
319 struct minstrel_ht_sta_priv
*msp
= priv_sta
;
321 debugfs_create_file("rc_stats", 0444, dir
, msp
,
322 &minstrel_ht_stat_fops
);
323 debugfs_create_file("rc_stats_csv", 0444, dir
, msp
,
324 &minstrel_ht_stat_csv_fops
);