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 if_printf(vap->iv_ifp, "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 if_printf(vap->iv_ifp, "ratectl structure was not allocated, " 235 "per-node structure allocation skipped\n"); 236 return; 237 } 238 239 if (ni->ni_rctls == NULL) { 240 ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), 241 M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 242 if (amn == NULL) { 243 if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " 244 "structure\n"); 245 return; 246 } 247 } else 248 amn = ni->ni_rctls; 249 250 /* Common state */ 251 amn->amn_amrr = amrr; 252 amn->amn_success = 0; 253 amn->amn_recovery = 0; 254 amn->amn_txcnt = amn->amn_retrycnt = 0; 255 amn->amn_success_threshold = amrr->amrr_min_success_threshold; 256 amn->amn_ticks = ticks; 257 258 /* Pick the right rateset */ 259 if (ieee80211_vht_check_tx_vht(ni)) 260 amrr_node_init_vht(ni); 261 else if (ieee80211_ht_check_tx_ht(ni)) 262 amrr_node_init_ht(ni); 263 else 264 amrr_node_init_legacy(ni); 265 } 266 267 static void 268 amrr_node_deinit(struct ieee80211_node *ni) 269 { 270 IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); 271 } 272 273 static void 274 amrr_update_vht_inc(struct ieee80211_node *ni) 275 { 276 struct ieee80211_amrr_node *amn = ni->ni_rctls; 277 uint8_t nss, mcs; 278 279 /* 280 * For now just keep looping over MCS to 9, then NSS up, checking if 281 * it's valid via ieee80211_vht_node_check_tx_valid_mcs(), 282 * until we hit max. This at least tests the VHT MCS rates, 283 * but definitely is suboptimal (in the same way the 11n MCS selection 284 * is suboptimal.) 285 */ 286 nss = amn->amn_vht_nss; 287 mcs = amn->amn_vht_mcs; 288 289 while (nss <= 8 && mcs <= 9) { 290 /* Increment MCS 0..9, NSS 1..8 */ 291 if (mcs == 9) { 292 mcs = 0; 293 nss++; 294 } else 295 mcs++; 296 if (nss > 8) 297 break; 298 299 if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss, 300 mcs)) { 301 amn->amn_vht_nss = nss; 302 amn->amn_vht_mcs = mcs; 303 break; 304 } 305 } 306 } 307 308 static void 309 amrr_update_vht_dec(struct ieee80211_node *ni) 310 { 311 struct ieee80211_amrr_node *amn = ni->ni_rctls; 312 uint8_t nss, mcs; 313 314 /* 315 * For now just keep looping over MCS 9 .. 0 then NSS down, checking if 316 * it's valid via ieee80211_vht_node_check_tx_valid_mcs(), 317 * until we hit min. This at least tests the VHT MCS rates, 318 * but definitely is suboptimal (in the same way the 11n MCS selection 319 * is suboptimal. 320 */ 321 nss = amn->amn_vht_nss; 322 mcs = amn->amn_vht_mcs; 323 324 while (nss >= 1 && mcs >= 0) { 325 326 if (mcs == 0) { 327 mcs = 9; 328 nss--; 329 } else 330 mcs--; 331 if (nss < 1) 332 break; 333 334 if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss, 335 mcs)) { 336 amn->amn_vht_nss = nss; 337 amn->amn_vht_mcs = mcs; 338 break; 339 } 340 } 341 } 342 343 /* 344 * A placeholder / temporary hack VHT rate control. 345 * 346 * Use the available MCS rates at the current node bandwidth 347 * and configured / negotiated MCS rates. 348 */ 349 static int 350 amrr_update_vht(struct ieee80211_node *ni) 351 { 352 struct ieee80211_amrr_node *amn = ni->ni_rctls; 353 struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs; 354 355 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 356 "AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d", 357 amn->amn_vht_nss, amn->amn_vht_mcs, amn->amn_txcnt, 358 amn->amn_retrycnt); 359 360 if (is_success(amn)) { 361 amn->amn_success++; 362 if (amn->amn_success >= amn->amn_success_threshold) { 363 amn->amn_recovery = 1; 364 amn->amn_success = 0; 365 366 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 367 "AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)", 368 amn->amn_txcnt, amn->amn_retrycnt); 369 370 amrr_update_vht_inc(ni); 371 } else { 372 amn->amn_recovery = 0; 373 } 374 } else if (is_failure(amn)) { 375 amn->amn_success = 0; 376 377 if (amn->amn_recovery) { 378 amn->amn_success_threshold *= 2; 379 if (amn->amn_success_threshold > 380 amrr->amrr_max_success_threshold) 381 amn->amn_success_threshold = 382 amrr->amrr_max_success_threshold; 383 } else { 384 amn->amn_success_threshold = 385 amrr->amrr_min_success_threshold; 386 } 387 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 388 "AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)", 389 amn->amn_txcnt, amn->amn_retrycnt); 390 391 amrr_update_vht_dec(ni); 392 393 amn->amn_recovery = 0; 394 } 395 396 /* Reset counters */ 397 amn->amn_txcnt = 0; 398 amn->amn_retrycnt = 0; 399 400 /* Return 0, not useful anymore */ 401 return (0); 402 } 403 404 static int 405 amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 406 struct ieee80211_node *ni) 407 { 408 int rix = amn->amn_rix; 409 const struct ieee80211_rateset *rs; 410 411 rs = (struct ieee80211_rateset *)&ni->ni_htrates; 412 413 /* XXX TODO: we really need a rate-to-string method */ 414 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 415 "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d", 416 rs->rs_rates[rix] & IEEE80211_RATE_VAL, 417 amn->amn_txcnt, 418 amn->amn_retrycnt); 419 420 /* 421 * XXX This is totally bogus for 11n, as although high MCS 422 * rates for each stream may be failing, the next stream 423 * should be checked. 424 * 425 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to 426 * MCS23, we should skip 6/7 and try 8 onwards. 427 */ 428 if (is_success(amn)) { 429 amn->amn_success++; 430 if (amn->amn_success >= amn->amn_success_threshold && 431 rix + 1 < rs->rs_nrates) { 432 amn->amn_recovery = 1; 433 amn->amn_success = 0; 434 rix++; 435 /* XXX TODO: we really need a rate-to-string method */ 436 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 437 "AMRR increasing rate MCS %d " 438 "(txcnt=%d retrycnt=%d)", 439 rs->rs_rates[rix] & IEEE80211_RATE_VAL, 440 amn->amn_txcnt, amn->amn_retrycnt); 441 } else { 442 amn->amn_recovery = 0; 443 } 444 } else if (is_failure(amn)) { 445 amn->amn_success = 0; 446 if (rix > 0) { 447 if (amn->amn_recovery) { 448 amn->amn_success_threshold *= 2; 449 if (amn->amn_success_threshold > 450 amrr->amrr_max_success_threshold) 451 amn->amn_success_threshold = 452 amrr->amrr_max_success_threshold; 453 } else { 454 amn->amn_success_threshold = 455 amrr->amrr_min_success_threshold; 456 } 457 rix--; 458 /* XXX TODO: we really need a rate-to-string method */ 459 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 460 "AMRR decreasing rate MCS %d " 461 "(txcnt=%d retrycnt=%d)", 462 rs->rs_rates[rix] & IEEE80211_RATE_VAL, 463 amn->amn_txcnt, amn->amn_retrycnt); 464 } 465 amn->amn_recovery = 0; 466 } 467 468 return (rix); 469 } 470 471 static int 472 amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 473 struct ieee80211_node *ni) 474 { 475 int rix = amn->amn_rix; 476 const struct ieee80211_rateset *rs; 477 478 rs = &ni->ni_rates; 479 480 /* XXX TODO: we really need a rate-to-string method */ 481 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 482 "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d", 483 (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 484 amn->amn_txcnt, 485 amn->amn_retrycnt); 486 487 if (is_success(amn)) { 488 amn->amn_success++; 489 if (amn->amn_success >= amn->amn_success_threshold && 490 rix + 1 < rs->rs_nrates) { 491 amn->amn_recovery = 1; 492 amn->amn_success = 0; 493 rix++; 494 /* XXX TODO: we really need a rate-to-string method */ 495 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 496 "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)", 497 (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 498 amn->amn_txcnt, amn->amn_retrycnt); 499 } else { 500 amn->amn_recovery = 0; 501 } 502 } else if (is_failure(amn)) { 503 amn->amn_success = 0; 504 if (rix > 0) { 505 if (amn->amn_recovery) { 506 amn->amn_success_threshold *= 2; 507 if (amn->amn_success_threshold > 508 amrr->amrr_max_success_threshold) 509 amn->amn_success_threshold = 510 amrr->amrr_max_success_threshold; 511 } else { 512 amn->amn_success_threshold = 513 amrr->amrr_min_success_threshold; 514 } 515 rix--; 516 /* XXX TODO: we really need a rate-to-string method */ 517 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 518 "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)", 519 (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 520 amn->amn_txcnt, amn->amn_retrycnt); 521 } 522 amn->amn_recovery = 0; 523 } 524 525 return (rix); 526 } 527 528 static int 529 amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 530 struct ieee80211_node *ni) 531 { 532 int rix; 533 534 KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); 535 536 /* Pick the right rateset */ 537 if (ieee80211_vht_check_tx_vht(ni)) 538 rix = amrr_update_vht(ni); 539 else if (ieee80211_ht_check_tx_ht(ni)) 540 rix = amrr_update_ht(amrr, amn, ni); 541 else 542 rix = amrr_update_legacy(amrr, amn, ni); 543 544 /* reset counters */ 545 amn->amn_txcnt = 0; 546 amn->amn_retrycnt = 0; 547 548 return (rix); 549 } 550 551 static int 552 amrr_rate_vht(struct ieee80211_node *ni) 553 { 554 struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs; 555 struct ieee80211_amrr_node *amn = ni->ni_rctls; 556 557 if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) 558 amrr_update_vht(ni); 559 560 ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss, 561 amn->amn_vht_mcs); 562 563 /* Note: There's no vht rs_rates, and the API doesn't use it anymore */ 564 return (0); 565 } 566 567 /* 568 * Return the rate index to use in sending a data frame. 569 * Update our internal state if it's been long enough. 570 * If the rate changes we also update ni_txrate to match. 571 */ 572 static int 573 amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) 574 { 575 struct ieee80211_amrr_node *amn = ni->ni_rctls; 576 struct ieee80211_amrr *amrr; 577 const struct ieee80211_rateset *rs = NULL; 578 int rix; 579 580 /* XXX should return -1 here, but drivers may not expect this... */ 581 if (!amn) 582 { 583 ieee80211_node_set_txrate_dot11rate(ni, 584 ni->ni_rates.rs_rates[0]); 585 return 0; 586 } 587 588 if (ieee80211_vht_check_tx_vht(ni)) 589 return (amrr_rate_vht(ni)); 590 591 /* Pick the right rateset */ 592 if (ieee80211_ht_check_tx_ht(ni)) { 593 /* XXX ew */ 594 rs = (struct ieee80211_rateset *) &ni->ni_htrates; 595 } else { 596 rs = &ni->ni_rates; 597 } 598 599 amrr = amn->amn_amrr; 600 if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { 601 rix = amrr_update(amrr, amn, ni); 602 if (rix != amn->amn_rix) { 603 uint8_t dot11Rate; 604 /* update public rate */ 605 dot11Rate = rs->rs_rates[rix]; 606 /* XXX strip basic rate flag from txrate, if non-11n */ 607 if (ieee80211_ht_check_tx_ht(ni)) 608 dot11Rate |= IEEE80211_RATE_MCS; 609 else 610 dot11Rate &= IEEE80211_RATE_VAL; 611 ieee80211_node_set_txrate_dot11rate(ni, dot11Rate); 612 613 amn->amn_rix = rix; 614 } 615 amn->amn_ticks = ticks; 616 } else 617 rix = amn->amn_rix; 618 return rix; 619 } 620 621 /* 622 * Update statistics with tx complete status. Ok is non-zero 623 * if the packet is known to be ACK'd. Retries has the number 624 * retransmissions (i.e. xmit attempts - 1). 625 */ 626 static void 627 amrr_tx_complete(const struct ieee80211_node *ni, 628 const struct ieee80211_ratectl_tx_status *status) 629 { 630 struct ieee80211_amrr_node *amn = ni->ni_rctls; 631 int retries; 632 633 if (!amn) 634 return; 635 636 retries = 0; 637 if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY) 638 retries = status->long_retries; 639 640 amn->amn_txcnt++; 641 if (status->status == IEEE80211_RATECTL_TX_SUCCESS) 642 amn->amn_success++; 643 amn->amn_retrycnt += retries; 644 } 645 646 static void 647 amrr_tx_update_cb(void *arg, struct ieee80211_node *ni) 648 { 649 struct ieee80211_ratectl_tx_stats *stats = arg; 650 struct ieee80211_amrr_node *amn = ni->ni_rctls; 651 int txcnt, success, retrycnt; 652 653 if (!amn) 654 return; 655 656 txcnt = stats->nframes; 657 success = stats->nsuccess; 658 retrycnt = 0; 659 if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES) 660 retrycnt = stats->nretries; 661 662 amn->amn_txcnt += txcnt; 663 amn->amn_success += success; 664 amn->amn_retrycnt += retrycnt; 665 } 666 667 /* 668 * Set tx count/retry statistics explicitly. Intended for 669 * drivers that poll the device for statistics maintained 670 * in the device. 671 */ 672 static void 673 amrr_tx_update(struct ieee80211vap *vap, 674 struct ieee80211_ratectl_tx_stats *stats) 675 { 676 677 if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE) 678 amrr_tx_update_cb(stats, stats->ni); 679 else { 680 ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap, 681 amrr_tx_update_cb, stats); 682 } 683 } 684 685 static int 686 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) 687 { 688 struct ieee80211vap *vap = arg1; 689 struct ieee80211_amrr *amrr = vap->iv_rs; 690 int msecs, error; 691 692 if (!amrr) 693 return ENOMEM; 694 695 msecs = ticks_to_msecs(amrr->amrr_interval); 696 error = sysctl_handle_int(oidp, &msecs, 0, req); 697 if (error || !req->newptr) 698 return error; 699 amrr_setinterval(vap, msecs); 700 return 0; 701 } 702 703 static void 704 amrr_sysctlattach(struct ieee80211vap *vap, 705 struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) 706 { 707 struct ieee80211_amrr *amrr = vap->iv_rs; 708 709 if (!amrr) 710 return; 711 712 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 713 "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 714 vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); 715 /* XXX bounds check values */ 716 SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 717 "amrr_max_sucess_threshold", CTLFLAG_RW, 718 &amrr->amrr_max_success_threshold, 0, ""); 719 SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 720 "amrr_min_sucess_threshold", CTLFLAG_RW, 721 &amrr->amrr_min_success_threshold, 0, ""); 722 } 723 724 static void 725 amrr_print_node_rate(struct ieee80211_amrr_node *amn, 726 struct ieee80211_node *ni, struct sbuf *s) 727 { 728 int rate; 729 struct ieee80211_rateset *rs; 730 731 if (ieee80211_ht_check_tx_ht(ni)) { 732 rs = (struct ieee80211_rateset *) &ni->ni_htrates; 733 rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 734 sbuf_printf(s, "rate: MCS %d\n", rate); 735 } else { 736 rs = &ni->ni_rates; 737 rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 738 sbuf_printf(s, "rate: %d Mbit\n", rate / 2); 739 } 740 } 741 742 static void 743 amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) 744 { 745 struct ieee80211_amrr_node *amn = ni->ni_rctls; 746 747 /* XXX TODO: check locking? */ 748 749 if (!amn) 750 return; 751 752 amrr_print_node_rate(amn, ni, s); 753 sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); 754 sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); 755 sbuf_printf(s, "success: %u\n", amn->amn_success); 756 sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); 757 sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); 758 sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); 759 } 760