1 /* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> 5 * Copyright (c) 2006 6 * Damien Bergamini <damien.bergamini@free.fr> 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