ef57234bead8bf7d638218eb756fe9d2b252c9b8
[openwrt/staging/lynxis.git] /
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Mon, 1 Feb 2021 10:47:58 +0100
3 Subject: [PATCH] mac80211: minstrel_ht: add debugfs monitoring/controlling
4 API
5
6 This allows user space to monitor tx status and take over rate control
7 functionality.
8
9 Signed-off-by: Felix Fietkau <nbd@nbd.name>
10 ---
11 create mode 100644 net/mac80211/rc80211_minstrel_ht_api.c
12
13 --- a/local-symbols
14 +++ b/local-symbols
15 @@ -49,6 +49,7 @@ LIB80211_DEBUG=
16 MAC80211=
17 MAC80211_HAS_RC=
18 MAC80211_RC_MINSTREL=
19 +MAC80211_RC_MINSTREL_DEBUGFS_API=
20 MAC80211_RC_DEFAULT_MINSTREL=
21 MAC80211_RC_DEFAULT=
22 MAC80211_MESH=
23 --- a/net/mac80211/Kconfig
24 +++ b/net/mac80211/Kconfig
25 @@ -29,6 +29,15 @@ config MAC80211_RC_MINSTREL
26 help
27 This option enables the 'minstrel' TX rate control algorithm
28
29 +config MAC80211_RC_MINSTREL_DEBUGFS_API
30 + bool "Minstrel debugfs userspace control API"
31 + depends on MAC80211_RC_MINSTREL
32 + depends on MAC80211_DEBUGFS
33 + select RELAY
34 + help
35 + This option creates debugfs files that allow user space to observe
36 + and/or control minstrel rate selection behavior
37 +
38 choice
39 prompt "Default rate control algorithm"
40 depends on MAC80211_HAS_RC
41 --- a/net/mac80211/Makefile
42 +++ b/net/mac80211/Makefile
43 @@ -61,6 +61,9 @@ rc80211_minstrel-y := \
44 rc80211_minstrel-$(CPTCFG_MAC80211_DEBUGFS) += \
45 rc80211_minstrel_ht_debugfs.o
46
47 +rc80211_minstrel-$(CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API) += \
48 + rc80211_minstrel_ht_api.o
49 +
50 mac80211-$(CPTCFG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
51
52 ccflags-y += -DDEBUG
53 --- a/net/mac80211/rc80211_minstrel_ht.c
54 +++ b/net/mac80211/rc80211_minstrel_ht.c
55 @@ -276,7 +276,8 @@ static const u8 minstrel_sample_seq[] =
56 };
57
58 static void
59 -minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
60 +minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
61 + bool force);
62
63 /*
64 * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer)
65 @@ -346,7 +347,7 @@ minstrel_vht_get_group_idx(struct ieee80
66
67 static struct minstrel_rate_stats *
68 minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
69 - struct ieee80211_tx_rate *rate)
70 + struct ieee80211_tx_rate *rate, u16 *dest_idx)
71 {
72 int group, idx;
73
74 @@ -381,6 +382,7 @@ minstrel_ht_get_stats(struct minstrel_pr
75
76 idx = 0;
77 out:
78 + *dest_idx = MI_RATE(group, idx);
79 return &mi->groups[group].rates[idx];
80 }
81
82 @@ -1024,6 +1026,8 @@ minstrel_ht_update_stats(struct minstrel
83 tp_rate = tmp_legacy_tp_rate;
84
85 for (i = MCS_GROUP_RATES - 1; i >= 0; i--) {
86 + bool changed;
87 +
88 if (!(mi->supported[group] & BIT(i)))
89 continue;
90
91 @@ -1031,7 +1035,11 @@ minstrel_ht_update_stats(struct minstrel
92
93 mrs = &mg->rates[i];
94 mrs->retry_updated = false;
95 + changed = mrs->attempts > 0;
96 minstrel_ht_calc_rate_stats(mp, mrs);
97 + if (changed)
98 + minstrel_ht_report_rate_update(mp, mi, index,
99 + mrs);
100
101 if (mrs->att_hist)
102 last_prob = max(last_prob, mrs->prob_avg);
103 @@ -1080,7 +1088,8 @@ minstrel_ht_update_stats(struct minstrel
104
105 mi->max_prob_rate = tmp_max_prob_rate;
106
107 - minstrel_ht_refill_sample_rates(mi);
108 + if (!minstrel_ht_manual_mode(mp))
109 + minstrel_ht_refill_sample_rates(mi);
110
111 #ifdef CPTCFG_MAC80211_DEBUGFS
112 /* use fixed index if set */
113 @@ -1177,6 +1186,7 @@ minstrel_ht_tx_status(void *priv, struct
114 struct minstrel_priv *mp = priv;
115 u32 update_interval = mp->update_interval;
116 bool last, update = false;
117 + u16 rate_list[IEEE80211_TX_MAX_RATES] = {};
118 int i;
119
120 /* This packet was aggregated but doesn't carry status info */
121 @@ -1208,13 +1218,15 @@ minstrel_ht_tx_status(void *priv, struct
122 last = (i == IEEE80211_TX_MAX_RATES - 1) ||
123 !minstrel_ht_txstat_valid(mp, mi, &ar[i + 1]);
124
125 - rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
126 + rate = minstrel_ht_get_stats(mp, mi, &ar[i], &rate_list[i]);
127 if (last)
128 rate->success += info->status.ampdu_ack_len;
129
130 rate->attempts += ar[i].count * info->status.ampdu_len;
131 }
132
133 + minstrel_ht_report_tx_status(mp, mi, info, rate_list, i);
134 +
135 if (mp->hw->max_rates > 1) {
136 /*
137 * check for sudden death of spatial multiplexing,
138 @@ -1236,7 +1248,7 @@ minstrel_ht_tx_status(void *priv, struct
139 }
140
141 if (update)
142 - minstrel_ht_update_rates(mp, mi);
143 + minstrel_ht_update_rates(mp, mi, false);
144 }
145
146 static void
147 @@ -1299,7 +1311,7 @@ minstrel_calc_retransmit(struct minstrel
148 }
149
150
151 -static void
152 +void
153 minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
154 struct ieee80211_sta_rates *ratetbl, int offset, int index)
155 {
156 @@ -1408,11 +1420,15 @@ minstrel_ht_get_max_amsdu_len(struct min
157 }
158
159 static void
160 -minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
161 +minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
162 + bool force)
163 {
164 struct ieee80211_sta_rates *rates;
165 int i = 0;
166
167 + if (minstrel_ht_manual_mode(mp) && !force)
168 + return;
169 +
170 rates = kzalloc(sizeof(*rates), GFP_ATOMIC);
171 if (!rates)
172 return;
173 @@ -1439,7 +1455,7 @@ minstrel_ht_get_sample_rate(struct minst
174 {
175 u8 seq;
176
177 - if (mp->hw->max_rates > 1) {
178 + if (mp->hw->max_rates > 1 && !minstrel_ht_manual_mode(mp)) {
179 seq = mi->sample_seq;
180 mi->sample_seq = (seq + 1) % ARRAY_SIZE(minstrel_sample_seq);
181 seq = minstrel_sample_seq[seq];
182 @@ -1689,7 +1705,9 @@ minstrel_ht_update_caps(void *priv, stru
183
184 /* create an initial rate table with the lowest supported rates */
185 minstrel_ht_update_stats(mp, mi);
186 - minstrel_ht_update_rates(mp, mi);
187 + minstrel_ht_update_rates(mp, mi, true);
188 +
189 + minstrel_ht_sta_update(mp, mi);
190 }
191
192 static void
193 @@ -1725,12 +1743,18 @@ minstrel_ht_alloc_sta(void *priv, struct
194 max_rates = sband->n_bitrates;
195 }
196
197 - return kzalloc(sizeof(*mi), gfp);
198 + mi = kzalloc(sizeof(*mi), gfp);
199 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
200 + INIT_LIST_HEAD(&mi->list);
201 +#endif
202 +
203 + return mi;
204 }
205
206 static void
207 minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
208 {
209 + minstrel_ht_sta_remove(priv, priv_sta);
210 kfree(priv_sta);
211 }
212
213 @@ -1841,12 +1865,14 @@ static void minstrel_ht_add_debugfs(stru
214 mp->fixed_rate_idx = (u32) -1;
215 debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
216 &mp->fixed_rate_idx);
217 + minstrel_ht_add_debugfs_api(hw, priv, debugfsdir);
218 }
219 #endif
220
221 static void
222 minstrel_ht_free(void *priv)
223 {
224 + minstrel_ht_remove_debugfs_api(priv);
225 kfree(priv);
226 }
227
228 --- a/net/mac80211/rc80211_minstrel_ht.h
229 +++ b/net/mac80211/rc80211_minstrel_ht.h
230 @@ -72,6 +72,10 @@
231 #define MINSTREL_SAMPLE_RATES 5 /* rates per sample type */
232 #define MINSTREL_SAMPLE_INTERVAL (HZ / 50)
233
234 +#define MINSTREL_MONITOR_STA BIT(0)
235 +#define MINSTREL_MONITOR_TXS BIT(1)
236 +#define MINSTREL_MONITOR_STATS BIT(2)
237 +
238 struct minstrel_priv {
239 struct ieee80211_hw *hw;
240 bool has_mrr;
241 @@ -93,6 +97,13 @@ struct minstrel_priv {
242 */
243 u32 fixed_rate_idx;
244 #endif
245 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
246 + struct rchan *relay_ev;
247 + struct list_head stations;
248 + spinlock_t lock;
249 + u8 monitor;
250 + bool manual;
251 +#endif
252 };
253
254
255 @@ -153,6 +164,9 @@ struct minstrel_sample_category {
256 };
257
258 struct minstrel_ht_sta {
259 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
260 + struct list_head list;
261 +#endif
262 struct ieee80211_sta *sta;
263
264 /* ampdu length (average, per sampling interval) */
265 @@ -197,6 +211,80 @@ struct minstrel_ht_sta {
266 };
267
268 void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
269 +
270 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
271 +void minstrel_ht_sta_update(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
272 +void minstrel_ht_sta_remove(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
273 +void __minstrel_ht_report_tx_status(struct minstrel_priv *mp,
274 + struct minstrel_ht_sta *mi,
275 + struct ieee80211_tx_info *info,
276 + u16 *rate_list, int n_rates);
277 +void __minstrel_ht_report_rate_update(struct minstrel_priv *mp,
278 + struct minstrel_ht_sta *mi, u16 rate,
279 + struct minstrel_rate_stats *mrs);
280 +void minstrel_ht_add_debugfs_api(struct ieee80211_hw *hw, void *priv,
281 + struct dentry *dir);
282 +void minstrel_ht_remove_debugfs_api(void *priv);
283 +#else
284 +static inline void
285 +minstrel_ht_sta_update(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
286 +{
287 +}
288 +static inline void
289 +minstrel_ht_sta_remove(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
290 +{
291 +}
292 +static inline void
293 +minstrel_ht_add_debugfs_api(struct ieee80211_hw *hw, void *priv,
294 + struct dentry *dir)
295 +{
296 +}
297 +static inline void
298 +minstrel_ht_remove_debugfs_api(void *priv)
299 +{
300 +}
301 +#endif
302 +
303 +static inline void
304 +minstrel_ht_report_tx_status(struct minstrel_priv *mp,
305 + struct minstrel_ht_sta *mi,
306 + struct ieee80211_tx_info *info,
307 + u16 *rate_list, int n_rates)
308 +{
309 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
310 + if (!(mp->monitor & MINSTREL_MONITOR_TXS))
311 + return;
312 +
313 + __minstrel_ht_report_tx_status(mp, mi, info, rate_list, n_rates);
314 +#endif
315 +}
316 +
317 +static inline void
318 +minstrel_ht_report_rate_update(struct minstrel_priv *mp,
319 + struct minstrel_ht_sta *mi, u16 rate,
320 + struct minstrel_rate_stats *mrs)
321 +{
322 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
323 + if (!(mp->monitor & MINSTREL_MONITOR_STATS))
324 + return;
325 +
326 + __minstrel_ht_report_rate_update(mp, mi, rate, mrs);
327 +#endif
328 +}
329 +
330 +static inline bool
331 +minstrel_ht_manual_mode(struct minstrel_priv *mp)
332 +{
333 +#ifdef CPTCFG_MAC80211_RC_MINSTREL_DEBUGFS_API
334 + return mp->manual;
335 +#else
336 + return false;
337 +#endif
338 +}
339 +
340 +void minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
341 + struct ieee80211_sta_rates *ratetbl, int offset,
342 + int index);
343 int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
344 int prob_avg);
345
346 --- /dev/null
347 +++ b/net/mac80211/rc80211_minstrel_ht_api.c
348 @@ -0,0 +1,540 @@
349 +// SPDX-License-Identifier: GPL-2.0-only
350 +/*
351 + * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
352 + */
353 +#include <linux/kernel.h>
354 +#include <linux/debugfs.h>
355 +#include <linux/relay.h>
356 +#include <net/mac80211.h>
357 +#include "rc80211_minstrel_ht.h"
358 +
359 +enum sta_cmd {
360 + STA_CMD_PROBE,
361 + STA_CMD_RATES,
362 +};
363 +
364 +static void
365 +minstrel_ht_print_rate_durations(struct seq_file *s, int group)
366 +{
367 + const struct mcs_group *g = &minstrel_mcs_groups[group];
368 + int n_rates;
369 + int i;
370 +
371 + if (g->flags & IEEE80211_TX_RC_VHT_MCS)
372 + n_rates = 10;
373 + else
374 + n_rates = 8;
375 +
376 + seq_printf(s, "%x", g->duration[0] << g->shift);
377 + for (i = 1; i < n_rates; i++)
378 + seq_printf(s, ",%x", g->duration[i] << g->shift);
379 +}
380 +
381 +static int
382 +minstrel_ht_read_api_info(struct seq_file *s, void *data)
383 +{
384 + int i;
385 +
386 + seq_printf(s, "#group;index;offset;type;nss;bw;gi;airtime\n");
387 + seq_printf(s, "#sta;action;macaddr;overhead_mcs;overhead_legacy;supported\n");
388 + seq_printf(s, "#txs;macaddr;num_frames;num_acked;probe;rates;counts\n");
389 + seq_printf(s, "#stats;macaddr;rate;avg_prob;avg_tp;cur_success;cur_attempts;hist_success;hist_attempts\n");
390 + seq_printf(s, "#rates;macaddr;rates;counts\n");
391 + seq_printf(s, "#probe;macaddr;rate\n");
392 + for (i = 0; i < MINSTREL_GROUPS_NB; i++) {
393 + const struct mcs_group *g = &minstrel_mcs_groups[i];
394 + const char *type;
395 +
396 + if (i == MINSTREL_CCK_GROUP)
397 + type = "cck";
398 + else if (i == MINSTREL_OFDM_GROUP)
399 + type = "ofdm";
400 + else if (g->flags & IEEE80211_TX_RC_VHT_MCS)
401 + type = "vht";
402 + else
403 + type = "ht";
404 +
405 + seq_printf(s, "group;%x;%x;%s;%x;%x;%x;",
406 + i, (u32) MI_RATE(i, 0), type, g->streams, g->bw,
407 + !!(g->flags & IEEE80211_TX_RC_SHORT_GI));
408 + minstrel_ht_print_rate_durations(s, i);
409 + seq_printf(s, "\n");
410 + }
411 +
412 + return 0;
413 +}
414 +
415 +static struct dentry *
416 +create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
417 + struct rchan_buf *buf, int *is_global)
418 +{
419 + struct dentry *f;
420 +
421 + f = debugfs_create_file("api_event", mode, parent, buf,
422 + &relay_file_operations);
423 + if (IS_ERR(f))
424 + return NULL;
425 +
426 + *is_global = 1;
427 +
428 + return f;
429 +}
430 +
431 +static int
432 +remove_buf_file_cb(struct dentry *f)
433 +{
434 + debugfs_remove(f);
435 +
436 + return 0;
437 +}
438 +
439 +static struct rchan_callbacks relay_ev_cb = {
440 + .create_buf_file = create_buf_file_cb,
441 + .remove_buf_file = remove_buf_file_cb,
442 +};
443 +
444 +static void
445 +minstrel_ht_dump_sta(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
446 + const char *type)
447 +{
448 + char info[64 + MINSTREL_GROUPS_NB * 4];
449 + int ofs = 0;
450 + int i;
451 +
452 + ofs += scnprintf(info + ofs, sizeof(info) - ofs, "%llx;sta;%s;%pM;%x;%x;",
453 + (unsigned long long)ktime_get_boottime_ns(),
454 + type, mi->sta->addr, mi->overhead, mi->overhead_legacy);
455 +
456 + ofs += scnprintf(info + ofs, sizeof(info) - ofs, "%x",
457 + mi->supported[0]);
458 + for (i = 1; i < MINSTREL_GROUPS_NB; i++)
459 + ofs += scnprintf(info + ofs, sizeof(info) - ofs, ",%x",
460 + mi->supported[i]);
461 +
462 + ofs += scnprintf(info + ofs, sizeof(info) - ofs, "\n");
463 + relay_write(mp->relay_ev, info, ofs);
464 + relay_flush(mp->relay_ev);
465 +}
466 +
467 +static void
468 +__minstrel_ht_dump_stations(struct minstrel_priv *mp, const char *type)
469 +{
470 + struct minstrel_ht_sta *mi;
471 +
472 + list_for_each_entry(mi, &mp->stations, list)
473 + minstrel_ht_dump_sta(mp, mi, type);
474 +}
475 +
476 +static void
477 +minstrel_ht_dump_stations(struct minstrel_priv *mp)
478 +{
479 + spin_lock_bh(&mp->lock);
480 + __minstrel_ht_dump_stations(mp, "dump");
481 + spin_unlock_bh(&mp->lock);
482 +}
483 +
484 +static void
485 +minstrel_ht_api_start(struct minstrel_priv *mp, char *params)
486 +{
487 + char *cur;
488 + u8 mask = 0;
489 +
490 + spin_lock_bh(&mp->lock);
491 +
492 + while ((cur = strsep(&params, ";")) != NULL) {
493 + if (!strlen(cur))
494 + break;
495 +
496 + if (!strcmp(cur, "txs"))
497 + mask |= MINSTREL_MONITOR_TXS;
498 + else if (!strcmp(cur, "sta"))
499 + mask |= MINSTREL_MONITOR_STA;
500 + else if (!strcmp(cur, "stats"))
501 + mask |= MINSTREL_MONITOR_STATS;
502 + }
503 +
504 + if (!mask)
505 + mask = MINSTREL_MONITOR_TXS;
506 +
507 + if (!mp->monitor)
508 + __minstrel_ht_dump_stations(mp, "add");
509 + mp->monitor = mask | MINSTREL_MONITOR_STA;
510 +
511 + spin_unlock_bh(&mp->lock);
512 +}
513 +
514 +static void
515 +minstrel_ht_api_stop(struct minstrel_priv *mp)
516 +{
517 + spin_lock_bh(&mp->lock);
518 + mp->monitor = 0;
519 + relay_reset(mp->relay_ev);
520 + spin_unlock_bh(&mp->lock);
521 +}
522 +
523 +static void
524 +minstrel_ht_reset_sample_table(struct minstrel_ht_sta *mi)
525 +{
526 + memset(mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates, 0,
527 + sizeof(mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates));
528 +}
529 +
530 +static void
531 +minstrel_ht_api_set_manual(struct minstrel_priv *mp, bool manual)
532 +{
533 + struct minstrel_ht_sta *mi;
534 +
535 + mp->manual = manual;
536 +
537 + spin_lock_bh(&mp->lock);
538 + list_for_each_entry(mi, &mp->stations, list)
539 + minstrel_ht_reset_sample_table(mi);
540 + spin_unlock_bh(&mp->lock);
541 +}
542 +
543 +static struct minstrel_ht_sta *
544 +minstrel_ht_api_get_sta(struct minstrel_priv *mp, const u8 *macaddr)
545 +{
546 + struct minstrel_ht_sta *mi;
547 +
548 + list_for_each_entry(mi, &mp->stations, list) {
549 + if (!memcmp(mi->sta->addr, macaddr, ETH_ALEN))
550 + return mi;
551 + }
552 +
553 + return NULL;
554 +}
555 +
556 +static int
557 +minstrel_ht_get_args(char **dest, int dest_size, char *str, char *sep)
558 +{
559 + int i, n;
560 +
561 + for (i = 0, n = 0; i < dest_size; i++) {
562 + if (!str) {
563 + dest[i] = NULL;
564 + continue;
565 + }
566 +
567 + dest[i] = strsep(&str, sep);
568 + if (dest[i])
569 + n++;
570 + }
571 +
572 + return n;
573 +}
574 +
575 +static bool
576 +minstrel_ht_valid_rate(struct minstrel_ht_sta *mi, u32 rate)
577 +{
578 + int group, idx;
579 +
580 + group = MI_RATE_GROUP(rate);
581 + if (group >= MINSTREL_GROUPS_NB)
582 + return false;
583 +
584 + idx = MI_RATE_IDX(rate);
585 +
586 + return !!(mi->supported[group] & BIT(idx));
587 +}
588 +
589 +static int
590 +minstrel_ht_rate_from_str(struct minstrel_ht_sta *mi, const char *str)
591 +{
592 + unsigned int rate;
593 +
594 + if (kstrtouint(str, 16, &rate))
595 + return -EINVAL;
596 +
597 + if (!minstrel_ht_valid_rate(mi, rate))
598 + return -EINVAL;
599 +
600 + return rate;
601 +}
602 +
603 +static int
604 +minstrel_ht_set_probe_rate(struct minstrel_ht_sta *mi, const char *rate_str)
605 +{
606 + u16 *sample_rates;
607 + int rate, i;
608 +
609 + if (!rate_str)
610 + return -EINVAL;
611 +
612 + rate = minstrel_ht_rate_from_str(mi, rate_str);
613 + if (rate < 0)
614 + return rate;
615 +
616 + sample_rates = mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates;
617 + for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) {
618 + if (sample_rates[i])
619 + continue;
620 +
621 + sample_rates[i] = rate;
622 + mi->sample_time = jiffies;
623 + return 0;
624 + }
625 +
626 + return -ENOSPC;
627 +}
628 +
629 +static int
630 +minstrel_ht_set_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
631 + char *rate_str, char *count_str)
632 +{
633 + struct ieee80211_sta_rates *ratetbl;
634 + unsigned int count;
635 + char *countlist[4];
636 + char *ratelist[4];
637 + int rate;
638 + int n_rates;
639 + int n_count;
640 + int err = -EINVAL;
641 + int i;
642 +
643 + if (!rate_str || !count_str)
644 + return -EINVAL;
645 +
646 + ratetbl = kzalloc(sizeof(*ratetbl), GFP_ATOMIC);
647 + if (!ratetbl)
648 + return -ENOMEM;
649 +
650 + n_rates = minstrel_ht_get_args(ratelist, ARRAY_SIZE(ratelist),
651 + rate_str, ",");
652 + n_count = minstrel_ht_get_args(countlist, ARRAY_SIZE(countlist),
653 + count_str, ",");
654 + for (i = 0; i < min(n_rates, n_count); i++) {
655 + rate = minstrel_ht_rate_from_str(mi, ratelist[i]);
656 + if (rate < 0)
657 + goto error;
658 +
659 + if (kstrtouint(countlist[0], 16, &count))
660 + goto error;
661 +
662 + minstrel_ht_set_rate(mp, mi, ratetbl, i, rate);
663 + ratetbl->rate[i].count = count;
664 + ratetbl->rate[i].count_rts = count;
665 + ratetbl->rate[i].count_cts = count;
666 + }
667 +
668 + rate_control_set_rates(mp->hw, mi->sta, ratetbl);
669 +
670 + return 0;
671 +
672 +error:
673 + kfree(ratetbl);
674 + return err;
675 +}
676 +
677 +static int
678 +minstrel_ht_api_sta_cmd(struct minstrel_priv *mp, enum sta_cmd cmd,
679 + char *arg_str)
680 +{
681 + struct minstrel_ht_sta *mi;
682 + uint8_t macaddr[ETH_ALEN];
683 + char *args[3];
684 + int n_args;
685 + int ret = -EINVAL;
686 +
687 + spin_lock_bh(&mp->lock);
688 + if (!mp->manual)
689 + goto out;
690 +
691 + n_args = minstrel_ht_get_args(args, ARRAY_SIZE(args), arg_str, ";");
692 + if (!args[0])
693 + goto out;
694 +
695 + if (!mac_pton(args[0], macaddr))
696 + goto out;
697 +
698 + mi = minstrel_ht_api_get_sta(mp, macaddr);
699 + if (!mi) {
700 + ret = -ENOENT;
701 + goto out;
702 + }
703 +
704 + switch (cmd) {
705 + case STA_CMD_PROBE:
706 + ret = minstrel_ht_set_probe_rate(mi, args[1]);
707 + break;
708 + case STA_CMD_RATES:
709 + ret = minstrel_ht_set_rates(mp, mi, args[1], args[2]);
710 + break;
711 + }
712 +
713 +out:
714 + spin_unlock_bh(&mp->lock);
715 +
716 + return ret;
717 +}
718 +
719 +static ssize_t
720 +minstrel_ht_control_write(struct file *file, const char __user *userbuf,
721 + size_t count, loff_t *ppos)
722 +{
723 + struct minstrel_priv *mp = file->private_data;
724 + char *pos, *cur;
725 + char buf[64];
726 + size_t len = count;
727 + int err;
728 +
729 + if (len > sizeof(buf) - 1)
730 + return -EINVAL;
731 +
732 + if (copy_from_user(buf, userbuf, len))
733 + return -EFAULT;
734 +
735 + if (count > 0 && buf[len - 1] == '\n')
736 + len--;
737 +
738 + buf[len] = 0;
739 + if (!len)
740 + return count;
741 +
742 + pos = buf;
743 + cur = strsep(&pos, ";");
744 +
745 + err = 0;
746 + if (!strcmp(cur, "dump"))
747 + minstrel_ht_dump_stations(mp);
748 + else if (!strcmp(cur, "start"))
749 + minstrel_ht_api_start(mp, pos);
750 + else if (!strcmp(cur, "stop"))
751 + minstrel_ht_api_stop(mp);
752 + else if (!strcmp(cur, "manual"))
753 + minstrel_ht_api_set_manual(mp, true);
754 + else if (!strcmp(cur, "auto"))
755 + minstrel_ht_api_set_manual(mp, false);
756 + else if (!strcmp(cur, "rates"))
757 + err = minstrel_ht_api_sta_cmd(mp, STA_CMD_RATES, pos);
758 + else if (!strcmp(cur, "probe"))
759 + err = minstrel_ht_api_sta_cmd(mp, STA_CMD_PROBE, pos);
760 + else
761 + err = -EINVAL;
762 +
763 + if (err)
764 + return err;
765 +
766 + return count;
767 +}
768 +
769 +static const struct file_operations fops_control = {
770 + .open = simple_open,
771 + .llseek = generic_file_llseek,
772 + .write = minstrel_ht_control_write,
773 +};
774 +
775 +void minstrel_ht_sta_update(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
776 +{
777 + bool add = list_empty(&mi->list);
778 +
779 + spin_lock_bh(&mp->lock);
780 + if (add)
781 + list_add(&mi->list, &mp->stations);
782 + if (mp->monitor)
783 + minstrel_ht_dump_sta(mp, mi, add ? "add" : "update");
784 + spin_unlock_bh(&mp->lock);
785 +}
786 +
787 +void minstrel_ht_sta_remove(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
788 +{
789 + char info[64];
790 + int ofs = 0;
791 +
792 + spin_lock_bh(&mp->lock);
793 + list_del_init(&mi->list);
794 +
795 + if (!mp->monitor)
796 + goto out;
797 +
798 + ofs = scnprintf(info, sizeof(info), "%llx;sta;remove;%pM;;;\n",
799 + (unsigned long long)ktime_get_boottime_ns(),
800 + mi->sta->addr);
801 + relay_write(mp->relay_ev, info, ofs);
802 + relay_flush(mp->relay_ev);
803 +
804 +out:
805 + spin_unlock_bh(&mp->lock);
806 +}
807 +
808 +void __minstrel_ht_report_tx_status(struct minstrel_priv *mp,
809 + struct minstrel_ht_sta *mi,
810 + struct ieee80211_tx_info *info,
811 + u16 *rate_list,
812 + int n_rates)
813 +{
814 + char txs[64 + IEEE80211_TX_MAX_RATES * 8];
815 + int ofs = 0;
816 + int i;
817 +
818 + if (!n_rates)
819 + return;
820 +
821 + ofs += scnprintf(txs, sizeof(txs), "%llx;txs;%pM;%x;%x;%x;",
822 + (unsigned long long)ktime_get_boottime_ns(),
823 + mi->sta->addr,
824 + info->status.ampdu_len,
825 + info->status.ampdu_ack_len,
826 + !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE));
827 +
828 + ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, "%x",
829 + rate_list[0]);
830 + for (i = 1; i < n_rates; i++)
831 + ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, ",%x",
832 + rate_list[i]);
833 +
834 + ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, ";%x",
835 + info->status.rates[0].count);
836 + for (i = 1; i < n_rates; i++)
837 + ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, ",%x",
838 + info->status.rates[i].count);
839 + ofs += scnprintf(txs + ofs, sizeof(txs) - ofs, "\n");
840 + relay_write(mp->relay_ev, txs, ofs);
841 + relay_flush(mp->relay_ev);
842 +}
843 +
844 +void __minstrel_ht_report_rate_update(struct minstrel_priv *mp,
845 + struct minstrel_ht_sta *mi, u16 rate,
846 + struct minstrel_rate_stats *mrs)
847 +{
848 + char stat[100];
849 + int ofs;
850 + int tp;
851 +
852 + tp = minstrel_ht_get_tp_avg(mi, MI_RATE_GROUP(rate), MI_RATE_IDX(rate),
853 + mrs->prob_avg);
854 +
855 + ofs = scnprintf(stat, sizeof(stat),
856 + "%llx;stats;%pM;%x;%x;%x;%x;%x;%x;%x\n",
857 + (unsigned long long)ktime_get_boottime_ns(),
858 + mi->sta->addr, rate,
859 + MINSTREL_TRUNC(mrs->prob_avg * 1000), tp,
860 + mrs->last_success,
861 + mrs->last_attempts,
862 + mrs->succ_hist, mrs->att_hist);
863 +
864 + relay_write(mp->relay_ev, stat, ofs);
865 + relay_flush(mp->relay_ev);
866 +}
867 +
868 +void minstrel_ht_add_debugfs_api(struct ieee80211_hw *hw, void *priv,
869 + struct dentry *dir)
870 +{
871 + struct minstrel_priv *mp = priv;
872 +
873 + spin_lock_init(&mp->lock);
874 + INIT_LIST_HEAD(&mp->stations);
875 + mp->relay_ev = relay_open("api_event", dir, 256, 512, &relay_ev_cb,
876 + NULL);
877 + debugfs_create_devm_seqfile(&hw->wiphy->dev, "api_info",
878 + dir, minstrel_ht_read_api_info);
879 + debugfs_create_file("api_control", 0200, dir, mp, &fops_control);
880 +}
881 +
882 +void minstrel_ht_remove_debugfs_api(void *priv)
883 +{
884 + struct minstrel_priv *mp = priv;
885 +
886 + if (mp->relay_ev)
887 + relay_close(mp->relay_ev);
888 +}