2 * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
8 #include <linux/netdevice.h>
9 #include <linux/types.h>
10 #include <linux/skbuff.h>
11 #include <linux/debugfs.h>
12 #include <linux/ieee80211.h>
13 #include <linux/export.h>
14 #include <net/mac80211.h>
15 #include "rc80211_minstrel.h"
16 #include "rc80211_minstrel_ht.h"
19 minstrel_ht_stats_dump(struct minstrel_ht_sta
*mi
, int i
, char *p
)
21 const struct mcs_group
*mg
;
22 unsigned int j
, tp_max
, tp_avg
, prob
, eprob
, tx_time
;
27 if (!mi
->groups
[i
].supported
)
30 mg
= &minstrel_mcs_groups
[i
];
33 if (gflags
& IEEE80211_TX_RC_40_MHZ_WIDTH
)
35 else if (gflags
& IEEE80211_TX_RC_80_MHZ_WIDTH
)
37 if (gflags
& IEEE80211_TX_RC_SHORT_GI
)
40 for (j
= 0; j
< MCS_GROUP_RATES
; j
++) {
41 struct minstrel_rate_stats
*mrs
= &mi
->groups
[i
].rates
[j
];
42 static const int bitrates
[4] = { 10, 20, 55, 110 };
43 int idx
= i
* MCS_GROUP_RATES
+ j
;
45 if (!(mi
->groups
[i
].supported
& BIT(j
)))
48 if (gflags
& IEEE80211_TX_RC_MCS
) {
49 p
+= sprintf(p
, "HT%c0 ", htmode
);
50 p
+= sprintf(p
, "%cGI ", gimode
);
51 p
+= sprintf(p
, "%d ", mg
->streams
);
52 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
53 p
+= sprintf(p
, "VHT%c0 ", htmode
);
54 p
+= sprintf(p
, "%cGI ", gimode
);
55 p
+= sprintf(p
, "%d ", mg
->streams
);
57 p
+= sprintf(p
, "CCK ");
58 p
+= sprintf(p
, "%cP ", j
< 4 ? 'L' : 'S');
59 p
+= sprintf(p
, "1 ");
62 *(p
++) = (idx
== mi
->max_tp_rate
[0]) ? 'A' : ' ';
63 *(p
++) = (idx
== mi
->max_tp_rate
[1]) ? 'B' : ' ';
64 *(p
++) = (idx
== mi
->max_tp_rate
[2]) ? 'C' : ' ';
65 *(p
++) = (idx
== mi
->max_tp_rate
[3]) ? 'D' : ' ';
66 *(p
++) = (idx
== mi
->max_prob_rate
) ? 'P' : ' ';
68 if (gflags
& IEEE80211_TX_RC_MCS
) {
69 p
+= sprintf(p
, " MCS%-2u", (mg
->streams
- 1) * 8 + j
);
70 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
71 p
+= sprintf(p
, " MCS%-1u/%1u", j
, mg
->streams
);
73 int r
= bitrates
[j
% 4];
75 p
+= sprintf(p
, " %2u.%1uM", r
/ 10, r
% 10);
78 p
+= sprintf(p
, " %3u ", idx
);
80 /* tx_time[rate(i)] in usec */
81 tx_time
= DIV_ROUND_CLOSEST(mg
->duration
[j
], 1000);
82 p
+= sprintf(p
, "%6u ", tx_time
);
84 tp_max
= minstrel_ht_get_tp_avg(mi
, i
, j
, MINSTREL_FRAC(100, 100));
85 tp_avg
= minstrel_ht_get_tp_avg(mi
, i
, j
, mrs
->prob_ewma
);
86 prob
= MINSTREL_TRUNC(mrs
->cur_prob
* 1000);
87 eprob
= MINSTREL_TRUNC(mrs
->prob_ewma
* 1000);
89 p
+= sprintf(p
, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
90 " %3u.%1u %3u %3u %-3u "
92 tp_max
/ 10, tp_max
% 10,
93 tp_avg
/ 10, tp_avg
% 10,
94 eprob
/ 10, eprob
% 10,
95 mrs
->prob_ewmsd
/ 10, mrs
->prob_ewmsd
% 10,
100 (unsigned long long)mrs
->succ_hist
,
101 (unsigned long long)mrs
->att_hist
);
108 minstrel_ht_stats_open(struct inode
*inode
, struct file
*file
)
110 struct minstrel_ht_sta_priv
*msp
= inode
->i_private
;
111 struct minstrel_ht_sta
*mi
= &msp
->ht
;
112 struct minstrel_debugfs_info
*ms
;
118 inode
->i_private
= &msp
->legacy
;
119 ret
= minstrel_stats_open(inode
, file
);
120 inode
->i_private
= msp
;
124 ms
= kmalloc(32768, GFP_KERNEL
);
128 file
->private_data
= ms
;
131 p
+= sprintf(p
, "\n");
132 p
+= sprintf(p
, " best ____________rate__________ "
133 "______statistics______ ________last_______ "
134 "______sum-of________\n");
135 p
+= sprintf(p
, "mode guard # rate [name idx airtime max_tp] "
136 "[ ø(tp) ø(prob) sd(prob)] [prob.|retry|suc|att] [#success | "
139 p
= minstrel_ht_stats_dump(mi
, MINSTREL_CCK_GROUP
, p
);
140 for (i
= 0; i
< MINSTREL_CCK_GROUP
; i
++)
141 p
= minstrel_ht_stats_dump(mi
, i
, p
);
142 for (i
++; i
< ARRAY_SIZE(mi
->groups
); i
++)
143 p
= minstrel_ht_stats_dump(mi
, i
, p
);
145 p
+= sprintf(p
, "\nTotal packet count:: ideal %d "
147 max(0, (int) mi
->total_packets
- (int) mi
->sample_packets
),
149 p
+= sprintf(p
, "Average # of aggregated frames per A-MPDU: %d.%d\n",
150 MINSTREL_TRUNC(mi
->avg_ampdu_len
),
151 MINSTREL_TRUNC(mi
->avg_ampdu_len
* 10) % 10);
152 ms
->len
= p
- ms
->buf
;
153 WARN_ON(ms
->len
+ sizeof(*ms
) > 32768);
155 return nonseekable_open(inode
, file
);
158 static const struct file_operations minstrel_ht_stat_fops
= {
159 .owner
= THIS_MODULE
,
160 .open
= minstrel_ht_stats_open
,
161 .read
= minstrel_stats_read
,
162 .release
= minstrel_stats_release
,
167 minstrel_ht_stats_csv_dump(struct minstrel_ht_sta
*mi
, int i
, char *p
)
169 const struct mcs_group
*mg
;
170 unsigned int j
, tp_max
, tp_avg
, prob
, eprob
, tx_time
;
175 if (!mi
->groups
[i
].supported
)
178 mg
= &minstrel_mcs_groups
[i
];
181 if (gflags
& IEEE80211_TX_RC_40_MHZ_WIDTH
)
183 else if (gflags
& IEEE80211_TX_RC_80_MHZ_WIDTH
)
185 if (gflags
& IEEE80211_TX_RC_SHORT_GI
)
188 for (j
= 0; j
< MCS_GROUP_RATES
; j
++) {
189 struct minstrel_rate_stats
*mrs
= &mi
->groups
[i
].rates
[j
];
190 static const int bitrates
[4] = { 10, 20, 55, 110 };
191 int idx
= i
* MCS_GROUP_RATES
+ j
;
193 if (!(mi
->groups
[i
].supported
& BIT(j
)))
196 if (gflags
& IEEE80211_TX_RC_MCS
) {
197 p
+= sprintf(p
, "HT%c0,", htmode
);
198 p
+= sprintf(p
, "%cGI,", gimode
);
199 p
+= sprintf(p
, "%d,", mg
->streams
);
200 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
201 p
+= sprintf(p
, "VHT%c0,", htmode
);
202 p
+= sprintf(p
, "%cGI,", gimode
);
203 p
+= sprintf(p
, "%d,", mg
->streams
);
205 p
+= sprintf(p
, "CCK,");
206 p
+= sprintf(p
, "%cP,", j
< 4 ? 'L' : 'S');
207 p
+= sprintf(p
, "1,");
210 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[0]) ? "A" : ""));
211 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[1]) ? "B" : ""));
212 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[2]) ? "C" : ""));
213 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_tp_rate
[3]) ? "D" : ""));
214 p
+= sprintf(p
, "%s" ,((idx
== mi
->max_prob_rate
) ? "P" : ""));
216 if (gflags
& IEEE80211_TX_RC_MCS
) {
217 p
+= sprintf(p
, ",MCS%-2u,", (mg
->streams
- 1) * 8 + j
);
218 } else if (gflags
& IEEE80211_TX_RC_VHT_MCS
) {
219 p
+= sprintf(p
, ",MCS%-1u/%1u,", j
, mg
->streams
);
221 int r
= bitrates
[j
% 4];
222 p
+= sprintf(p
, ",%2u.%1uM,", r
/ 10, r
% 10);
225 p
+= sprintf(p
, "%u,", idx
);
226 tx_time
= DIV_ROUND_CLOSEST(mg
->duration
[j
], 1000);
227 p
+= sprintf(p
, "%u,", tx_time
);
229 tp_max
= minstrel_ht_get_tp_avg(mi
, i
, j
, MINSTREL_FRAC(100, 100));
230 tp_avg
= minstrel_ht_get_tp_avg(mi
, i
, j
, mrs
->prob_ewma
);
231 prob
= MINSTREL_TRUNC(mrs
->cur_prob
* 1000);
232 eprob
= MINSTREL_TRUNC(mrs
->prob_ewma
* 1000);
234 p
+= sprintf(p
, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
236 tp_max
/ 10, tp_max
% 10,
237 tp_avg
/ 10, tp_avg
% 10,
238 eprob
/ 10, eprob
% 10,
239 mrs
->prob_ewmsd
/ 10, mrs
->prob_ewmsd
% 10,
240 prob
/ 10, prob
% 10,
244 (unsigned long long)mrs
->succ_hist
,
245 (unsigned long long)mrs
->att_hist
);
246 p
+= sprintf(p
, "%d,%d,%d.%d\n",
247 max(0, (int) mi
->total_packets
-
248 (int) mi
->sample_packets
),
250 MINSTREL_TRUNC(mi
->avg_ampdu_len
),
251 MINSTREL_TRUNC(mi
->avg_ampdu_len
* 10) % 10);
258 minstrel_ht_stats_csv_open(struct inode
*inode
, struct file
*file
)
260 struct minstrel_ht_sta_priv
*msp
= inode
->i_private
;
261 struct minstrel_ht_sta
*mi
= &msp
->ht
;
262 struct minstrel_debugfs_info
*ms
;
268 inode
->i_private
= &msp
->legacy
;
269 ret
= minstrel_stats_csv_open(inode
, file
);
270 inode
->i_private
= msp
;
274 ms
= kmalloc(32768, GFP_KERNEL
);
279 file
->private_data
= ms
;
283 p
= minstrel_ht_stats_csv_dump(mi
, MINSTREL_CCK_GROUP
, p
);
284 for (i
= 0; i
< MINSTREL_CCK_GROUP
; i
++)
285 p
= minstrel_ht_stats_csv_dump(mi
, i
, p
);
286 for (i
++; i
< ARRAY_SIZE(mi
->groups
); i
++)
287 p
= minstrel_ht_stats_csv_dump(mi
, i
, p
);
289 ms
->len
= p
- ms
->buf
;
290 WARN_ON(ms
->len
+ sizeof(*ms
) > 32768);
292 return nonseekable_open(inode
, file
);
295 static const struct file_operations minstrel_ht_stat_csv_fops
= {
296 .owner
= THIS_MODULE
,
297 .open
= minstrel_ht_stats_csv_open
,
298 .read
= minstrel_stats_read
,
299 .release
= minstrel_stats_release
,
304 minstrel_ht_add_sta_debugfs(void *priv
, void *priv_sta
, struct dentry
*dir
)
306 struct minstrel_ht_sta_priv
*msp
= priv_sta
;
308 msp
->dbg_stats
= debugfs_create_file("rc_stats", S_IRUGO
, dir
, msp
,
309 &minstrel_ht_stat_fops
);
310 msp
->dbg_stats_csv
= debugfs_create_file("rc_stats_csv", S_IRUGO
,
311 dir
, msp
, &minstrel_ht_stat_csv_fops
);
315 minstrel_ht_remove_sta_debugfs(void *priv
, void *priv_sta
)
317 struct minstrel_ht_sta_priv
*msp
= priv_sta
;
319 debugfs_remove(msp
->dbg_stats
);
320 debugfs_remove(msp
->dbg_stats_csv
);