174828f25SSam Leffler /* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */ 274828f25SSam Leffler 374828f25SSam Leffler /*- 4b6108616SRui Paulo * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> 574828f25SSam Leffler * Copyright (c) 2006 674828f25SSam Leffler * Damien Bergamini <damien.bergamini@free.fr> 774828f25SSam Leffler * 874828f25SSam Leffler * Permission to use, copy, modify, and distribute this software for any 974828f25SSam Leffler * purpose with or without fee is hereby granted, provided that the above 1074828f25SSam Leffler * copyright notice and this permission notice appear in all copies. 1174828f25SSam Leffler * 1274828f25SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1374828f25SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1474828f25SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1574828f25SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1674828f25SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1774828f25SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1874828f25SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1974828f25SSam Leffler */ 2074828f25SSam Leffler 2174828f25SSam Leffler #include <sys/cdefs.h> 2274828f25SSam Leffler /*- 2374828f25SSam Leffler * Naive implementation of the Adaptive Multi Rate Retry algorithm: 2474828f25SSam Leffler * 2574828f25SSam Leffler * "IEEE 802.11 Rate Adaptation: A Practical Approach" 2674828f25SSam Leffler * Mathieu Lacage, Hossein Manshaei, Thierry Turletti 2774828f25SSam Leffler * INRIA Sophia - Projet Planete 2874828f25SSam Leffler * http://www-sop.inria.fr/rapports/sophia/RR-5208.html 2974828f25SSam Leffler */ 30b032f27cSSam Leffler #include "opt_wlan.h" 31b032f27cSSam Leffler 3274828f25SSam Leffler #include <sys/param.h> 3374828f25SSam Leffler #include <sys/kernel.h> 348ec07310SGleb Smirnoff #include <sys/malloc.h> 3574828f25SSam Leffler #include <sys/module.h> 3673931706SAdrian Chadd #include <sys/sbuf.h> 3774828f25SSam Leffler #include <sys/socket.h> 3874828f25SSam Leffler #include <sys/sysctl.h> 3974828f25SSam Leffler 4074828f25SSam Leffler #include <net/if.h> 4176039bc8SGleb Smirnoff #include <net/if_var.h> 4274828f25SSam Leffler #include <net/if_media.h> 43c3322cb9SGleb Smirnoff #include <net/ethernet.h> 4474828f25SSam Leffler 4574828f25SSam Leffler #ifdef INET 4674828f25SSam Leffler #include <netinet/in.h> 4774828f25SSam Leffler #include <netinet/if_ether.h> 4874828f25SSam Leffler #endif 4974828f25SSam Leffler 5074828f25SSam Leffler #include <net80211/ieee80211_var.h> 51f8bf74f2SAdrian Chadd #include <net80211/ieee80211_ht.h> 52*4b2c7dfeSAdrian Chadd #include <net80211/ieee80211_vht.h> 5374828f25SSam Leffler #include <net80211/ieee80211_amrr.h> 54b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h> 5574828f25SSam Leffler 5674828f25SSam Leffler #define is_success(amn) \ 5774828f25SSam Leffler ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) 5874828f25SSam Leffler #define is_failure(amn) \ 5974828f25SSam Leffler ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) 6074828f25SSam Leffler #define is_enough(amn) \ 6174828f25SSam Leffler ((amn)->amn_txcnt > 10) 62b032f27cSSam Leffler 63b6108616SRui Paulo static void amrr_setinterval(const struct ieee80211vap *, int); 64b6108616SRui Paulo static void amrr_init(struct ieee80211vap *); 65b6108616SRui Paulo static void amrr_deinit(struct ieee80211vap *); 66b6108616SRui Paulo static void amrr_node_init(struct ieee80211_node *); 67b6108616SRui Paulo static void amrr_node_deinit(struct ieee80211_node *); 68b6108616SRui Paulo static int amrr_update(struct ieee80211_amrr *, 69b6108616SRui Paulo struct ieee80211_amrr_node *, struct ieee80211_node *); 70b6108616SRui Paulo static int amrr_rate(struct ieee80211_node *, void *, uint32_t); 71f6930becSAndriy Voskoboinyk static void amrr_tx_complete(const struct ieee80211_node *, 72f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *); 73f6930becSAndriy Voskoboinyk static void amrr_tx_update_cb(void *, struct ieee80211_node *); 74f6930becSAndriy Voskoboinyk static void amrr_tx_update(struct ieee80211vap *vap, 75f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *); 76b6108616SRui Paulo static void amrr_sysctlattach(struct ieee80211vap *, 77b6108616SRui Paulo struct sysctl_ctx_list *, struct sysctl_oid *); 7873931706SAdrian Chadd static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s); 79b032f27cSSam Leffler 80b032f27cSSam Leffler /* number of references from net80211 layer */ 81b032f27cSSam Leffler static int nrefs = 0; 82b032f27cSSam Leffler 83b6108616SRui Paulo static const struct ieee80211_ratectl amrr = { 84b6108616SRui Paulo .ir_name = "amrr", 85b6108616SRui Paulo .ir_attach = NULL, 86b6108616SRui Paulo .ir_detach = NULL, 87b6108616SRui Paulo .ir_init = amrr_init, 88b6108616SRui Paulo .ir_deinit = amrr_deinit, 89b6108616SRui Paulo .ir_node_init = amrr_node_init, 90b6108616SRui Paulo .ir_node_deinit = amrr_node_deinit, 91b6108616SRui Paulo .ir_rate = amrr_rate, 92b6108616SRui Paulo .ir_tx_complete = amrr_tx_complete, 93b6108616SRui Paulo .ir_tx_update = amrr_tx_update, 94b6108616SRui Paulo .ir_setinterval = amrr_setinterval, 9573931706SAdrian Chadd .ir_node_stats = amrr_node_stats, 96b6108616SRui Paulo }; 97b6108616SRui Paulo IEEE80211_RATECTL_MODULE(amrr, 1); 98b6108616SRui Paulo IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); 99b6108616SRui Paulo 100b6108616SRui Paulo static void 101b6108616SRui Paulo amrr_setinterval(const struct ieee80211vap *vap, int msecs) 102b032f27cSSam Leffler { 103b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 104b032f27cSSam Leffler 1054367c2d1SAndriy Voskoboinyk if (!amrr) 1064367c2d1SAndriy Voskoboinyk return; 1074367c2d1SAndriy Voskoboinyk 108b032f27cSSam Leffler if (msecs < 100) 109b032f27cSSam Leffler msecs = 100; 1109df9e936SAndriy Voskoboinyk amrr->amrr_interval = msecs_to_ticks(msecs); 111b032f27cSSam Leffler } 11274828f25SSam Leffler 113b6108616SRui Paulo static void 114b6108616SRui Paulo amrr_init(struct ieee80211vap *vap) 11574828f25SSam Leffler { 116b6108616SRui Paulo struct ieee80211_amrr *amrr; 117b032f27cSSam Leffler 118b6108616SRui Paulo KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); 119b6108616SRui Paulo 120810490a0SAndriy Voskoboinyk nrefs++; /* XXX locking */ 121b9b53389SAdrian Chadd amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr), 122b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 123645fe19aSRui Paulo if (amrr == NULL) { 124645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); 125645fe19aSRui Paulo return; 126645fe19aSRui Paulo } 127b6108616SRui Paulo amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; 128b6108616SRui Paulo amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; 129b6108616SRui Paulo amrr_setinterval(vap, 500 /* ms */); 130b6108616SRui Paulo amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); 131b032f27cSSam Leffler } 132b032f27cSSam Leffler 133b6108616SRui Paulo static void 134b6108616SRui Paulo amrr_deinit(struct ieee80211vap *vap) 135b032f27cSSam Leffler { 136810490a0SAndriy Voskoboinyk KASSERT(nrefs > 0, ("imbalanced attach/detach")); 137cd9fee3dSBjoern A. Zeeb IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); 138cd9fee3dSBjoern A. Zeeb vap->iv_rs = NULL; /* guard */ 139810490a0SAndriy Voskoboinyk nrefs--; /* XXX locking */ 14074828f25SSam Leffler } 14174828f25SSam Leffler 142b6108616SRui Paulo static void 143*4b2c7dfeSAdrian Chadd amrr_node_init_vht(struct ieee80211_node *ni) 144*4b2c7dfeSAdrian Chadd { 145*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 146*4b2c7dfeSAdrian Chadd 147*4b2c7dfeSAdrian Chadd /* Default to VHT NSS 1 MCS 2; should be reliable! */ 148*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs = 2; 149*4b2c7dfeSAdrian Chadd amn->amn_vht_nss = 1; 150*4b2c7dfeSAdrian Chadd ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss, 151*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs); 152*4b2c7dfeSAdrian Chadd 153*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 154*4b2c7dfeSAdrian Chadd "AMRR: VHT: initial rate NSS %d MCS %d", 155*4b2c7dfeSAdrian Chadd amn->amn_vht_nss, 156*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs); 157*4b2c7dfeSAdrian Chadd } 158*4b2c7dfeSAdrian Chadd 159*4b2c7dfeSAdrian Chadd static void 160078acac8SAdrian Chadd amrr_node_init_ht(struct ieee80211_node *ni) 161078acac8SAdrian Chadd { 162078acac8SAdrian Chadd const struct ieee80211_rateset *rs; 163078acac8SAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 164078acac8SAdrian Chadd uint8_t rate; /* dot11rate */ 165078acac8SAdrian Chadd 166078acac8SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates; 167078acac8SAdrian Chadd /* Initial rate - lowest */ 168078acac8SAdrian Chadd rate = rs->rs_rates[0]; 169078acac8SAdrian Chadd 170078acac8SAdrian Chadd /* Pick something low that's likely to succeed */ 171078acac8SAdrian Chadd for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; 172078acac8SAdrian Chadd amn->amn_rix--) { 173078acac8SAdrian Chadd /* 11n - stop at MCS4 */ 174078acac8SAdrian Chadd if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) 175078acac8SAdrian Chadd break; 176078acac8SAdrian Chadd } 177078acac8SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 178078acac8SAdrian Chadd 179078acac8SAdrian Chadd /* Ensure the MCS bit is set */ 180078acac8SAdrian Chadd rate |= IEEE80211_RATE_MCS; 181078acac8SAdrian Chadd 182078acac8SAdrian Chadd /* Assign initial rate from the rateset */ 183078acac8SAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, rate); 184078acac8SAdrian Chadd 185078acac8SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 186078acac8SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 187078acac8SAdrian Chadd "AMRR: nrates=%d, initial rate MCS %d", 188078acac8SAdrian Chadd rs->rs_nrates, 189078acac8SAdrian Chadd (rate & IEEE80211_RATE_VAL)); 190078acac8SAdrian Chadd } 191078acac8SAdrian Chadd 192078acac8SAdrian Chadd static void 193078acac8SAdrian Chadd amrr_node_init_legacy(struct ieee80211_node *ni) 194078acac8SAdrian Chadd { 195078acac8SAdrian Chadd const struct ieee80211_rateset *rs; 196078acac8SAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 197078acac8SAdrian Chadd uint8_t rate; /* dot11rate */ 198078acac8SAdrian Chadd 199078acac8SAdrian Chadd rs = &ni->ni_rates; 200078acac8SAdrian Chadd /* Initial rate - lowest */ 201078acac8SAdrian Chadd rate = rs->rs_rates[0]; 202078acac8SAdrian Chadd 203078acac8SAdrian Chadd /* Clear the basic rate flag if it's not 11n */ 204078acac8SAdrian Chadd rate &= IEEE80211_RATE_VAL; 205078acac8SAdrian Chadd 206078acac8SAdrian Chadd /* Pick something low that's likely to succeed */ 207078acac8SAdrian Chadd for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; 208078acac8SAdrian Chadd amn->amn_rix--) { 209078acac8SAdrian Chadd /* legacy - anything < 36mbit, stop searching */ 210078acac8SAdrian Chadd if ((rs->rs_rates[amn->amn_rix] & 211078acac8SAdrian Chadd IEEE80211_RATE_VAL) <= 72) 212078acac8SAdrian Chadd break; 213078acac8SAdrian Chadd } 214078acac8SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 215078acac8SAdrian Chadd 216078acac8SAdrian Chadd /* Assign initial rate from the rateset */ 217078acac8SAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, rate); 218078acac8SAdrian Chadd 219078acac8SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 220078acac8SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 221078acac8SAdrian Chadd "AMRR: nrates=%d, initial rate %d Mb", 222078acac8SAdrian Chadd rs->rs_nrates, 223078acac8SAdrian Chadd (rate & IEEE80211_RATE_VAL) / 2); 224078acac8SAdrian Chadd } 225078acac8SAdrian Chadd 226078acac8SAdrian Chadd static void 227b6108616SRui Paulo amrr_node_init(struct ieee80211_node *ni) 22874828f25SSam Leffler { 229b6108616SRui Paulo struct ieee80211vap *vap = ni->ni_vap; 230b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 231b6108616SRui Paulo struct ieee80211_amrr_node *amn; 232b032f27cSSam Leffler 2334367c2d1SAndriy Voskoboinyk if (!amrr) { 2344367c2d1SAndriy Voskoboinyk if_printf(vap->iv_ifp, "ratectl structure was not allocated, " 2354367c2d1SAndriy Voskoboinyk "per-node structure allocation skipped\n"); 2364367c2d1SAndriy Voskoboinyk return; 2374367c2d1SAndriy Voskoboinyk } 2384367c2d1SAndriy Voskoboinyk 239380fe2dfSRui Paulo if (ni->ni_rctls == NULL) { 240b9b53389SAdrian Chadd ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), 241b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 242645fe19aSRui Paulo if (amn == NULL) { 243645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " 244645fe19aSRui Paulo "structure\n"); 245645fe19aSRui Paulo return; 246645fe19aSRui Paulo } 247380fe2dfSRui Paulo } else 248380fe2dfSRui Paulo amn = ni->ni_rctls; 249078acac8SAdrian Chadd 250078acac8SAdrian Chadd /* Common state */ 251b032f27cSSam Leffler amn->amn_amrr = amrr; 25274828f25SSam Leffler amn->amn_success = 0; 25374828f25SSam Leffler amn->amn_recovery = 0; 25474828f25SSam Leffler amn->amn_txcnt = amn->amn_retrycnt = 0; 25574828f25SSam Leffler amn->amn_success_threshold = amrr->amrr_min_success_threshold; 256b032f27cSSam Leffler amn->amn_ticks = ticks; 257b032f27cSSam Leffler 258*4b2c7dfeSAdrian Chadd /* Pick the right rateset */ 259*4b2c7dfeSAdrian Chadd if (ieee80211_vht_check_tx_vht(ni)) 260*4b2c7dfeSAdrian Chadd amrr_node_init_vht(ni); 261*4b2c7dfeSAdrian Chadd else if (ieee80211_ht_check_tx_ht(ni)) 262078acac8SAdrian Chadd amrr_node_init_ht(ni); 263078acac8SAdrian Chadd else 264078acac8SAdrian Chadd amrr_node_init_legacy(ni); 26574828f25SSam Leffler } 26674828f25SSam Leffler 267b6108616SRui Paulo static void 268b6108616SRui Paulo amrr_node_deinit(struct ieee80211_node *ni) 269b6108616SRui Paulo { 270b9b53389SAdrian Chadd IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); 271b6108616SRui Paulo } 272b6108616SRui Paulo 273*4b2c7dfeSAdrian Chadd static void 274*4b2c7dfeSAdrian Chadd amrr_update_vht_inc(struct ieee80211_node *ni) 275*4b2c7dfeSAdrian Chadd { 276*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 277*4b2c7dfeSAdrian Chadd uint8_t nss, mcs; 278*4b2c7dfeSAdrian Chadd 279*4b2c7dfeSAdrian Chadd /* 280*4b2c7dfeSAdrian Chadd * For now just keep looping over MCS to 9, then NSS up, checking if 281*4b2c7dfeSAdrian Chadd * it's valid via ieee80211_vht_node_check_tx_valid_mcs(), 282*4b2c7dfeSAdrian Chadd * until we hit max. This at least tests the VHT MCS rates, 283*4b2c7dfeSAdrian Chadd * but definitely is suboptimal (in the same way the 11n MCS selection 284*4b2c7dfeSAdrian Chadd * is suboptimal.) 285*4b2c7dfeSAdrian Chadd */ 286*4b2c7dfeSAdrian Chadd nss = amn->amn_vht_nss; 287*4b2c7dfeSAdrian Chadd mcs = amn->amn_vht_mcs; 288*4b2c7dfeSAdrian Chadd 289*4b2c7dfeSAdrian Chadd while (nss <= 8 && mcs <= 9) { 290*4b2c7dfeSAdrian Chadd /* Increment MCS 0..9, NSS 1..8 */ 291*4b2c7dfeSAdrian Chadd if (mcs == 9) { 292*4b2c7dfeSAdrian Chadd mcs = 0; 293*4b2c7dfeSAdrian Chadd nss++; 294*4b2c7dfeSAdrian Chadd } else 295*4b2c7dfeSAdrian Chadd mcs++; 296*4b2c7dfeSAdrian Chadd if (nss > 8) 297*4b2c7dfeSAdrian Chadd break; 298*4b2c7dfeSAdrian Chadd 299*4b2c7dfeSAdrian Chadd if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss, 300*4b2c7dfeSAdrian Chadd mcs)) { 301*4b2c7dfeSAdrian Chadd amn->amn_vht_nss = nss; 302*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs = mcs; 303*4b2c7dfeSAdrian Chadd break; 304*4b2c7dfeSAdrian Chadd } 305*4b2c7dfeSAdrian Chadd } 306*4b2c7dfeSAdrian Chadd } 307*4b2c7dfeSAdrian Chadd 308*4b2c7dfeSAdrian Chadd static void 309*4b2c7dfeSAdrian Chadd amrr_update_vht_dec(struct ieee80211_node *ni) 310*4b2c7dfeSAdrian Chadd { 311*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 312*4b2c7dfeSAdrian Chadd uint8_t nss, mcs; 313*4b2c7dfeSAdrian Chadd 314*4b2c7dfeSAdrian Chadd /* 315*4b2c7dfeSAdrian Chadd * For now just keep looping over MCS 9 .. 0 then NSS down, checking if 316*4b2c7dfeSAdrian Chadd * it's valid via ieee80211_vht_node_check_tx_valid_mcs(), 317*4b2c7dfeSAdrian Chadd * until we hit min. This at least tests the VHT MCS rates, 318*4b2c7dfeSAdrian Chadd * but definitely is suboptimal (in the same way the 11n MCS selection 319*4b2c7dfeSAdrian Chadd * is suboptimal. 320*4b2c7dfeSAdrian Chadd */ 321*4b2c7dfeSAdrian Chadd nss = amn->amn_vht_nss; 322*4b2c7dfeSAdrian Chadd mcs = amn->amn_vht_mcs; 323*4b2c7dfeSAdrian Chadd 324*4b2c7dfeSAdrian Chadd while (nss >= 1 && mcs >= 0) { 325*4b2c7dfeSAdrian Chadd 326*4b2c7dfeSAdrian Chadd if (mcs == 0) { 327*4b2c7dfeSAdrian Chadd mcs = 9; 328*4b2c7dfeSAdrian Chadd nss--; 329*4b2c7dfeSAdrian Chadd } else 330*4b2c7dfeSAdrian Chadd mcs--; 331*4b2c7dfeSAdrian Chadd if (nss < 1) 332*4b2c7dfeSAdrian Chadd break; 333*4b2c7dfeSAdrian Chadd 334*4b2c7dfeSAdrian Chadd if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss, 335*4b2c7dfeSAdrian Chadd mcs)) { 336*4b2c7dfeSAdrian Chadd amn->amn_vht_nss = nss; 337*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs = mcs; 338*4b2c7dfeSAdrian Chadd break; 339*4b2c7dfeSAdrian Chadd } 340*4b2c7dfeSAdrian Chadd } 341*4b2c7dfeSAdrian Chadd } 342*4b2c7dfeSAdrian Chadd 343*4b2c7dfeSAdrian Chadd /* 344*4b2c7dfeSAdrian Chadd * A placeholder / temporary hack VHT rate control. 345*4b2c7dfeSAdrian Chadd * 346*4b2c7dfeSAdrian Chadd * Use the available MCS rates at the current node bandwidth 347*4b2c7dfeSAdrian Chadd * and configured / negotiated MCS rates. 348*4b2c7dfeSAdrian Chadd */ 349*4b2c7dfeSAdrian Chadd static int 350*4b2c7dfeSAdrian Chadd amrr_update_vht(struct ieee80211_node *ni) 351*4b2c7dfeSAdrian Chadd { 352*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 353*4b2c7dfeSAdrian Chadd struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs; 354*4b2c7dfeSAdrian Chadd 355*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 356*4b2c7dfeSAdrian Chadd "AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d", 357*4b2c7dfeSAdrian Chadd amn->amn_vht_nss, amn->amn_vht_mcs, amn->amn_txcnt, 358*4b2c7dfeSAdrian Chadd amn->amn_retrycnt); 359*4b2c7dfeSAdrian Chadd 360*4b2c7dfeSAdrian Chadd if (is_success(amn)) { 361*4b2c7dfeSAdrian Chadd amn->amn_success++; 362*4b2c7dfeSAdrian Chadd if (amn->amn_success >= amn->amn_success_threshold) { 363*4b2c7dfeSAdrian Chadd amn->amn_recovery = 1; 364*4b2c7dfeSAdrian Chadd amn->amn_success = 0; 365*4b2c7dfeSAdrian Chadd 366*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 367*4b2c7dfeSAdrian Chadd "AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)", 368*4b2c7dfeSAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt); 369*4b2c7dfeSAdrian Chadd 370*4b2c7dfeSAdrian Chadd amrr_update_vht_inc(ni); 371*4b2c7dfeSAdrian Chadd } else { 372*4b2c7dfeSAdrian Chadd amn->amn_recovery = 0; 373*4b2c7dfeSAdrian Chadd } 374*4b2c7dfeSAdrian Chadd } else if (is_failure(amn)) { 375*4b2c7dfeSAdrian Chadd amn->amn_success = 0; 376*4b2c7dfeSAdrian Chadd 377*4b2c7dfeSAdrian Chadd if (amn->amn_recovery) { 378*4b2c7dfeSAdrian Chadd amn->amn_success_threshold *= 2; 379*4b2c7dfeSAdrian Chadd if (amn->amn_success_threshold > 380*4b2c7dfeSAdrian Chadd amrr->amrr_max_success_threshold) 381*4b2c7dfeSAdrian Chadd amn->amn_success_threshold = 382*4b2c7dfeSAdrian Chadd amrr->amrr_max_success_threshold; 383*4b2c7dfeSAdrian Chadd } else { 384*4b2c7dfeSAdrian Chadd amn->amn_success_threshold = 385*4b2c7dfeSAdrian Chadd amrr->amrr_min_success_threshold; 386*4b2c7dfeSAdrian Chadd } 387*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 388*4b2c7dfeSAdrian Chadd "AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)", 389*4b2c7dfeSAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt); 390*4b2c7dfeSAdrian Chadd 391*4b2c7dfeSAdrian Chadd amrr_update_vht_dec(ni); 392*4b2c7dfeSAdrian Chadd 393*4b2c7dfeSAdrian Chadd amn->amn_recovery = 0; 394*4b2c7dfeSAdrian Chadd } 395*4b2c7dfeSAdrian Chadd 396*4b2c7dfeSAdrian Chadd /* Reset counters */ 397*4b2c7dfeSAdrian Chadd amn->amn_txcnt = 0; 398*4b2c7dfeSAdrian Chadd amn->amn_retrycnt = 0; 399*4b2c7dfeSAdrian Chadd 400*4b2c7dfeSAdrian Chadd /* Return 0, not useful anymore */ 401*4b2c7dfeSAdrian Chadd return (0); 402*4b2c7dfeSAdrian Chadd } 403*4b2c7dfeSAdrian Chadd 404b032f27cSSam Leffler static int 405e99cbea4SAdrian Chadd amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 406b032f27cSSam Leffler struct ieee80211_node *ni) 40774828f25SSam Leffler { 408b032f27cSSam Leffler int rix = amn->amn_rix; 409e99cbea4SAdrian Chadd const struct ieee80211_rateset *rs; 41074828f25SSam Leffler 411f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *)&ni->ni_htrates; 412f8bf74f2SAdrian Chadd 413eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 414f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 415e99cbea4SAdrian Chadd "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d", 416f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL, 417f8bf74f2SAdrian Chadd amn->amn_txcnt, 418f8bf74f2SAdrian Chadd amn->amn_retrycnt); 419f8bf74f2SAdrian Chadd 420f8d390beSAdrian Chadd /* 421f8d390beSAdrian Chadd * XXX This is totally bogus for 11n, as although high MCS 422f8d390beSAdrian Chadd * rates for each stream may be failing, the next stream 423f8d390beSAdrian Chadd * should be checked. 424f8d390beSAdrian Chadd * 425f8d390beSAdrian Chadd * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to 426f8d390beSAdrian Chadd * MCS23, we should skip 6/7 and try 8 onwards. 427f8d390beSAdrian Chadd */ 428b032f27cSSam Leffler if (is_success(amn)) { 42974828f25SSam Leffler amn->amn_success++; 43074828f25SSam Leffler if (amn->amn_success >= amn->amn_success_threshold && 431f8bf74f2SAdrian Chadd rix + 1 < rs->rs_nrates) { 43274828f25SSam Leffler amn->amn_recovery = 1; 43374828f25SSam Leffler amn->amn_success = 0; 434b032f27cSSam Leffler rix++; 435eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 436b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 437e99cbea4SAdrian Chadd "AMRR increasing rate MCS %d " 438e99cbea4SAdrian Chadd "(txcnt=%d retrycnt=%d)", 439f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL, 44074828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt); 44174828f25SSam Leffler } else { 44274828f25SSam Leffler amn->amn_recovery = 0; 44374828f25SSam Leffler } 44474828f25SSam Leffler } else if (is_failure(amn)) { 44574828f25SSam Leffler amn->amn_success = 0; 446b032f27cSSam Leffler if (rix > 0) { 44774828f25SSam Leffler if (amn->amn_recovery) { 44874828f25SSam Leffler amn->amn_success_threshold *= 2; 44974828f25SSam Leffler if (amn->amn_success_threshold > 45074828f25SSam Leffler amrr->amrr_max_success_threshold) 45174828f25SSam Leffler amn->amn_success_threshold = 45274828f25SSam Leffler amrr->amrr_max_success_threshold; 45374828f25SSam Leffler } else { 45474828f25SSam Leffler amn->amn_success_threshold = 45574828f25SSam Leffler amrr->amrr_min_success_threshold; 45674828f25SSam Leffler } 457b032f27cSSam Leffler rix--; 458eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 459b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 460e99cbea4SAdrian Chadd "AMRR decreasing rate MCS %d " 461e99cbea4SAdrian Chadd "(txcnt=%d retrycnt=%d)", 462f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL, 46374828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt); 46474828f25SSam Leffler } 46574828f25SSam Leffler amn->amn_recovery = 0; 46674828f25SSam Leffler } 46774828f25SSam Leffler 468e99cbea4SAdrian Chadd return (rix); 469e99cbea4SAdrian Chadd } 470e99cbea4SAdrian Chadd 471e99cbea4SAdrian Chadd static int 472e99cbea4SAdrian Chadd amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 473e99cbea4SAdrian Chadd struct ieee80211_node *ni) 474e99cbea4SAdrian Chadd { 475e99cbea4SAdrian Chadd int rix = amn->amn_rix; 476e99cbea4SAdrian Chadd const struct ieee80211_rateset *rs; 477e99cbea4SAdrian Chadd 478e99cbea4SAdrian Chadd rs = &ni->ni_rates; 479e99cbea4SAdrian Chadd 480e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 481e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 482e99cbea4SAdrian Chadd "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d", 483e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 484e99cbea4SAdrian Chadd amn->amn_txcnt, 485e99cbea4SAdrian Chadd amn->amn_retrycnt); 486e99cbea4SAdrian Chadd 487e99cbea4SAdrian Chadd if (is_success(amn)) { 488e99cbea4SAdrian Chadd amn->amn_success++; 489e99cbea4SAdrian Chadd if (amn->amn_success >= amn->amn_success_threshold && 490e99cbea4SAdrian Chadd rix + 1 < rs->rs_nrates) { 491e99cbea4SAdrian Chadd amn->amn_recovery = 1; 492e99cbea4SAdrian Chadd amn->amn_success = 0; 493e99cbea4SAdrian Chadd rix++; 494e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 495e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 496e99cbea4SAdrian Chadd "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)", 497e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 498e99cbea4SAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt); 499e99cbea4SAdrian Chadd } else { 500e99cbea4SAdrian Chadd amn->amn_recovery = 0; 501e99cbea4SAdrian Chadd } 502e99cbea4SAdrian Chadd } else if (is_failure(amn)) { 503e99cbea4SAdrian Chadd amn->amn_success = 0; 504e99cbea4SAdrian Chadd if (rix > 0) { 505e99cbea4SAdrian Chadd if (amn->amn_recovery) { 506e99cbea4SAdrian Chadd amn->amn_success_threshold *= 2; 507e99cbea4SAdrian Chadd if (amn->amn_success_threshold > 508e99cbea4SAdrian Chadd amrr->amrr_max_success_threshold) 509e99cbea4SAdrian Chadd amn->amn_success_threshold = 510e99cbea4SAdrian Chadd amrr->amrr_max_success_threshold; 511e99cbea4SAdrian Chadd } else { 512e99cbea4SAdrian Chadd amn->amn_success_threshold = 513e99cbea4SAdrian Chadd amrr->amrr_min_success_threshold; 514e99cbea4SAdrian Chadd } 515e99cbea4SAdrian Chadd rix--; 516e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 517e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 518e99cbea4SAdrian Chadd "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)", 519e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 520e99cbea4SAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt); 521e99cbea4SAdrian Chadd } 522e99cbea4SAdrian Chadd amn->amn_recovery = 0; 523e99cbea4SAdrian Chadd } 524e99cbea4SAdrian Chadd 525e99cbea4SAdrian Chadd return (rix); 526e99cbea4SAdrian Chadd } 527e99cbea4SAdrian Chadd 528e99cbea4SAdrian Chadd static int 529e99cbea4SAdrian Chadd amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 530e99cbea4SAdrian Chadd struct ieee80211_node *ni) 531e99cbea4SAdrian Chadd { 532e99cbea4SAdrian Chadd int rix; 533e99cbea4SAdrian Chadd 534e99cbea4SAdrian Chadd KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); 535e99cbea4SAdrian Chadd 536*4b2c7dfeSAdrian Chadd /* Pick the right rateset */ 537*4b2c7dfeSAdrian Chadd if (ieee80211_vht_check_tx_vht(ni)) 538*4b2c7dfeSAdrian Chadd rix = amrr_update_vht(ni); 539*4b2c7dfeSAdrian Chadd else if (ieee80211_ht_check_tx_ht(ni)) 540e99cbea4SAdrian Chadd rix = amrr_update_ht(amrr, amn, ni); 541e99cbea4SAdrian Chadd else 542e99cbea4SAdrian Chadd rix = amrr_update_legacy(amrr, amn, ni); 543e99cbea4SAdrian Chadd 544b032f27cSSam Leffler /* reset counters */ 545b032f27cSSam Leffler amn->amn_txcnt = 0; 546b032f27cSSam Leffler amn->amn_retrycnt = 0; 547b032f27cSSam Leffler 548e99cbea4SAdrian Chadd return (rix); 549b032f27cSSam Leffler } 550b032f27cSSam Leffler 551*4b2c7dfeSAdrian Chadd static int 552*4b2c7dfeSAdrian Chadd amrr_rate_vht(struct ieee80211_node *ni) 553*4b2c7dfeSAdrian Chadd { 554*4b2c7dfeSAdrian Chadd struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs; 555*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls; 556*4b2c7dfeSAdrian Chadd 557*4b2c7dfeSAdrian Chadd if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) 558*4b2c7dfeSAdrian Chadd amrr_update_vht(ni); 559*4b2c7dfeSAdrian Chadd 560*4b2c7dfeSAdrian Chadd ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss, 561*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs); 562*4b2c7dfeSAdrian Chadd 563*4b2c7dfeSAdrian Chadd /* Note: There's no vht rs_rates, and the API doesn't use it anymore */ 564*4b2c7dfeSAdrian Chadd return (0); 565*4b2c7dfeSAdrian Chadd } 566*4b2c7dfeSAdrian Chadd 567b032f27cSSam Leffler /* 568b032f27cSSam Leffler * Return the rate index to use in sending a data frame. 569b032f27cSSam Leffler * Update our internal state if it's been long enough. 570b032f27cSSam Leffler * If the rate changes we also update ni_txrate to match. 571b032f27cSSam Leffler */ 572b6108616SRui Paulo static int 573b6108616SRui Paulo amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) 574b032f27cSSam Leffler { 575b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 5764367c2d1SAndriy Voskoboinyk struct ieee80211_amrr *amrr; 577f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL; 578b032f27cSSam Leffler int rix; 579b032f27cSSam Leffler 5804367c2d1SAndriy Voskoboinyk /* XXX should return -1 here, but drivers may not expect this... */ 5814367c2d1SAndriy Voskoboinyk if (!amn) 5824367c2d1SAndriy Voskoboinyk { 58338075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, 58438075f7dSAdrian Chadd ni->ni_rates.rs_rates[0]); 5854367c2d1SAndriy Voskoboinyk return 0; 5864367c2d1SAndriy Voskoboinyk } 5874367c2d1SAndriy Voskoboinyk 588*4b2c7dfeSAdrian Chadd if (ieee80211_vht_check_tx_vht(ni)) 589*4b2c7dfeSAdrian Chadd return (amrr_rate_vht(ni)); 5904367c2d1SAndriy Voskoboinyk 591*4b2c7dfeSAdrian Chadd /* Pick the right rateset */ 5922014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) { 593f8bf74f2SAdrian Chadd /* XXX ew */ 594f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates; 595f8bf74f2SAdrian Chadd } else { 596f8bf74f2SAdrian Chadd rs = &ni->ni_rates; 597f8bf74f2SAdrian Chadd } 598f8bf74f2SAdrian Chadd 599*4b2c7dfeSAdrian Chadd amrr = amn->amn_amrr; 600b032f27cSSam Leffler if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { 601b032f27cSSam Leffler rix = amrr_update(amrr, amn, ni); 602b032f27cSSam Leffler if (rix != amn->amn_rix) { 60338075f7dSAdrian Chadd uint8_t dot11Rate; 604b032f27cSSam Leffler /* update public rate */ 60538075f7dSAdrian Chadd dot11Rate = rs->rs_rates[rix]; 606f8bf74f2SAdrian Chadd /* XXX strip basic rate flag from txrate, if non-11n */ 6072014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) 60838075f7dSAdrian Chadd dot11Rate |= IEEE80211_RATE_MCS; 609f8bf74f2SAdrian Chadd else 61038075f7dSAdrian Chadd dot11Rate &= IEEE80211_RATE_VAL; 61138075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, dot11Rate); 61238075f7dSAdrian Chadd 613b032f27cSSam Leffler amn->amn_rix = rix; 614b032f27cSSam Leffler } 615b032f27cSSam Leffler amn->amn_ticks = ticks; 616b032f27cSSam Leffler } else 617b032f27cSSam Leffler rix = amn->amn_rix; 618b032f27cSSam Leffler return rix; 619b032f27cSSam Leffler } 620b032f27cSSam Leffler 621b6108616SRui Paulo /* 622b6108616SRui Paulo * Update statistics with tx complete status. Ok is non-zero 623b6108616SRui Paulo * if the packet is known to be ACK'd. Retries has the number 624b6108616SRui Paulo * retransmissions (i.e. xmit attempts - 1). 625b6108616SRui Paulo */ 626b6108616SRui Paulo static void 627f6930becSAndriy Voskoboinyk amrr_tx_complete(const struct ieee80211_node *ni, 628f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *status) 629b6108616SRui Paulo { 630b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 631f6930becSAndriy Voskoboinyk int retries; 632f6930becSAndriy Voskoboinyk 6334367c2d1SAndriy Voskoboinyk if (!amn) 6344367c2d1SAndriy Voskoboinyk return; 6354367c2d1SAndriy Voskoboinyk 636f6930becSAndriy Voskoboinyk retries = 0; 637f6930becSAndriy Voskoboinyk if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY) 638f6930becSAndriy Voskoboinyk retries = status->long_retries; 639b6108616SRui Paulo 640b6108616SRui Paulo amn->amn_txcnt++; 641f6930becSAndriy Voskoboinyk if (status->status == IEEE80211_RATECTL_TX_SUCCESS) 642b6108616SRui Paulo amn->amn_success++; 643b6108616SRui Paulo amn->amn_retrycnt += retries; 644b6108616SRui Paulo } 645b6108616SRui Paulo 646f6930becSAndriy Voskoboinyk static void 647f6930becSAndriy Voskoboinyk amrr_tx_update_cb(void *arg, struct ieee80211_node *ni) 648f6930becSAndriy Voskoboinyk { 649f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats = arg; 650f6930becSAndriy Voskoboinyk struct ieee80211_amrr_node *amn = ni->ni_rctls; 651f6930becSAndriy Voskoboinyk int txcnt, success, retrycnt; 652f6930becSAndriy Voskoboinyk 6534367c2d1SAndriy Voskoboinyk if (!amn) 6544367c2d1SAndriy Voskoboinyk return; 6554367c2d1SAndriy Voskoboinyk 656f6930becSAndriy Voskoboinyk txcnt = stats->nframes; 657f6930becSAndriy Voskoboinyk success = stats->nsuccess; 658f6930becSAndriy Voskoboinyk retrycnt = 0; 659f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES) 660f6930becSAndriy Voskoboinyk retrycnt = stats->nretries; 661f6930becSAndriy Voskoboinyk 662f6930becSAndriy Voskoboinyk amn->amn_txcnt += txcnt; 663f6930becSAndriy Voskoboinyk amn->amn_success += success; 664f6930becSAndriy Voskoboinyk amn->amn_retrycnt += retrycnt; 665f6930becSAndriy Voskoboinyk } 666f6930becSAndriy Voskoboinyk 667b6108616SRui Paulo /* 668b6108616SRui Paulo * Set tx count/retry statistics explicitly. Intended for 669b6108616SRui Paulo * drivers that poll the device for statistics maintained 670b6108616SRui Paulo * in the device. 671b6108616SRui Paulo */ 672b6108616SRui Paulo static void 673f6930becSAndriy Voskoboinyk amrr_tx_update(struct ieee80211vap *vap, 674f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats) 675b6108616SRui Paulo { 676b6108616SRui Paulo 677f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE) 678f6930becSAndriy Voskoboinyk amrr_tx_update_cb(stats, stats->ni); 679f6930becSAndriy Voskoboinyk else { 680f6930becSAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap, 681f6930becSAndriy Voskoboinyk amrr_tx_update_cb, stats); 682f6930becSAndriy Voskoboinyk } 683b6108616SRui Paulo } 684b6108616SRui Paulo 685b032f27cSSam Leffler static int 686b032f27cSSam Leffler amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) 687b032f27cSSam Leffler { 688b6108616SRui Paulo struct ieee80211vap *vap = arg1; 689b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 6904367c2d1SAndriy Voskoboinyk int msecs, error; 691b032f27cSSam Leffler 6924367c2d1SAndriy Voskoboinyk if (!amrr) 6934367c2d1SAndriy Voskoboinyk return ENOMEM; 6944367c2d1SAndriy Voskoboinyk 6954367c2d1SAndriy Voskoboinyk msecs = ticks_to_msecs(amrr->amrr_interval); 696b032f27cSSam Leffler error = sysctl_handle_int(oidp, &msecs, 0, req); 697b032f27cSSam Leffler if (error || !req->newptr) 698b032f27cSSam Leffler return error; 699b6108616SRui Paulo amrr_setinterval(vap, msecs); 700b032f27cSSam Leffler return 0; 701b032f27cSSam Leffler } 702b032f27cSSam Leffler 703b032f27cSSam Leffler static void 704b6108616SRui Paulo amrr_sysctlattach(struct ieee80211vap *vap, 705b032f27cSSam Leffler struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) 706b032f27cSSam Leffler { 707b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 708b032f27cSSam Leffler 7094367c2d1SAndriy Voskoboinyk if (!amrr) 7104367c2d1SAndriy Voskoboinyk return; 7114367c2d1SAndriy Voskoboinyk 712b032f27cSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 71308f5e6bbSPawel Biernacki "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 71408f5e6bbSPawel Biernacki vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); 715b032f27cSSam Leffler /* XXX bounds check values */ 716f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 717b032f27cSSam Leffler "amrr_max_sucess_threshold", CTLFLAG_RW, 718b032f27cSSam Leffler &amrr->amrr_max_success_threshold, 0, ""); 719f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 720b032f27cSSam Leffler "amrr_min_sucess_threshold", CTLFLAG_RW, 721b032f27cSSam Leffler &amrr->amrr_min_success_threshold, 0, ""); 72274828f25SSam Leffler } 72373931706SAdrian Chadd 72473931706SAdrian Chadd static void 725c1475a19SConrad Meyer amrr_print_node_rate(struct ieee80211_amrr_node *amn, 726c1475a19SConrad Meyer struct ieee80211_node *ni, struct sbuf *s) 72773931706SAdrian Chadd { 72873931706SAdrian Chadd int rate; 72973931706SAdrian Chadd struct ieee80211_rateset *rs; 73073931706SAdrian Chadd 7312014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) { 73273931706SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates; 73373931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 73473931706SAdrian Chadd sbuf_printf(s, "rate: MCS %d\n", rate); 73573931706SAdrian Chadd } else { 73673931706SAdrian Chadd rs = &ni->ni_rates; 73773931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 73873931706SAdrian Chadd sbuf_printf(s, "rate: %d Mbit\n", rate / 2); 73973931706SAdrian Chadd } 740c1475a19SConrad Meyer } 74173931706SAdrian Chadd 742c1475a19SConrad Meyer static void 743c1475a19SConrad Meyer amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) 744c1475a19SConrad Meyer { 745c1475a19SConrad Meyer struct ieee80211_amrr_node *amn = ni->ni_rctls; 746c1475a19SConrad Meyer 747c1475a19SConrad Meyer /* XXX TODO: check locking? */ 748c1475a19SConrad Meyer 749c1475a19SConrad Meyer if (!amn) 750c1475a19SConrad Meyer return; 751c1475a19SConrad Meyer 752c1475a19SConrad Meyer amrr_print_node_rate(amn, ni, s); 75373931706SAdrian Chadd sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); 75473931706SAdrian Chadd sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); 75573931706SAdrian Chadd sbuf_printf(s, "success: %u\n", amn->amn_success); 75673931706SAdrian Chadd sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); 75773931706SAdrian Chadd sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); 75873931706SAdrian Chadd sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); 75973931706SAdrian Chadd } 760