Changeset View
Changeset View
Standalone View
Standalone View
sys/net80211/ieee80211_amrr.c
Show First 20 Lines • Show All 133 Lines • ▼ Show 20 Lines | |||||
amrr_deinit(struct ieee80211vap *vap) | amrr_deinit(struct ieee80211vap *vap) | ||||
{ | { | ||||
IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); | IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL); | ||||
KASSERT(nrefs > 0, ("imbalanced attach/detach")); | KASSERT(nrefs > 0, ("imbalanced attach/detach")); | ||||
nrefs--; /* XXX locking */ | nrefs--; /* XXX locking */ | ||||
} | } | ||||
/* | /* | ||||
* Return whether 11n rates are possible. | * Return whether 11ac rates are possible. | ||||
* | * | ||||
* Some 11n devices may return HT information but no HT rates. | * Some 11ac devices may return VHT information but no VHT rates. | ||||
* Thus, we shouldn't treat them as an 11n node. | * Thus, we shouldn't treat them as an 11ac node. | ||||
*/ | */ | ||||
static int | static int | ||||
amrr_node_is_11ac(struct ieee80211_node *ni) | |||||
{ | |||||
if (ni->ni_chan == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC) | |||||
return (0); | |||||
if (ni->ni_vhtrates.rs_nrates == 0) | |||||
return (0); | |||||
return (IEEE80211_IS_CHAN_VHT(ni->ni_chan)); | |||||
} | |||||
/* | |||||
* The same, but for 11n nodes. | |||||
*/ | |||||
static int | |||||
amrr_node_is_11n(struct ieee80211_node *ni) | amrr_node_is_11n(struct ieee80211_node *ni) | ||||
{ | { | ||||
if (ni->ni_chan == NULL) | if (ni->ni_chan == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC) | ||||
return (0); | return (0); | ||||
if (ni->ni_chan == IEEE80211_CHAN_ANYC) | if (ni->ni_htrates.rs_nrates == 0) | ||||
return (0); | return (0); | ||||
if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0) | |||||
return (0); | |||||
return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); | return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); | ||||
} | } | ||||
static const struct ieee80211_rateset * | |||||
amrr_get_rateset(struct ieee80211_node *ni) | |||||
{ | |||||
/* 11n or not? Pick the right rateset */ | |||||
if (amrr_node_is_11ac(ni)) { | |||||
return ((struct ieee80211_rateset *) &ni->ni_vhtrates); | |||||
} else if (amrr_node_is_11n(ni)) { | |||||
/* XXX ew */ | |||||
return ((struct ieee80211_rateset *) &ni->ni_htrates); | |||||
} else | |||||
return (&ni->ni_rates); | |||||
} | |||||
static __inline char * | |||||
amrr_rate_to_string(struct ieee80211_node *ni, uint16_t rate_index, | |||||
char *buf, int buflen) | |||||
{ | |||||
uint32_t flags = IEEE80211_GET_CHAN_FLAGS(ni->ni_chan); | |||||
/* XXX shortgi? */ | |||||
return (ieee80211_rate_to_string(rate_index, flags, 0, buf, buflen)); | |||||
} | |||||
static void | static void | ||||
amrr_node_init(struct ieee80211_node *ni) | amrr_node_init(struct ieee80211_node *ni) | ||||
{ | { | ||||
const struct ieee80211_rateset *rs = NULL; | const struct ieee80211_rateset *rs = NULL; | ||||
const struct ieee80211_rate_t *rate; | |||||
struct ieee80211vap *vap = ni->ni_vap; | struct ieee80211vap *vap = ni->ni_vap; | ||||
struct ieee80211_amrr *amrr = vap->iv_rs; | struct ieee80211_amrr *amrr = vap->iv_rs; | ||||
struct ieee80211_amrr_node *amn; | struct ieee80211_amrr_node *amn; | ||||
uint8_t rate; | uint16_t rate_index, max_rate_index; | ||||
uint8_t max_rate_value; | |||||
#ifdef IEEE80211_DEBUG | |||||
char buf[40]; | |||||
#endif | |||||
int rix; | |||||
if (ni->ni_rctls == NULL) { | if (ni->ni_rctls == NULL) { | ||||
ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), | ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node), | ||||
M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); | M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); | ||||
if (amn == NULL) { | if (amn == NULL) { | ||||
if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " | if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " | ||||
"structure\n"); | "structure\n"); | ||||
return; | return; | ||||
} | } | ||||
} else | } else | ||||
amn = ni->ni_rctls; | amn = ni->ni_rctls; | ||||
amn->amn_amrr = amrr; | amn->amn_amrr = amrr; | ||||
amn->amn_success = 0; | amn->amn_success = 0; | ||||
amn->amn_recovery = 0; | amn->amn_recovery = 0; | ||||
amn->amn_txcnt = amn->amn_retrycnt = 0; | amn->amn_txcnt = amn->amn_retrycnt = 0; | ||||
amn->amn_success_threshold = amrr->amrr_min_success_threshold; | amn->amn_success_threshold = amrr->amrr_min_success_threshold; | ||||
/* 11n or not? Pick the right rateset */ | /* Select max rate value */ | ||||
if (amrr_node_is_11n(ni)) { | if (amrr_node_is_11ac(ni)) { | ||||
/* XXX ew */ | /* 11ac - stop at MCS4 */ | ||||
max_rate_index = IEEE80211_RATE_INDEX_VHT(3, 0); | |||||
} else if (amrr_node_is_11n(ni)) { | |||||
/* 11n - stop at MCS4 */ | |||||
max_rate_index = IEEE80211_RATE_INDEX_HT(3); | |||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | ||||
"%s: 11n node", __func__); | "%s: 11n node", __func__); | ||||
rs = (struct ieee80211_rateset *) &ni->ni_htrates; | |||||
} else { | } else { | ||||
max_rate_index = IEEE80211_RATE_NONEXISTENT; | |||||
max_rate_value = 72; /* legacy - anything < 36mbit */ | |||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | ||||
"%s: non-11n node", __func__); | "%s: non-11n node", __func__); | ||||
rs = &ni->ni_rates; | |||||
} | } | ||||
/* Initial rate - lowest */ | rs = amrr_get_rateset(ni); | ||||
rate = rs->rs_rates[0]; | if (__predict_false(rs->rs_nrates == 0)) { | ||||
if_printf(vap->iv_ifp, "%s: no rates available!\n", __func__); | |||||
return; | |||||
} | |||||
/* XXX clear the basic rate flag if it's not 11n */ | |||||
if (! amrr_node_is_11n(ni)) | |||||
rate &= IEEE80211_RATE_VAL; | |||||
/* pick initial rate from the rateset - HT or otherwise */ | |||||
/* Pick something low that's likely to succeed */ | /* Pick something low that's likely to succeed */ | ||||
for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; | for (rix = rs->rs_nrates - 1; rix > 0; rix--) { | ||||
amn->amn_rix--) { | rate_index = rs->rates[rix].rs_index; | ||||
/* legacy - anything < 36mbit, stop searching */ | if (max_rate_index != IEEE80211_RATE_NONEXISTENT) { | ||||
/* 11n - stop at MCS4 */ | if (rate_index <= max_rate_index) | ||||
if (amrr_node_is_11n(ni)) { | |||||
if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) | |||||
break; | break; | ||||
} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) | } else { | ||||
rate = ieee80211_get_rate(rate_index); | |||||
if (rate->value <= max_rate_value) | |||||
break; | break; | ||||
} | } | ||||
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; | } | ||||
/* if the rate is an 11n rate, ensure the MCS bit is set */ | |||||
if (amrr_node_is_11n(ni)) | |||||
rate |= IEEE80211_RATE_MCS; | |||||
/* Assign initial rate from the rateset */ | /* Assign initial rate from the rateset */ | ||||
ni->ni_txrate = rate; | ni->ni_txrate = rs->rates[rix].rs_index; | ||||
amn->amn_ticks = ticks; | amn->amn_ticks = ticks; | ||||
amn->amn_rix = rix; | |||||
/* XXX TODO: we really need a rate-to-string method */ | |||||
/* XXX TODO: non-11n rate should be divided by two.. */ | |||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | ||||
"AMRR: nrates=%d, initial rate %s%d", | "AMRR: nrates=%d, initial rate: %s", rs->rs_nrates, | ||||
rs->rs_nrates, | amrr_rate_to_string(ni, ni->ni_txrate, buf, sizeof(buf))); | ||||
amrr_node_is_11n(ni) ? "MCS " : "", | |||||
rate & IEEE80211_RATE_VAL); | |||||
} | } | ||||
static void | static void | ||||
amrr_node_deinit(struct ieee80211_node *ni) | amrr_node_deinit(struct ieee80211_node *ni) | ||||
{ | { | ||||
IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); | IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL); | ||||
} | } | ||||
static int | static int | ||||
amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, | amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, | ||||
struct ieee80211_node *ni) | struct ieee80211_node *ni) | ||||
{ | { | ||||
int rix = amn->amn_rix; | int rix = amn->amn_rix; | ||||
const struct ieee80211_rateset *rs = NULL; | const struct ieee80211_rateset *rs; | ||||
#ifdef IEEE80211_DEBUG | |||||
uint16_t rate_index; | |||||
char buf[40]; | |||||
#endif | |||||
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); | KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); | ||||
/* 11n or not? Pick the right rateset */ | rs = amrr_get_rateset(ni); | ||||
if (amrr_node_is_11n(ni)) { | |||||
/* XXX ew */ | |||||
rs = (struct ieee80211_rateset *) &ni->ni_htrates; | |||||
} else { | |||||
rs = &ni->ni_rates; | |||||
} | |||||
/* XXX TODO: we really need a rate-to-string method */ | #ifdef IEEE80211_DEBUG | ||||
/* XXX TODO: non-11n rate should be divided by two.. */ | rate_index = rs->rates[rix].rs_index; | ||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | ||||
"AMRR: current rate %d, txcnt=%d, retrycnt=%d", | "AMRR: current rate: %s, txcnt=%d, retrycnt=%d", | ||||
rs->rs_rates[rix] & IEEE80211_RATE_VAL, | amrr_rate_to_string(ni, rate_index, buf, sizeof(buf)), | ||||
amn->amn_txcnt, | amn->amn_txcnt, amn->amn_retrycnt); | ||||
amn->amn_retrycnt); | #endif | ||||
/* | /* | ||||
* XXX This is totally bogus for 11n, as although high MCS | * XXX This is totally bogus for 11n, as although high MCS | ||||
* rates for each stream may be failing, the next stream | * rates for each stream may be failing, the next stream | ||||
* should be checked. | * should be checked. | ||||
* | * | ||||
* Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to | * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to | ||||
* MCS23, we should skip 6/7 and try 8 onwards. | * MCS23, we should skip 6/7 and try 8 onwards. | ||||
* | |||||
* XXX compare via *get_rateKbps() instead? | |||||
*/ | */ | ||||
if (is_success(amn)) { | if (is_success(amn)) { | ||||
amn->amn_success++; | amn->amn_success++; | ||||
if (amn->amn_success >= amn->amn_success_threshold && | if (amn->amn_success >= amn->amn_success_threshold && | ||||
rix + 1 < rs->rs_nrates) { | rix + 1 < rs->rs_nrates) { | ||||
amn->amn_recovery = 1; | amn->amn_recovery = 1; | ||||
amn->amn_success = 0; | amn->amn_success = 0; | ||||
rix++; | rix++; | ||||
/* XXX TODO: we really need a rate-to-string method */ | |||||
/* XXX TODO: non-11n rate should be divided by two.. */ | #ifdef IEEE80211_DEBUG | ||||
rate_index = rs->rates[rix].rs_index; | |||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | ||||
"AMRR increasing rate %d (txcnt=%d retrycnt=%d)", | "AMRR: increasing rate: %s (txcnt=%d retrycnt=%d)", | ||||
rs->rs_rates[rix] & IEEE80211_RATE_VAL, | amrr_rate_to_string(ni, rate_index, buf, | ||||
sizeof(buf)), | |||||
amn->amn_txcnt, amn->amn_retrycnt); | amn->amn_txcnt, amn->amn_retrycnt); | ||||
#endif | |||||
} else { | } else { | ||||
amn->amn_recovery = 0; | amn->amn_recovery = 0; | ||||
} | } | ||||
} else if (is_failure(amn)) { | } else if (is_failure(amn)) { | ||||
amn->amn_success = 0; | amn->amn_success = 0; | ||||
if (rix > 0) { | if (rix > 0) { | ||||
if (amn->amn_recovery) { | if (amn->amn_recovery) { | ||||
amn->amn_success_threshold *= 2; | amn->amn_success_threshold *= 2; | ||||
if (amn->amn_success_threshold > | if (amn->amn_success_threshold > | ||||
amrr->amrr_max_success_threshold) | amrr->amrr_max_success_threshold) | ||||
amn->amn_success_threshold = | amn->amn_success_threshold = | ||||
amrr->amrr_max_success_threshold; | amrr->amrr_max_success_threshold; | ||||
} else { | } else { | ||||
amn->amn_success_threshold = | amn->amn_success_threshold = | ||||
amrr->amrr_min_success_threshold; | amrr->amrr_min_success_threshold; | ||||
} | } | ||||
rix--; | rix--; | ||||
/* XXX TODO: we really need a rate-to-string method */ | |||||
/* XXX TODO: non-11n rate should be divided by two.. */ | #ifdef IEEE80211_DEBUG | ||||
rate_index = rs->rates[rix].rs_index; | |||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, | ||||
"AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", | "AMRR: decreasing rate: %s (txcnt=%d retrycnt=%d)", | ||||
rs->rs_rates[rix] & IEEE80211_RATE_VAL, | amrr_rate_to_string(ni, rate_index, buf, | ||||
sizeof(buf)), | |||||
amn->amn_txcnt, amn->amn_retrycnt); | amn->amn_txcnt, amn->amn_retrycnt); | ||||
#endif | |||||
} | } | ||||
amn->amn_recovery = 0; | amn->amn_recovery = 0; | ||||
} | } | ||||
/* reset counters */ | /* reset counters */ | ||||
amn->amn_txcnt = 0; | amn->amn_txcnt = 0; | ||||
amn->amn_retrycnt = 0; | amn->amn_retrycnt = 0; | ||||
return rix; | return rix; | ||||
} | } | ||||
/* | /* | ||||
* Return the rate index to use in sending a data frame. | * Return the rate index to use in sending a data frame. | ||||
* Update our internal state if it's been long enough. | * Update our internal state if it's been long enough. | ||||
* If the rate changes we also update ni_txrate to match. | * If the rate changes we also update ni_txrate to match. | ||||
*/ | */ | ||||
static int | static int | ||||
amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) | amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) | ||||
{ | { | ||||
struct ieee80211_amrr_node *amn = ni->ni_rctls; | struct ieee80211_amrr_node *amn = ni->ni_rctls; | ||||
struct ieee80211_amrr *amrr = amn->amn_amrr; | struct ieee80211_amrr *amrr = amn->amn_amrr; | ||||
const struct ieee80211_rateset *rs = NULL; | const struct ieee80211_rateset *rs; | ||||
int rix; | int rix; | ||||
/* 11n or not? Pick the right rateset */ | rs = amrr_get_rateset(ni); | ||||
if (amrr_node_is_11n(ni)) { | |||||
/* XXX ew */ | |||||
rs = (struct ieee80211_rateset *) &ni->ni_htrates; | |||||
} else { | |||||
rs = &ni->ni_rates; | |||||
} | |||||
if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { | if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { | ||||
rix = amrr_update(amrr, amn, ni); | rix = amrr_update(amrr, amn, ni); | ||||
if (rix != amn->amn_rix) { | if (rix != amn->amn_rix) { | ||||
/* update public rate */ | /* update public rate */ | ||||
ni->ni_txrate = rs->rs_rates[rix]; | ni->ni_txrate = rs->rates[rix].rs_index; | ||||
/* XXX strip basic rate flag from txrate, if non-11n */ | |||||
if (amrr_node_is_11n(ni)) | |||||
ni->ni_txrate |= IEEE80211_RATE_MCS; | |||||
else | |||||
ni->ni_txrate &= IEEE80211_RATE_VAL; | |||||
amn->amn_rix = rix; | amn->amn_rix = rix; | ||||
} | } | ||||
amn->amn_ticks = ticks; | amn->amn_ticks = ticks; | ||||
} else | } else | ||||
rix = amn->amn_rix; | rix = amn->amn_rix; | ||||
return rix; | return rix; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct ieee80211_amrr *amrr = vap->iv_rs; | struct ieee80211_amrr *amrr = vap->iv_rs; | ||||
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | ||||
"amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, | "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap, | ||||
0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); | 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)"); | ||||
/* XXX bounds check values */ | /* XXX bounds check values */ | ||||
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | ||||
"amrr_max_sucess_threshold", CTLFLAG_RW, | "amrr_max_success_threshold", CTLFLAG_RW, | ||||
&amrr->amrr_max_success_threshold, 0, ""); | &amrr->amrr_max_success_threshold, 0, ""); | ||||
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | ||||
"amrr_min_sucess_threshold", CTLFLAG_RW, | "amrr_min_success_threshold", CTLFLAG_RW, | ||||
&amrr->amrr_min_success_threshold, 0, ""); | &amrr->amrr_min_success_threshold, 0, ""); | ||||
} | } | ||||
static void | static void | ||||
amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) | amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s) | ||||
{ | { | ||||
int rate; | |||||
struct ieee80211_amrr_node *amn = ni->ni_rctls; | struct ieee80211_amrr_node *amn = ni->ni_rctls; | ||||
struct ieee80211_rateset *rs; | const struct ieee80211_rateset *rs; | ||||
uint16_t rate_index; | |||||
char buf[40]; | |||||
/* XXX TODO: check locking? */ | /* XXX TODO: check locking? */ | ||||
/* XXX TODO: this should be a method */ | rs = amrr_get_rateset(ni); | ||||
if (amrr_node_is_11n(ni)) { | rate_index = rs->rates[amn->amn_rix].rs_index; | ||||
rs = (struct ieee80211_rateset *) &ni->ni_htrates; | |||||
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; | |||||
sbuf_printf(s, "rate: MCS %d\n", rate); | |||||
} else { | |||||
rs = &ni->ni_rates; | |||||
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; | |||||
sbuf_printf(s, "rate: %d Mbit\n", rate / 2); | |||||
} | |||||
sbuf_printf(s, "rate: %s\n", | |||||
amrr_rate_to_string(ni, rate_index, buf, sizeof(buf))); | |||||
sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); | sbuf_printf(s, "ticks: %d\n", amn->amn_ticks); | ||||
sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); | sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt); | ||||
sbuf_printf(s, "success: %u\n", amn->amn_success); | sbuf_printf(s, "success: %u\n", amn->amn_success); | ||||
sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); | sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold); | ||||
sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); | sbuf_printf(s, "recovery: %u\n", amn->amn_recovery); | ||||
sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); | sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt); | ||||
} | } |