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> 5274828f25SSam Leffler #include <net80211/ieee80211_amrr.h> 53b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h> 5474828f25SSam Leffler 5574828f25SSam Leffler #define is_success(amn) \ 5674828f25SSam Leffler ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) 5774828f25SSam Leffler #define is_failure(amn) \ 5874828f25SSam Leffler ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) 5974828f25SSam Leffler #define is_enough(amn) \ 6074828f25SSam Leffler ((amn)->amn_txcnt > 10) 61b032f27cSSam Leffler 62b6108616SRui Paulo static void amrr_setinterval(const struct ieee80211vap *, int); 63b6108616SRui Paulo static void amrr_init(struct ieee80211vap *); 64b6108616SRui Paulo static void amrr_deinit(struct ieee80211vap *); 65b6108616SRui Paulo static void amrr_node_init(struct ieee80211_node *); 66b6108616SRui Paulo static void amrr_node_deinit(struct ieee80211_node *); 67b6108616SRui Paulo static int amrr_update(struct ieee80211_amrr *, 68b6108616SRui Paulo struct ieee80211_amrr_node *, struct ieee80211_node *); 69b6108616SRui Paulo static int amrr_rate(struct ieee80211_node *, void *, uint32_t); 70f6930becSAndriy Voskoboinyk static void amrr_tx_complete(const struct ieee80211_node *, 71f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *); 72f6930becSAndriy Voskoboinyk static void amrr_tx_update_cb(void *, struct ieee80211_node *); 73f6930becSAndriy Voskoboinyk static void amrr_tx_update(struct ieee80211vap *vap, 74f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *); 75b6108616SRui Paulo static void amrr_sysctlattach(struct ieee80211vap *, 76b6108616SRui Paulo struct sysctl_ctx_list *, struct sysctl_oid *); 7773931706SAdrian Chadd static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s); 78b032f27cSSam Leffler 79b032f27cSSam Leffler /* number of references from net80211 layer */ 80b032f27cSSam Leffler static int nrefs = 0; 81b032f27cSSam Leffler 82b6108616SRui Paulo static const struct ieee80211_ratectl amrr = { 83b6108616SRui Paulo .ir_name = "amrr", 84b6108616SRui Paulo .ir_attach = NULL, 85b6108616SRui Paulo .ir_detach = NULL, 86b6108616SRui Paulo .ir_init = amrr_init, 87b6108616SRui Paulo .ir_deinit = amrr_deinit, 88b6108616SRui Paulo .ir_node_init = amrr_node_init, 89b6108616SRui Paulo .ir_node_deinit = amrr_node_deinit, 90b6108616SRui Paulo .ir_rate = amrr_rate, 91b6108616SRui Paulo .ir_tx_complete = amrr_tx_complete, 92b6108616SRui Paulo .ir_tx_update = amrr_tx_update, 93b6108616SRui Paulo .ir_setinterval = amrr_setinterval, 9473931706SAdrian Chadd .ir_node_stats = amrr_node_stats, 95b6108616SRui Paulo }; 96b6108616SRui Paulo IEEE80211_RATECTL_MODULE(amrr, 1); 97b6108616SRui Paulo IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); 98b6108616SRui Paulo 99b6108616SRui Paulo static void 100b6108616SRui Paulo amrr_setinterval(const struct ieee80211vap *vap, int msecs) 101b032f27cSSam Leffler { 102b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 103b032f27cSSam Leffler 1044367c2d1SAndriy Voskoboinyk if (!amrr) 1054367c2d1SAndriy Voskoboinyk return; 1064367c2d1SAndriy Voskoboinyk 107b032f27cSSam Leffler if (msecs < 100) 108b032f27cSSam Leffler msecs = 100; 1099df9e936SAndriy Voskoboinyk amrr->amrr_interval = msecs_to_ticks(msecs); 110b032f27cSSam Leffler } 11174828f25SSam Leffler 112b6108616SRui Paulo static void 113b6108616SRui Paulo amrr_init(struct ieee80211vap *vap) 11474828f25SSam Leffler { 115b6108616SRui Paulo struct ieee80211_amrr *amrr; 116b032f27cSSam Leffler 117b6108616SRui Paulo KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); 118b6108616SRui Paulo 119810490a0SAndriy Voskoboinyk nrefs++; /* XXX locking */ 120b9b53389SAdrian Chadd amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr), 121b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 122645fe19aSRui Paulo if (amrr == NULL) { 123645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); 124645fe19aSRui Paulo return; 125645fe19aSRui Paulo } 126b6108616SRui Paulo amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; 127b6108616SRui Paulo amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; 128b6108616SRui Paulo amrr_setinterval(vap, 500 /* ms */); 129b6108616SRui Paulo amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); 130b032f27cSSam Leffler } 131b032f27cSSam Leffler 132b6108616SRui Paulo static void 133b6108616SRui Paulo amrr_deinit(struct ieee80211vap *vap) 134b032f27cSSam Leffler { 135810490a0SAndriy Voskoboinyk KASSERT(nrefs > 0, ("imbalanced attach/detach")); 136cd9fee3dSBjoern A. Zeeb IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); 137cd9fee3dSBjoern A. Zeeb vap->iv_rs = NULL; /* guard */ 138810490a0SAndriy Voskoboinyk nrefs--; /* XXX locking */ 13974828f25SSam Leffler } 14074828f25SSam Leffler 141b6108616SRui Paulo static void 142b6108616SRui Paulo amrr_node_init(struct ieee80211_node *ni) 14374828f25SSam Leffler { 144f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL; 145b6108616SRui Paulo struct ieee80211vap *vap = ni->ni_vap; 146b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 147b6108616SRui Paulo struct ieee80211_amrr_node *amn; 148f8bf74f2SAdrian Chadd uint8_t rate; 149b032f27cSSam Leffler 1504367c2d1SAndriy Voskoboinyk if (!amrr) { 1514367c2d1SAndriy Voskoboinyk if_printf(vap->iv_ifp, "ratectl structure was not allocated, " 1524367c2d1SAndriy Voskoboinyk "per-node structure allocation skipped\n"); 1534367c2d1SAndriy Voskoboinyk return; 1544367c2d1SAndriy Voskoboinyk } 1554367c2d1SAndriy Voskoboinyk 156380fe2dfSRui Paulo if (ni->ni_rctls == NULL) { 157b9b53389SAdrian Chadd ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), 158b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 159645fe19aSRui Paulo if (amn == NULL) { 160645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " 161645fe19aSRui Paulo "structure\n"); 162645fe19aSRui Paulo return; 163645fe19aSRui Paulo } 164380fe2dfSRui Paulo } else 165380fe2dfSRui Paulo amn = ni->ni_rctls; 166b032f27cSSam Leffler amn->amn_amrr = amrr; 16774828f25SSam Leffler amn->amn_success = 0; 16874828f25SSam Leffler amn->amn_recovery = 0; 16974828f25SSam Leffler amn->amn_txcnt = amn->amn_retrycnt = 0; 17074828f25SSam Leffler amn->amn_success_threshold = amrr->amrr_min_success_threshold; 171b032f27cSSam Leffler 172f8bf74f2SAdrian Chadd /* 11n or not? Pick the right rateset */ 1732014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) { 174f8bf74f2SAdrian Chadd /* XXX ew */ 175f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 176f8bf74f2SAdrian Chadd "%s: 11n node", __func__); 177f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates; 178f8bf74f2SAdrian Chadd } else { 179f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 180f8bf74f2SAdrian Chadd "%s: non-11n node", __func__); 181f8bf74f2SAdrian Chadd rs = &ni->ni_rates; 182f8bf74f2SAdrian Chadd } 183f8bf74f2SAdrian Chadd 184f8bf74f2SAdrian Chadd /* Initial rate - lowest */ 185f8bf74f2SAdrian Chadd rate = rs->rs_rates[0]; 186f8bf74f2SAdrian Chadd 187f8bf74f2SAdrian Chadd /* XXX clear the basic rate flag if it's not 11n */ 1882014462dSAdrian Chadd if (! ieee80211_ht_check_tx_ht(ni)) 189f8bf74f2SAdrian Chadd rate &= IEEE80211_RATE_VAL; 190f8bf74f2SAdrian Chadd 191f8bf74f2SAdrian Chadd /* pick initial rate from the rateset - HT or otherwise */ 192ebb12408SAdrian Chadd /* Pick something low that's likely to succeed */ 193f8bf74f2SAdrian Chadd for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; 194f8bf74f2SAdrian Chadd amn->amn_rix--) { 195f8bf74f2SAdrian Chadd /* legacy - anything < 36mbit, stop searching */ 196ebb12408SAdrian Chadd /* 11n - stop at MCS4 */ 1972014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) { 198ebb12408SAdrian Chadd if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) 199f8bf74f2SAdrian Chadd break; 2005140f9e6SAdrian Chadd } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) 201f8bf74f2SAdrian Chadd break; 202f8bf74f2SAdrian Chadd } 2035140f9e6SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 204f8bf74f2SAdrian Chadd 205f8bf74f2SAdrian Chadd /* if the rate is an 11n rate, ensure the MCS bit is set */ 2062014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) 207f8bf74f2SAdrian Chadd rate |= IEEE80211_RATE_MCS; 208f8bf74f2SAdrian Chadd 209f8bf74f2SAdrian Chadd /* Assign initial rate from the rateset */ 21038075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, rate); 211b032f27cSSam Leffler amn->amn_ticks = ticks; 212b032f27cSSam Leffler 213eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 214eec88845SAdrian Chadd /* XXX TODO: non-11n rate should be divided by two.. */ 215b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 216eec88845SAdrian Chadd "AMRR: nrates=%d, initial rate %s%d", 217f8bf74f2SAdrian Chadd rs->rs_nrates, 2182014462dSAdrian Chadd ieee80211_ht_check_tx_ht(ni) ? "MCS " : "", 219eec88845SAdrian Chadd rate & IEEE80211_RATE_VAL); 22074828f25SSam Leffler } 22174828f25SSam Leffler 222b6108616SRui Paulo static void 223b6108616SRui Paulo amrr_node_deinit(struct ieee80211_node *ni) 224b6108616SRui Paulo { 225b9b53389SAdrian Chadd IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); 226b6108616SRui Paulo } 227b6108616SRui Paulo 228b032f27cSSam Leffler static int 229*e99cbea4SAdrian Chadd amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 230b032f27cSSam Leffler struct ieee80211_node *ni) 23174828f25SSam Leffler { 232b032f27cSSam Leffler int rix = amn->amn_rix; 233*e99cbea4SAdrian Chadd const struct ieee80211_rateset *rs; 23474828f25SSam Leffler 235f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *)&ni->ni_htrates; 236f8bf74f2SAdrian Chadd 237eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 238f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 239*e99cbea4SAdrian Chadd "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d", 240f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL, 241f8bf74f2SAdrian Chadd amn->amn_txcnt, 242f8bf74f2SAdrian Chadd amn->amn_retrycnt); 243f8bf74f2SAdrian Chadd 244f8d390beSAdrian Chadd /* 245f8d390beSAdrian Chadd * XXX This is totally bogus for 11n, as although high MCS 246f8d390beSAdrian Chadd * rates for each stream may be failing, the next stream 247f8d390beSAdrian Chadd * should be checked. 248f8d390beSAdrian Chadd * 249f8d390beSAdrian Chadd * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to 250f8d390beSAdrian Chadd * MCS23, we should skip 6/7 and try 8 onwards. 251f8d390beSAdrian Chadd */ 252b032f27cSSam Leffler if (is_success(amn)) { 25374828f25SSam Leffler amn->amn_success++; 25474828f25SSam Leffler if (amn->amn_success >= amn->amn_success_threshold && 255f8bf74f2SAdrian Chadd rix + 1 < rs->rs_nrates) { 25674828f25SSam Leffler amn->amn_recovery = 1; 25774828f25SSam Leffler amn->amn_success = 0; 258b032f27cSSam Leffler rix++; 259eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 260b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 261*e99cbea4SAdrian Chadd "AMRR increasing rate MCS %d " 262*e99cbea4SAdrian Chadd "(txcnt=%d retrycnt=%d)", 263f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL, 26474828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt); 26574828f25SSam Leffler } else { 26674828f25SSam Leffler amn->amn_recovery = 0; 26774828f25SSam Leffler } 26874828f25SSam Leffler } else if (is_failure(amn)) { 26974828f25SSam Leffler amn->amn_success = 0; 270b032f27cSSam Leffler if (rix > 0) { 27174828f25SSam Leffler if (amn->amn_recovery) { 27274828f25SSam Leffler amn->amn_success_threshold *= 2; 27374828f25SSam Leffler if (amn->amn_success_threshold > 27474828f25SSam Leffler amrr->amrr_max_success_threshold) 27574828f25SSam Leffler amn->amn_success_threshold = 27674828f25SSam Leffler amrr->amrr_max_success_threshold; 27774828f25SSam Leffler } else { 27874828f25SSam Leffler amn->amn_success_threshold = 27974828f25SSam Leffler amrr->amrr_min_success_threshold; 28074828f25SSam Leffler } 281b032f27cSSam Leffler rix--; 282eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 283b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 284*e99cbea4SAdrian Chadd "AMRR decreasing rate MCS %d " 285*e99cbea4SAdrian Chadd "(txcnt=%d retrycnt=%d)", 286f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL, 28774828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt); 28874828f25SSam Leffler } 28974828f25SSam Leffler amn->amn_recovery = 0; 29074828f25SSam Leffler } 29174828f25SSam Leffler 292*e99cbea4SAdrian Chadd return (rix); 293*e99cbea4SAdrian Chadd } 294*e99cbea4SAdrian Chadd 295*e99cbea4SAdrian Chadd static int 296*e99cbea4SAdrian Chadd amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 297*e99cbea4SAdrian Chadd struct ieee80211_node *ni) 298*e99cbea4SAdrian Chadd { 299*e99cbea4SAdrian Chadd int rix = amn->amn_rix; 300*e99cbea4SAdrian Chadd const struct ieee80211_rateset *rs; 301*e99cbea4SAdrian Chadd 302*e99cbea4SAdrian Chadd rs = &ni->ni_rates; 303*e99cbea4SAdrian Chadd 304*e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 305*e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 306*e99cbea4SAdrian Chadd "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d", 307*e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 308*e99cbea4SAdrian Chadd amn->amn_txcnt, 309*e99cbea4SAdrian Chadd amn->amn_retrycnt); 310*e99cbea4SAdrian Chadd 311*e99cbea4SAdrian Chadd if (is_success(amn)) { 312*e99cbea4SAdrian Chadd amn->amn_success++; 313*e99cbea4SAdrian Chadd if (amn->amn_success >= amn->amn_success_threshold && 314*e99cbea4SAdrian Chadd rix + 1 < rs->rs_nrates) { 315*e99cbea4SAdrian Chadd amn->amn_recovery = 1; 316*e99cbea4SAdrian Chadd amn->amn_success = 0; 317*e99cbea4SAdrian Chadd rix++; 318*e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 319*e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 320*e99cbea4SAdrian Chadd "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)", 321*e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 322*e99cbea4SAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt); 323*e99cbea4SAdrian Chadd } else { 324*e99cbea4SAdrian Chadd amn->amn_recovery = 0; 325*e99cbea4SAdrian Chadd } 326*e99cbea4SAdrian Chadd } else if (is_failure(amn)) { 327*e99cbea4SAdrian Chadd amn->amn_success = 0; 328*e99cbea4SAdrian Chadd if (rix > 0) { 329*e99cbea4SAdrian Chadd if (amn->amn_recovery) { 330*e99cbea4SAdrian Chadd amn->amn_success_threshold *= 2; 331*e99cbea4SAdrian Chadd if (amn->amn_success_threshold > 332*e99cbea4SAdrian Chadd amrr->amrr_max_success_threshold) 333*e99cbea4SAdrian Chadd amn->amn_success_threshold = 334*e99cbea4SAdrian Chadd amrr->amrr_max_success_threshold; 335*e99cbea4SAdrian Chadd } else { 336*e99cbea4SAdrian Chadd amn->amn_success_threshold = 337*e99cbea4SAdrian Chadd amrr->amrr_min_success_threshold; 338*e99cbea4SAdrian Chadd } 339*e99cbea4SAdrian Chadd rix--; 340*e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */ 341*e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, 342*e99cbea4SAdrian Chadd "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)", 343*e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2, 344*e99cbea4SAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt); 345*e99cbea4SAdrian Chadd } 346*e99cbea4SAdrian Chadd amn->amn_recovery = 0; 347*e99cbea4SAdrian Chadd } 348*e99cbea4SAdrian Chadd 349*e99cbea4SAdrian Chadd return (rix); 350*e99cbea4SAdrian Chadd } 351*e99cbea4SAdrian Chadd 352*e99cbea4SAdrian Chadd static int 353*e99cbea4SAdrian Chadd amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, 354*e99cbea4SAdrian Chadd struct ieee80211_node *ni) 355*e99cbea4SAdrian Chadd { 356*e99cbea4SAdrian Chadd int rix; 357*e99cbea4SAdrian Chadd 358*e99cbea4SAdrian Chadd KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); 359*e99cbea4SAdrian Chadd 360*e99cbea4SAdrian Chadd /* 11n or not? Pick the right rateset */ 361*e99cbea4SAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) 362*e99cbea4SAdrian Chadd rix = amrr_update_ht(amrr, amn, ni); 363*e99cbea4SAdrian Chadd else 364*e99cbea4SAdrian Chadd rix = amrr_update_legacy(amrr, amn, ni); 365*e99cbea4SAdrian Chadd 366b032f27cSSam Leffler /* reset counters */ 367b032f27cSSam Leffler amn->amn_txcnt = 0; 368b032f27cSSam Leffler amn->amn_retrycnt = 0; 369b032f27cSSam Leffler 370*e99cbea4SAdrian Chadd return (rix); 371b032f27cSSam Leffler } 372b032f27cSSam Leffler 373b032f27cSSam Leffler /* 374b032f27cSSam Leffler * Return the rate index to use in sending a data frame. 375b032f27cSSam Leffler * Update our internal state if it's been long enough. 376b032f27cSSam Leffler * If the rate changes we also update ni_txrate to match. 377b032f27cSSam Leffler */ 378b6108616SRui Paulo static int 379b6108616SRui Paulo amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) 380b032f27cSSam Leffler { 381b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 3824367c2d1SAndriy Voskoboinyk struct ieee80211_amrr *amrr; 383f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL; 384b032f27cSSam Leffler int rix; 385b032f27cSSam Leffler 3864367c2d1SAndriy Voskoboinyk /* XXX should return -1 here, but drivers may not expect this... */ 3874367c2d1SAndriy Voskoboinyk if (!amn) 3884367c2d1SAndriy Voskoboinyk { 38938075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, 39038075f7dSAdrian Chadd ni->ni_rates.rs_rates[0]); 3914367c2d1SAndriy Voskoboinyk return 0; 3924367c2d1SAndriy Voskoboinyk } 3934367c2d1SAndriy Voskoboinyk 3944367c2d1SAndriy Voskoboinyk amrr = amn->amn_amrr; 3954367c2d1SAndriy Voskoboinyk 396f8bf74f2SAdrian Chadd /* 11n or not? Pick the right rateset */ 3972014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) { 398f8bf74f2SAdrian Chadd /* XXX ew */ 399f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates; 400f8bf74f2SAdrian Chadd } else { 401f8bf74f2SAdrian Chadd rs = &ni->ni_rates; 402f8bf74f2SAdrian Chadd } 403f8bf74f2SAdrian Chadd 404b032f27cSSam Leffler if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { 405b032f27cSSam Leffler rix = amrr_update(amrr, amn, ni); 406b032f27cSSam Leffler if (rix != amn->amn_rix) { 40738075f7dSAdrian Chadd uint8_t dot11Rate; 408b032f27cSSam Leffler /* update public rate */ 40938075f7dSAdrian Chadd dot11Rate = rs->rs_rates[rix]; 410f8bf74f2SAdrian Chadd /* XXX strip basic rate flag from txrate, if non-11n */ 4112014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) 41238075f7dSAdrian Chadd dot11Rate |= IEEE80211_RATE_MCS; 413f8bf74f2SAdrian Chadd else 41438075f7dSAdrian Chadd dot11Rate &= IEEE80211_RATE_VAL; 41538075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, dot11Rate); 41638075f7dSAdrian Chadd 417b032f27cSSam Leffler amn->amn_rix = rix; 418b032f27cSSam Leffler } 419b032f27cSSam Leffler amn->amn_ticks = ticks; 420b032f27cSSam Leffler } else 421b032f27cSSam Leffler rix = amn->amn_rix; 422b032f27cSSam Leffler return rix; 423b032f27cSSam Leffler } 424b032f27cSSam Leffler 425b6108616SRui Paulo /* 426b6108616SRui Paulo * Update statistics with tx complete status. Ok is non-zero 427b6108616SRui Paulo * if the packet is known to be ACK'd. Retries has the number 428b6108616SRui Paulo * retransmissions (i.e. xmit attempts - 1). 429b6108616SRui Paulo */ 430b6108616SRui Paulo static void 431f6930becSAndriy Voskoboinyk amrr_tx_complete(const struct ieee80211_node *ni, 432f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *status) 433b6108616SRui Paulo { 434b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls; 435f6930becSAndriy Voskoboinyk int retries; 436f6930becSAndriy Voskoboinyk 4374367c2d1SAndriy Voskoboinyk if (!amn) 4384367c2d1SAndriy Voskoboinyk return; 4394367c2d1SAndriy Voskoboinyk 440f6930becSAndriy Voskoboinyk retries = 0; 441f6930becSAndriy Voskoboinyk if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY) 442f6930becSAndriy Voskoboinyk retries = status->long_retries; 443b6108616SRui Paulo 444b6108616SRui Paulo amn->amn_txcnt++; 445f6930becSAndriy Voskoboinyk if (status->status == IEEE80211_RATECTL_TX_SUCCESS) 446b6108616SRui Paulo amn->amn_success++; 447b6108616SRui Paulo amn->amn_retrycnt += retries; 448b6108616SRui Paulo } 449b6108616SRui Paulo 450f6930becSAndriy Voskoboinyk static void 451f6930becSAndriy Voskoboinyk amrr_tx_update_cb(void *arg, struct ieee80211_node *ni) 452f6930becSAndriy Voskoboinyk { 453f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats = arg; 454f6930becSAndriy Voskoboinyk struct ieee80211_amrr_node *amn = ni->ni_rctls; 455f6930becSAndriy Voskoboinyk int txcnt, success, retrycnt; 456f6930becSAndriy Voskoboinyk 4574367c2d1SAndriy Voskoboinyk if (!amn) 4584367c2d1SAndriy Voskoboinyk return; 4594367c2d1SAndriy Voskoboinyk 460f6930becSAndriy Voskoboinyk txcnt = stats->nframes; 461f6930becSAndriy Voskoboinyk success = stats->nsuccess; 462f6930becSAndriy Voskoboinyk retrycnt = 0; 463f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES) 464f6930becSAndriy Voskoboinyk retrycnt = stats->nretries; 465f6930becSAndriy Voskoboinyk 466f6930becSAndriy Voskoboinyk amn->amn_txcnt += txcnt; 467f6930becSAndriy Voskoboinyk amn->amn_success += success; 468f6930becSAndriy Voskoboinyk amn->amn_retrycnt += retrycnt; 469f6930becSAndriy Voskoboinyk } 470f6930becSAndriy Voskoboinyk 471b6108616SRui Paulo /* 472b6108616SRui Paulo * Set tx count/retry statistics explicitly. Intended for 473b6108616SRui Paulo * drivers that poll the device for statistics maintained 474b6108616SRui Paulo * in the device. 475b6108616SRui Paulo */ 476b6108616SRui Paulo static void 477f6930becSAndriy Voskoboinyk amrr_tx_update(struct ieee80211vap *vap, 478f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats) 479b6108616SRui Paulo { 480b6108616SRui Paulo 481f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE) 482f6930becSAndriy Voskoboinyk amrr_tx_update_cb(stats, stats->ni); 483f6930becSAndriy Voskoboinyk else { 484f6930becSAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap, 485f6930becSAndriy Voskoboinyk amrr_tx_update_cb, stats); 486f6930becSAndriy Voskoboinyk } 487b6108616SRui Paulo } 488b6108616SRui Paulo 489b032f27cSSam Leffler static int 490b032f27cSSam Leffler amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) 491b032f27cSSam Leffler { 492b6108616SRui Paulo struct ieee80211vap *vap = arg1; 493b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 4944367c2d1SAndriy Voskoboinyk int msecs, error; 495b032f27cSSam Leffler 4964367c2d1SAndriy Voskoboinyk if (!amrr) 4974367c2d1SAndriy Voskoboinyk return ENOMEM; 4984367c2d1SAndriy Voskoboinyk 4994367c2d1SAndriy Voskoboinyk msecs = ticks_to_msecs(amrr->amrr_interval); 500b032f27cSSam Leffler error = sysctl_handle_int(oidp, &msecs, 0, req); 501b032f27cSSam Leffler if (error || !req->newptr) 502b032f27cSSam Leffler return error; 503b6108616SRui Paulo amrr_setinterval(vap, msecs); 504b032f27cSSam Leffler return 0; 505b032f27cSSam Leffler } 506b032f27cSSam Leffler 507b032f27cSSam Leffler static void 508b6108616SRui Paulo amrr_sysctlattach(struct ieee80211vap *vap, 509b032f27cSSam Leffler struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) 510b032f27cSSam Leffler { 511b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs; 512b032f27cSSam Leffler 5134367c2d1SAndriy Voskoboinyk if (!amrr) 5144367c2d1SAndriy Voskoboinyk return; 5154367c2d1SAndriy Voskoboinyk 516b032f27cSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 51708f5e6bbSPawel Biernacki "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 51808f5e6bbSPawel Biernacki vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); 519b032f27cSSam Leffler /* XXX bounds check values */ 520f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 521b032f27cSSam Leffler "amrr_max_sucess_threshold", CTLFLAG_RW, 522b032f27cSSam Leffler &amrr->amrr_max_success_threshold, 0, ""); 523f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 524b032f27cSSam Leffler "amrr_min_sucess_threshold", CTLFLAG_RW, 525b032f27cSSam Leffler &amrr->amrr_min_success_threshold, 0, ""); 52674828f25SSam Leffler } 52773931706SAdrian Chadd 52873931706SAdrian Chadd static void 529c1475a19SConrad Meyer amrr_print_node_rate(struct ieee80211_amrr_node *amn, 530c1475a19SConrad Meyer struct ieee80211_node *ni, struct sbuf *s) 53173931706SAdrian Chadd { 53273931706SAdrian Chadd int rate; 53373931706SAdrian Chadd struct ieee80211_rateset *rs; 53473931706SAdrian Chadd 5352014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) { 53673931706SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates; 53773931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 53873931706SAdrian Chadd sbuf_printf(s, "rate: MCS %d\n", rate); 53973931706SAdrian Chadd } else { 54073931706SAdrian Chadd rs = &ni->ni_rates; 54173931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; 54273931706SAdrian Chadd sbuf_printf(s, "rate: %d Mbit\n", rate / 2); 54373931706SAdrian Chadd } 544c1475a19SConrad Meyer } 54573931706SAdrian Chadd 546c1475a19SConrad Meyer static void 547c1475a19SConrad Meyer amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) 548c1475a19SConrad Meyer { 549c1475a19SConrad Meyer struct ieee80211_amrr_node *amn = ni->ni_rctls; 550c1475a19SConrad Meyer 551c1475a19SConrad Meyer /* XXX TODO: check locking? */ 552c1475a19SConrad Meyer 553c1475a19SConrad Meyer if (!amn) 554c1475a19SConrad Meyer return; 555c1475a19SConrad Meyer 556c1475a19SConrad Meyer amrr_print_node_rate(amn, ni, s); 55773931706SAdrian Chadd sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); 55873931706SAdrian Chadd sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); 55973931706SAdrian Chadd sbuf_printf(s, "success: %u\n", amn->amn_success); 56073931706SAdrian Chadd sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); 56173931706SAdrian Chadd sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); 56273931706SAdrian Chadd sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); 56373931706SAdrian Chadd } 564