diff --git a/sys/net80211/ieee80211_amrr.h b/sys/net80211/ieee80211_amrr.h --- a/sys/net80211/ieee80211_amrr.h +++ b/sys/net80211/ieee80211_amrr.h @@ -49,6 +49,10 @@ struct ieee80211_amrr *amn_amrr;/* backpointer */ int amn_rix; /* current rate index */ int amn_ticks; /* time of last update */ + /* for VHT, since there's no VHT RIX right now */ + int amn_vht_mcs; + int amn_vht_nss; + /* statistics */ u_int amn_txcnt; u_int amn_success; diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -49,6 +49,7 @@ #include #include +#include #include #include @@ -138,6 +139,24 @@ nrefs--; /* XXX locking */ } +static void +amrr_node_init_vht(struct ieee80211_node *ni) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + + /* Default to VHT NSS 1 MCS 2; should be reliable! */ + amn->amn_vht_mcs = 2; + amn->amn_vht_nss = 1; + ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss, + amn->amn_vht_mcs); + amn->amn_ticks = ticks; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR: VHT: initial rate NSS %d MCS %d", + amn->amn_vht_nss, + amn->amn_vht_mcs); +} + static void amrr_node_init_ht(struct ieee80211_node *ni) { @@ -241,6 +260,8 @@ /* 11n or not? Pick the right rateset */ if (ieee80211_ht_check_tx_ht(ni)) + amrr_node_init_vht(ni); + else if (ieee80211_ht_check_tx_ht(ni)) amrr_node_init_ht(ni); else amrr_node_init_legacy(ni); @@ -252,6 +273,139 @@ IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); } +static void +amrr_update_vht_inc(struct ieee80211_node *ni) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + uint8_t nss, mcs; + + /* + * For now just keep looping over MCS to 9, + * then NSS up, until we hit max. This at least + * tests the VHT MCS rates, but definitely is + * suboptimal (in the same way the 11n MCS selection + * is suboptimal.) + */ + nss = amn->amn_vht_nss; + mcs = amn->amn_vht_mcs; + + while (nss <= 8 && mcs <= 9) { + /* Increment MCS 0..9, NSS 1..8 */ + if (mcs == 9) { + mcs = 0; + nss++; + } else + mcs++; + if (nss > 8) + break; + + if (ieee80211_vht_node_check_valid_mcs(ni, ni->ni_chw, nss, + mcs)) { + amn->amn_vht_nss = nss; + amn->amn_vht_mcs = mcs; + break; + } + } +} + +static void +amrr_update_vht_dec(struct ieee80211_node *ni) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + uint8_t nss, mcs; + + /* + * For now just keep looping over MCS 9 .. 0 + * then NSS down, until we hit min. This at least + * tests the VHT MCS rates, but definitely is + * suboptimal (in the same way the 11n MCS selection + * is suboptimal.) + */ + nss = amn->amn_vht_nss; + mcs = amn->amn_vht_mcs; + + while (nss >= 1 && mcs >= 0) { + + if (mcs == 0) { + mcs = 9; + nss--; + } else + mcs--; + if (nss < 1) + break; + + if (ieee80211_vht_node_check_valid_mcs(ni, ni->ni_chw, nss, + mcs)) { + amn->amn_vht_nss = nss; + amn->amn_vht_mcs = mcs; + break; + } + } +} + +/* + * A placeholder / temporary hack VHT rate control. + * + * Use the available MCS rates at the current node bandwidth + * and configured / negotiated MCS rates. + */ +static int +amrr_update_vht(struct ieee80211_node *ni) +{ + struct ieee80211_amrr_node *amn = ni->ni_rctls; + struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d", + amn->amn_vht_nss, + amn->amn_vht_mcs, + amn->amn_txcnt, + amn->amn_retrycnt); + + if (is_success(amn)) { + amn->amn_success++; + if (amn->amn_success >= amn->amn_success_threshold) { + amn->amn_recovery = 1; + amn->amn_success = 0; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)", + amn->amn_txcnt, amn->amn_retrycnt); + + amrr_update_vht_inc(ni); + } else { + amn->amn_recovery = 0; + } + } else if (is_failure(amn)) { + amn->amn_success = 0; + + if (amn->amn_recovery) { + amn->amn_success_threshold *= 2; + if (amn->amn_success_threshold > + amrr->amrr_max_success_threshold) + amn->amn_success_threshold = + amrr->amrr_max_success_threshold; + } else { + amn->amn_success_threshold = + amrr->amrr_min_success_threshold; + } + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)", + amn->amn_txcnt, amn->amn_retrycnt); + + amrr_update_vht_dec(ni); + + amn->amn_recovery = 0; + } + + /* reset counters */ + amn->amn_txcnt = 0; + amn->amn_retrycnt = 0; + + /* Return 0, not useful anymore */ + return (0); +} + static int amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, struct ieee80211_node *ni) @@ -385,7 +539,9 @@ KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); /* 11n or not? Pick the right rateset */ - if (ieee80211_ht_check_tx_ht(ni)) + if (ieee80211_vht_check_tx_vht(ni)) + rix = amrr_update_vht(ni); + else if (ieee80211_ht_check_tx_ht(ni)) rix = amrr_update_ht(amrr, amn, ni); else rix = amrr_update_legacy(amrr, amn, ni); @@ -397,6 +553,23 @@ return (rix); } +static int +amrr_rate_vht(struct ieee80211_node *ni) +{ + struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs; + struct ieee80211_amrr_node *amn = ni->ni_rctls; + + if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { + amrr_update_vht(ni); + } + + ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss, + amn->amn_vht_mcs); + + /* There's no need for it anymore */ + return (0); +} + /* * Return the rate index to use in sending a data frame. * Update our internal state if it's been long enough. @@ -418,6 +591,9 @@ return 0; } + if (ieee80211_vht_check_tx_vht(ni)) + return amrr_rate_vht(ni); + amrr = amn->amn_amrr; /* 11n or not? Pick the right rateset */