Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/net80211/ieee80211_amrr.c
39475 views
1
/* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */
2
3
/*-
4
* Copyright (c) 2010 Rui Paulo <[email protected]>
5
* Copyright (c) 2006
6
* Damien Bergamini <[email protected]>
7
*
8
* Permission to use, copy, modify, and distribute this software for any
9
* purpose with or without fee is hereby granted, provided that the above
10
* copyright notice and this permission notice appear in all copies.
11
*
12
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
*/
20
21
#include <sys/cdefs.h>
22
/*-
23
* Naive implementation of the Adaptive Multi Rate Retry algorithm:
24
*
25
* "IEEE 802.11 Rate Adaptation: A Practical Approach"
26
* Mathieu Lacage, Hossein Manshaei, Thierry Turletti
27
* INRIA Sophia - Projet Planete
28
* http://www-sop.inria.fr/rapports/sophia/RR-5208.html
29
*/
30
#include "opt_wlan.h"
31
32
#include <sys/param.h>
33
#include <sys/kernel.h>
34
#include <sys/malloc.h>
35
#include <sys/module.h>
36
#include <sys/sbuf.h>
37
#include <sys/socket.h>
38
#include <sys/sysctl.h>
39
40
#include <net/if.h>
41
#include <net/if_var.h>
42
#include <net/if_media.h>
43
#include <net/ethernet.h>
44
45
#ifdef INET
46
#include <netinet/in.h>
47
#include <netinet/if_ether.h>
48
#endif
49
50
#include <net80211/ieee80211_var.h>
51
#include <net80211/ieee80211_ht.h>
52
#include <net80211/ieee80211_vht.h>
53
#include <net80211/ieee80211_amrr.h>
54
#include <net80211/ieee80211_ratectl.h>
55
56
#define is_success(amn) \
57
((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
58
#define is_failure(amn) \
59
((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
60
#define is_enough(amn) \
61
((amn)->amn_txcnt > 10)
62
63
static void amrr_setinterval(const struct ieee80211vap *, int);
64
static void amrr_init(struct ieee80211vap *);
65
static void amrr_deinit(struct ieee80211vap *);
66
static void amrr_node_init(struct ieee80211_node *);
67
static void amrr_node_deinit(struct ieee80211_node *);
68
static int amrr_update(struct ieee80211_amrr *,
69
struct ieee80211_amrr_node *, struct ieee80211_node *);
70
static int amrr_rate(struct ieee80211_node *, void *, uint32_t);
71
static void amrr_tx_complete(const struct ieee80211_node *,
72
const struct ieee80211_ratectl_tx_status *);
73
static void amrr_tx_update_cb(void *, struct ieee80211_node *);
74
static void amrr_tx_update(struct ieee80211vap *vap,
75
struct ieee80211_ratectl_tx_stats *);
76
static void amrr_sysctlattach(struct ieee80211vap *,
77
struct sysctl_ctx_list *, struct sysctl_oid *);
78
static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
79
80
/* number of references from net80211 layer */
81
static int nrefs = 0;
82
83
static const struct ieee80211_ratectl amrr = {
84
.ir_name = "amrr",
85
.ir_attach = NULL,
86
.ir_detach = NULL,
87
.ir_init = amrr_init,
88
.ir_deinit = amrr_deinit,
89
.ir_node_init = amrr_node_init,
90
.ir_node_deinit = amrr_node_deinit,
91
.ir_rate = amrr_rate,
92
.ir_tx_complete = amrr_tx_complete,
93
.ir_tx_update = amrr_tx_update,
94
.ir_setinterval = amrr_setinterval,
95
.ir_node_stats = amrr_node_stats,
96
};
97
IEEE80211_RATECTL_MODULE(amrr, 1);
98
IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
99
100
static void
101
amrr_setinterval(const struct ieee80211vap *vap, int msecs)
102
{
103
struct ieee80211_amrr *amrr = vap->iv_rs;
104
105
if (!amrr)
106
return;
107
108
if (msecs < 100)
109
msecs = 100;
110
amrr->amrr_interval = msecs_to_ticks(msecs);
111
}
112
113
static void
114
amrr_init(struct ieee80211vap *vap)
115
{
116
struct ieee80211_amrr *amrr;
117
118
KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
119
120
nrefs++; /* XXX locking */
121
amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
122
M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
123
if (amrr == NULL) {
124
net80211_vap_printf(vap, "couldn't alloc ratectl structure\n");
125
return;
126
}
127
amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
128
amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
129
amrr_setinterval(vap, 500 /* ms */);
130
amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
131
}
132
133
static void
134
amrr_deinit(struct ieee80211vap *vap)
135
{
136
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
137
IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
138
vap->iv_rs = NULL; /* guard */
139
nrefs--; /* XXX locking */
140
}
141
142
static void
143
amrr_node_init_vht(struct ieee80211_node *ni)
144
{
145
struct ieee80211_amrr_node *amn = ni->ni_rctls;
146
147
/* Default to VHT NSS 1 MCS 2; should be reliable! */
148
amn->amn_vht_mcs = 2;
149
amn->amn_vht_nss = 1;
150
ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
151
amn->amn_vht_mcs);
152
153
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
154
"AMRR: VHT: initial rate NSS %d MCS %d",
155
amn->amn_vht_nss,
156
amn->amn_vht_mcs);
157
}
158
159
static void
160
amrr_node_init_ht(struct ieee80211_node *ni)
161
{
162
const struct ieee80211_rateset *rs;
163
struct ieee80211_amrr_node *amn = ni->ni_rctls;
164
uint8_t rate; /* dot11rate */
165
166
rs = (struct ieee80211_rateset *) &ni->ni_htrates;
167
/* Initial rate - lowest */
168
rate = rs->rs_rates[0];
169
170
/* Pick something low that's likely to succeed */
171
for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
172
amn->amn_rix--) {
173
/* 11n - stop at MCS4 */
174
if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
175
break;
176
}
177
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
178
179
/* Ensure the MCS bit is set */
180
rate |= IEEE80211_RATE_MCS;
181
182
/* Assign initial rate from the rateset */
183
ieee80211_node_set_txrate_dot11rate(ni, rate);
184
185
/* XXX TODO: we really need a rate-to-string method */
186
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
187
"AMRR: nrates=%d, initial rate MCS %d",
188
rs->rs_nrates,
189
(rate & IEEE80211_RATE_VAL));
190
}
191
192
static void
193
amrr_node_init_legacy(struct ieee80211_node *ni)
194
{
195
const struct ieee80211_rateset *rs;
196
struct ieee80211_amrr_node *amn = ni->ni_rctls;
197
uint8_t rate; /* dot11rate */
198
199
rs = &ni->ni_rates;
200
/* Initial rate - lowest */
201
rate = rs->rs_rates[0];
202
203
/* Clear the basic rate flag if it's not 11n */
204
rate &= IEEE80211_RATE_VAL;
205
206
/* Pick something low that's likely to succeed */
207
for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
208
amn->amn_rix--) {
209
/* legacy - anything < 36mbit, stop searching */
210
if ((rs->rs_rates[amn->amn_rix] &
211
IEEE80211_RATE_VAL) <= 72)
212
break;
213
}
214
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
215
216
/* Assign initial rate from the rateset */
217
ieee80211_node_set_txrate_dot11rate(ni, rate);
218
219
/* XXX TODO: we really need a rate-to-string method */
220
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
221
"AMRR: nrates=%d, initial rate %d Mb",
222
rs->rs_nrates,
223
(rate & IEEE80211_RATE_VAL) / 2);
224
}
225
226
static void
227
amrr_node_init(struct ieee80211_node *ni)
228
{
229
struct ieee80211vap *vap = ni->ni_vap;
230
struct ieee80211_amrr *amrr = vap->iv_rs;
231
struct ieee80211_amrr_node *amn;
232
233
if (!amrr) {
234
net80211_vap_printf(vap,
235
"ratectl structure was not allocated, "
236
"per-node structure allocation skipped\n");
237
return;
238
}
239
240
if (ni->ni_rctls == NULL) {
241
ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
242
M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
243
if (amn == NULL) {
244
net80211_vap_printf(vap,
245
"couldn't alloc per-node ratectl structure\n");
246
return;
247
}
248
} else
249
amn = ni->ni_rctls;
250
251
/* Common state */
252
amn->amn_amrr = amrr;
253
amn->amn_success = 0;
254
amn->amn_recovery = 0;
255
amn->amn_txcnt = amn->amn_retrycnt = 0;
256
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
257
amn->amn_ticks = ticks;
258
259
/* Pick the right rateset */
260
if (ieee80211_vht_check_tx_vht(ni))
261
amrr_node_init_vht(ni);
262
else if (ieee80211_ht_check_tx_ht(ni))
263
amrr_node_init_ht(ni);
264
else
265
amrr_node_init_legacy(ni);
266
}
267
268
static void
269
amrr_node_deinit(struct ieee80211_node *ni)
270
{
271
IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
272
}
273
274
static void
275
amrr_update_vht_inc(struct ieee80211_node *ni)
276
{
277
struct ieee80211_amrr_node *amn = ni->ni_rctls;
278
uint8_t nss, mcs;
279
280
/*
281
* For now just keep looping over MCS to 9, then NSS up, checking if
282
* it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
283
* until we hit max. This at least tests the VHT MCS rates,
284
* but definitely is suboptimal (in the same way the 11n MCS selection
285
* is suboptimal.)
286
*/
287
nss = amn->amn_vht_nss;
288
mcs = amn->amn_vht_mcs;
289
290
while (nss <= 8 && mcs <= 9) {
291
/* Increment MCS 0..9, NSS 1..8 */
292
if (mcs == 9) {
293
mcs = 0;
294
nss++;
295
} else
296
mcs++;
297
if (nss > 8)
298
break;
299
300
if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
301
mcs)) {
302
amn->amn_vht_nss = nss;
303
amn->amn_vht_mcs = mcs;
304
break;
305
}
306
}
307
}
308
309
static void
310
amrr_update_vht_dec(struct ieee80211_node *ni)
311
{
312
struct ieee80211_amrr_node *amn = ni->ni_rctls;
313
uint8_t nss, mcs;
314
315
/*
316
* For now just keep looping over MCS 9 .. 0 then NSS down, checking if
317
* it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
318
* until we hit min. This at least tests the VHT MCS rates,
319
* but definitely is suboptimal (in the same way the 11n MCS selection
320
* is suboptimal.
321
*/
322
nss = amn->amn_vht_nss;
323
mcs = amn->amn_vht_mcs;
324
325
while (nss >= 1 && mcs >= 0) {
326
327
if (mcs == 0) {
328
mcs = 9;
329
nss--;
330
} else
331
mcs--;
332
if (nss < 1)
333
break;
334
335
if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
336
mcs)) {
337
amn->amn_vht_nss = nss;
338
amn->amn_vht_mcs = mcs;
339
break;
340
}
341
}
342
}
343
344
/*
345
* A placeholder / temporary hack VHT rate control.
346
*
347
* Use the available MCS rates at the current node bandwidth
348
* and configured / negotiated MCS rates.
349
*/
350
static int
351
amrr_update_vht(struct ieee80211_node *ni)
352
{
353
struct ieee80211_amrr_node *amn = ni->ni_rctls;
354
struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
355
356
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
357
"AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d",
358
amn->amn_vht_nss, amn->amn_vht_mcs, amn->amn_txcnt,
359
amn->amn_retrycnt);
360
361
if (is_success(amn)) {
362
amn->amn_success++;
363
if (amn->amn_success >= amn->amn_success_threshold) {
364
amn->amn_recovery = 1;
365
amn->amn_success = 0;
366
367
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
368
"AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)",
369
amn->amn_txcnt, amn->amn_retrycnt);
370
371
amrr_update_vht_inc(ni);
372
} else {
373
amn->amn_recovery = 0;
374
}
375
} else if (is_failure(amn)) {
376
amn->amn_success = 0;
377
378
if (amn->amn_recovery) {
379
amn->amn_success_threshold *= 2;
380
if (amn->amn_success_threshold >
381
amrr->amrr_max_success_threshold)
382
amn->amn_success_threshold =
383
amrr->amrr_max_success_threshold;
384
} else {
385
amn->amn_success_threshold =
386
amrr->amrr_min_success_threshold;
387
}
388
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
389
"AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)",
390
amn->amn_txcnt, amn->amn_retrycnt);
391
392
amrr_update_vht_dec(ni);
393
394
amn->amn_recovery = 0;
395
}
396
397
/* Reset counters */
398
amn->amn_txcnt = 0;
399
amn->amn_retrycnt = 0;
400
401
/* Return 0, not useful anymore */
402
return (0);
403
}
404
405
static int
406
amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
407
struct ieee80211_node *ni)
408
{
409
int rix = amn->amn_rix;
410
const struct ieee80211_rateset *rs;
411
412
rs = (struct ieee80211_rateset *)&ni->ni_htrates;
413
414
/* XXX TODO: we really need a rate-to-string method */
415
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
416
"AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d",
417
rs->rs_rates[rix] & IEEE80211_RATE_VAL,
418
amn->amn_txcnt,
419
amn->amn_retrycnt);
420
421
/*
422
* XXX This is totally bogus for 11n, as although high MCS
423
* rates for each stream may be failing, the next stream
424
* should be checked.
425
*
426
* Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
427
* MCS23, we should skip 6/7 and try 8 onwards.
428
*/
429
if (is_success(amn)) {
430
amn->amn_success++;
431
if (amn->amn_success >= amn->amn_success_threshold &&
432
rix + 1 < rs->rs_nrates) {
433
amn->amn_recovery = 1;
434
amn->amn_success = 0;
435
rix++;
436
/* XXX TODO: we really need a rate-to-string method */
437
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
438
"AMRR increasing rate MCS %d "
439
"(txcnt=%d retrycnt=%d)",
440
rs->rs_rates[rix] & IEEE80211_RATE_VAL,
441
amn->amn_txcnt, amn->amn_retrycnt);
442
} else {
443
amn->amn_recovery = 0;
444
}
445
} else if (is_failure(amn)) {
446
amn->amn_success = 0;
447
if (rix > 0) {
448
if (amn->amn_recovery) {
449
amn->amn_success_threshold *= 2;
450
if (amn->amn_success_threshold >
451
amrr->amrr_max_success_threshold)
452
amn->amn_success_threshold =
453
amrr->amrr_max_success_threshold;
454
} else {
455
amn->amn_success_threshold =
456
amrr->amrr_min_success_threshold;
457
}
458
rix--;
459
/* XXX TODO: we really need a rate-to-string method */
460
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
461
"AMRR decreasing rate MCS %d "
462
"(txcnt=%d retrycnt=%d)",
463
rs->rs_rates[rix] & IEEE80211_RATE_VAL,
464
amn->amn_txcnt, amn->amn_retrycnt);
465
}
466
amn->amn_recovery = 0;
467
}
468
469
return (rix);
470
}
471
472
static int
473
amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
474
struct ieee80211_node *ni)
475
{
476
int rix = amn->amn_rix;
477
const struct ieee80211_rateset *rs;
478
479
rs = &ni->ni_rates;
480
481
/* XXX TODO: we really need a rate-to-string method */
482
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
483
"AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d",
484
(rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
485
amn->amn_txcnt,
486
amn->amn_retrycnt);
487
488
if (is_success(amn)) {
489
amn->amn_success++;
490
if (amn->amn_success >= amn->amn_success_threshold &&
491
rix + 1 < rs->rs_nrates) {
492
amn->amn_recovery = 1;
493
amn->amn_success = 0;
494
rix++;
495
/* XXX TODO: we really need a rate-to-string method */
496
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
497
"AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)",
498
(rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
499
amn->amn_txcnt, amn->amn_retrycnt);
500
} else {
501
amn->amn_recovery = 0;
502
}
503
} else if (is_failure(amn)) {
504
amn->amn_success = 0;
505
if (rix > 0) {
506
if (amn->amn_recovery) {
507
amn->amn_success_threshold *= 2;
508
if (amn->amn_success_threshold >
509
amrr->amrr_max_success_threshold)
510
amn->amn_success_threshold =
511
amrr->amrr_max_success_threshold;
512
} else {
513
amn->amn_success_threshold =
514
amrr->amrr_min_success_threshold;
515
}
516
rix--;
517
/* XXX TODO: we really need a rate-to-string method */
518
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
519
"AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)",
520
(rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
521
amn->amn_txcnt, amn->amn_retrycnt);
522
}
523
amn->amn_recovery = 0;
524
}
525
526
return (rix);
527
}
528
529
static int
530
amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
531
struct ieee80211_node *ni)
532
{
533
int rix;
534
535
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
536
537
/* Pick the right rateset */
538
if (ieee80211_vht_check_tx_vht(ni))
539
rix = amrr_update_vht(ni);
540
else if (ieee80211_ht_check_tx_ht(ni))
541
rix = amrr_update_ht(amrr, amn, ni);
542
else
543
rix = amrr_update_legacy(amrr, amn, ni);
544
545
/* reset counters */
546
amn->amn_txcnt = 0;
547
amn->amn_retrycnt = 0;
548
549
return (rix);
550
}
551
552
static int
553
amrr_rate_vht(struct ieee80211_node *ni)
554
{
555
struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
556
struct ieee80211_amrr_node *amn = ni->ni_rctls;
557
558
if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval)
559
amrr_update_vht(ni);
560
561
ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
562
amn->amn_vht_mcs);
563
564
/* Note: There's no vht rs_rates, and the API doesn't use it anymore */
565
return (0);
566
}
567
568
/*
569
* Return the rate index to use in sending a data frame.
570
* Update our internal state if it's been long enough.
571
* If the rate changes we also update ni_txrate to match.
572
*/
573
static int
574
amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
575
{
576
struct ieee80211_amrr_node *amn = ni->ni_rctls;
577
struct ieee80211_amrr *amrr;
578
const struct ieee80211_rateset *rs = NULL;
579
int rix;
580
581
/* XXX should return -1 here, but drivers may not expect this... */
582
if (!amn)
583
{
584
ieee80211_node_set_txrate_dot11rate(ni,
585
ni->ni_rates.rs_rates[0]);
586
return 0;
587
}
588
589
if (ieee80211_vht_check_tx_vht(ni))
590
return (amrr_rate_vht(ni));
591
592
/* Pick the right rateset */
593
if (ieee80211_ht_check_tx_ht(ni)) {
594
/* XXX ew */
595
rs = (struct ieee80211_rateset *) &ni->ni_htrates;
596
} else {
597
rs = &ni->ni_rates;
598
}
599
600
amrr = amn->amn_amrr;
601
if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
602
rix = amrr_update(amrr, amn, ni);
603
if (rix != amn->amn_rix) {
604
uint8_t dot11Rate;
605
/* update public rate */
606
dot11Rate = rs->rs_rates[rix];
607
/* XXX strip basic rate flag from txrate, if non-11n */
608
if (ieee80211_ht_check_tx_ht(ni))
609
dot11Rate |= IEEE80211_RATE_MCS;
610
else
611
dot11Rate &= IEEE80211_RATE_VAL;
612
ieee80211_node_set_txrate_dot11rate(ni, dot11Rate);
613
614
amn->amn_rix = rix;
615
}
616
amn->amn_ticks = ticks;
617
} else
618
rix = amn->amn_rix;
619
return rix;
620
}
621
622
/*
623
* Update statistics with tx complete status. Ok is non-zero
624
* if the packet is known to be ACK'd. Retries has the number
625
* retransmissions (i.e. xmit attempts - 1).
626
*/
627
static void
628
amrr_tx_complete(const struct ieee80211_node *ni,
629
const struct ieee80211_ratectl_tx_status *status)
630
{
631
struct ieee80211_amrr_node *amn = ni->ni_rctls;
632
int retries;
633
634
if (!amn)
635
return;
636
637
retries = 0;
638
if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
639
retries = status->long_retries;
640
641
amn->amn_txcnt++;
642
if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
643
amn->amn_success++;
644
amn->amn_retrycnt += retries;
645
}
646
647
static void
648
amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
649
{
650
struct ieee80211_ratectl_tx_stats *stats = arg;
651
struct ieee80211_amrr_node *amn = ni->ni_rctls;
652
int txcnt, success, retrycnt;
653
654
if (!amn)
655
return;
656
657
txcnt = stats->nframes;
658
success = stats->nsuccess;
659
retrycnt = 0;
660
if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
661
retrycnt = stats->nretries;
662
663
amn->amn_txcnt += txcnt;
664
amn->amn_success += success;
665
amn->amn_retrycnt += retrycnt;
666
}
667
668
/*
669
* Set tx count/retry statistics explicitly. Intended for
670
* drivers that poll the device for statistics maintained
671
* in the device.
672
*/
673
static void
674
amrr_tx_update(struct ieee80211vap *vap,
675
struct ieee80211_ratectl_tx_stats *stats)
676
{
677
678
if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
679
amrr_tx_update_cb(stats, stats->ni);
680
else {
681
ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
682
amrr_tx_update_cb, stats);
683
}
684
}
685
686
static int
687
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
688
{
689
struct ieee80211vap *vap = arg1;
690
struct ieee80211_amrr *amrr = vap->iv_rs;
691
int msecs, error;
692
693
if (!amrr)
694
return ENOMEM;
695
696
msecs = ticks_to_msecs(amrr->amrr_interval);
697
error = sysctl_handle_int(oidp, &msecs, 0, req);
698
if (error || !req->newptr)
699
return error;
700
amrr_setinterval(vap, msecs);
701
return 0;
702
}
703
704
static void
705
amrr_sysctlattach(struct ieee80211vap *vap,
706
struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
707
{
708
struct ieee80211_amrr *amrr = vap->iv_rs;
709
710
if (!amrr)
711
return;
712
713
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
714
"amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
715
vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
716
/* XXX bounds check values */
717
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
718
"amrr_max_sucess_threshold", CTLFLAG_RW,
719
&amrr->amrr_max_success_threshold, 0, "");
720
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
721
"amrr_min_sucess_threshold", CTLFLAG_RW,
722
&amrr->amrr_min_success_threshold, 0, "");
723
}
724
725
static void
726
amrr_print_node_rate(struct ieee80211_amrr_node *amn,
727
struct ieee80211_node *ni, struct sbuf *s)
728
{
729
int rate;
730
struct ieee80211_rateset *rs;
731
732
if (ieee80211_ht_check_tx_ht(ni)) {
733
rs = (struct ieee80211_rateset *) &ni->ni_htrates;
734
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
735
sbuf_printf(s, "rate: MCS %d\n", rate);
736
} else {
737
rs = &ni->ni_rates;
738
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
739
sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
740
}
741
}
742
743
static void
744
amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
745
{
746
struct ieee80211_amrr_node *amn = ni->ni_rctls;
747
748
/* XXX TODO: check locking? */
749
750
if (!amn)
751
return;
752
753
amrr_print_node_rate(amn, ni, s);
754
sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
755
sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
756
sbuf_printf(s, "success: %u\n", amn->amn_success);
757
sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
758
sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
759
sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
760
}
761
762