1
0
mirror of https://github.com/OpenIntelWireless/itlwm.git synced 2025-05-12 12:22:46 +00:00
Files
itlwm/itl80211/openbsd/net80211/ieee80211_mira.c

1314 lines
46 KiB
C

/*
* Copyright (C) 2020 钟先耀
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* $OpenBSD: ieee80211_mira.c,v 1.30 2020/05/01 14:04:17 stsp Exp $ */
/*
* Copyright (c) 2016 Stefan Sperling <stsp@openbsd.org>
* Copyright (c) 2016 Theo Buehler <tb@openbsd.org>
* Copyright (c) 2006 Damien Bergamini <damien.bergamini@free.fr>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <sys/_arc4random.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_mira.h>
/* Allow for aggressive down probing when channel quality changes. */
#define MIRA_AGGRESSIVE_DOWNWARDS_PROBING
const struct ieee80211_ht_rateset * ieee80211_mira_get_rateset(int, int, bool);
void ieee80211_mira_probe_timeout_up(void *);
void ieee80211_mira_probe_timeout_down(void *);
uint64_t ieee80211_mira_get_txrate(int, int, bool is_40mhz);
uint16_t ieee80211_mira_legacy_txtime(uint32_t, int, struct ieee80211com *);
uint32_t ieee80211_mira_ht_txtime(uint32_t, int, int, int, bool is_40mhz);
int ieee80211_mira_best_basic_rate(struct ieee80211_node *);
int ieee80211_mira_ack_rate(struct ieee80211_node *);
uint64_t ieee80211_mira_toverhead(struct ieee80211_mira_node *,
struct ieee80211com *, struct ieee80211_node *);
void ieee80211_mira_update_stats(struct ieee80211_mira_node *,
struct ieee80211com *, struct ieee80211_node *);
void ieee80211_mira_reset_goodput_stats(struct ieee80211_mira_node *);
void ieee80211_mira_reset_driver_stats(struct ieee80211_mira_node *);
int ieee80211_mira_next_lower_intra_rate(struct ieee80211_mira_node *,
struct ieee80211_node *);
int ieee80211_mira_next_intra_rate(struct ieee80211_mira_node *,
struct ieee80211_node *);
const struct ieee80211_ht_rateset * ieee80211_mira_next_rateset(
struct ieee80211_mira_node *, struct ieee80211_node *);
int ieee80211_mira_best_mcs_in_rateset(struct ieee80211_mira_node *,
const struct ieee80211_ht_rateset *);
void ieee80211_mira_probe_next_rateset(struct ieee80211_mira_node *,
struct ieee80211_node *, const struct ieee80211_ht_rateset *);
int ieee80211_mira_next_mcs(struct ieee80211_mira_node *,
struct ieee80211_node *);
int ieee80211_mira_prev_mcs(struct ieee80211_mira_node *,
struct ieee80211_node *);
int ieee80211_mira_probe_valid(struct ieee80211_mira_node *,
struct ieee80211_node *);
void ieee80211_mira_probe_done(struct ieee80211_mira_node *);
int ieee80211_mira_intra_mode_ra_finished(
struct ieee80211_mira_node *, struct ieee80211_node *);
void ieee80211_mira_trigger_next_rateset(struct ieee80211_mira_node *mn,
struct ieee80211_node *);
int ieee80211_mira_inter_mode_ra_finished(
struct ieee80211_mira_node *, struct ieee80211_node *);
int ieee80211_mira_best_rate(struct ieee80211_mira_node *,
struct ieee80211_node *);
void ieee80211_mira_update_probe_interval(
struct ieee80211_mira_goodput_stats *);
void ieee80211_mira_schedule_probe_timers(struct ieee80211_mira_node *,
struct ieee80211_node *);
int ieee80211_mira_check_probe_timers(struct ieee80211_mira_node *,
struct ieee80211_node *);
void ieee80211_mira_probe_next_rate(struct ieee80211_mira_node *,
struct ieee80211_node *);
int ieee80211_mira_valid_tx_mcs(struct ieee80211com *, int);
uint32_t ieee80211_mira_valid_rates(struct ieee80211com *,
struct ieee80211_node *);
uint32_t ieee80211_mira_mcs_below(struct ieee80211_mira_node *, int, int, bool);
void ieee80211_mira_set_rts_threshold(struct ieee80211_mira_node *,
struct ieee80211com *, struct ieee80211_node *);
void ieee80211_mira_reset_collision_stats(struct ieee80211_mira_node *);
/* We use fixed point arithmetic with 64 bit integers. */
#define MIRA_FP_SHIFT 21
#define MIRA_FP_INT(x) (x ## ULL << MIRA_FP_SHIFT) /* the integer x */
#define MIRA_FP_1 MIRA_FP_INT(1)
/* Multiply two fixed point numbers. */
#define MIRA_FP_MUL(a, b) \
(((a) * (b)) >> MIRA_FP_SHIFT)
/* Divide two fixed point numbers. */
#define MIRA_FP_DIV(a, b) \
(b == 0 ? (uint64_t)-1 : (((a) << MIRA_FP_SHIFT) / (b)))
#ifdef MIRA_DEBUG
#define DPRINTF(x) do { if (mira_debug > 0) printf x; } while (0)
#define DPRINTFN(n, x) do { if (mira_debug >= (n)) printf x; } while (0)
int mira_debug = 1;
#else
#define DPRINTF(x) do { ; } while (0)
#define DPRINTFN(n, x) do { ; } while (0)
#endif
#ifdef MIRA_DEBUG
void
mira_fixedp_split(uint32_t *i, uint32_t *f, uint64_t fp)
{
uint64_t tmp;
/* integer part */
*i = (fp >> MIRA_FP_SHIFT);
/* fractional part */
tmp = (fp & ((uint64_t)-1 >> (64 - MIRA_FP_SHIFT)));
tmp *= 100;
*f = (uint32_t)(tmp >> MIRA_FP_SHIFT);
}
char *
mira_fp_sprintf(uint64_t fp)
{
uint32_t i, f;
static char buf[64];
int ret;
mira_fixedp_split(&i, &f, fp);
ret = snprintf(buf, sizeof(buf), "%u.%02u", i, f);
if (ret == -1 || ret >= sizeof(buf))
return "ERR";
return buf;
}
void
mira_print_driver_stats(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni) {
DPRINTF(("%s driver stats:\n", ether_sprintf(ni->ni_macaddr)));
DPRINTF(("mn->frames = %u\n", mn->frames));
DPRINTF(("mn->retries = %u\n", mn->retries));
DPRINTF(("mn->txfail = %u\n", mn->txfail));
DPRINTF(("mn->ampdu_size = %u\n", mn->ampdu_size));
DPRINTF(("mn->agglen = %u\n", mn->agglen));
}
#endif /* MIRA_DEBUG */
const struct ieee80211_ht_rateset *
ieee80211_mira_get_rateset(int mcs, int sgi, bool is_40mhz)
{
const struct ieee80211_ht_rateset *rs;
int i;
int first = 0;
int last = IEEE80211_HT_RATESET_MIMO4_SGI;
if (is_40mhz) {
first = IEEE80211_HT_RATESET_CBW40_SISO;
last = IEEE80211_HT_RATESET_CBW40_MIMO4_SGI;
}
for (i = first; i <= last; i++) {
rs = &ieee80211_std_ratesets_11n[i];
if (sgi != rs->sgi)
continue;
if (mcs >= rs->min_mcs && mcs <= rs->max_mcs)
return rs;
}
panic("MCS %d is not part of any rateset", mcs);
return NULL;
}
/*
* Probe timers.
*/
/* Constants related to timeouts for time-driven rate probing. */
#define IEEE80211_MIRA_PROBE_TIMEOUT_MIN 2 /* in msec */
#define IEEE80211_MIRA_PROBE_INTVAL_MAX (1 << 10) /* 2^10 */
void
ieee80211_mira_probe_timeout_up(void *arg)
{
struct ieee80211_mira_node *mn = (struct ieee80211_mira_node *)arg;
int s;
s = splnet();
mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_UP] = 1;
DPRINTFN(3, ("probe up timeout fired\n"));
splx(s);
}
void
ieee80211_mira_probe_timeout_down(void *arg)
{
struct ieee80211_mira_node *mn = (struct ieee80211_mira_node *)arg;
int s;
s = splnet();
mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_DOWN] = 1;
DPRINTFN(3, ("probe down timeout fired\n"));
splx(s);
}
/*
* Update goodput statistics.
*/
uint64_t
ieee80211_mira_get_txrate(int mcs, int sgi, bool is_40mhz)
{
const struct ieee80211_ht_rateset *rs;
uint64_t txrate;
rs = ieee80211_mira_get_rateset(mcs, sgi, is_40mhz);
txrate = rs->rates[mcs - rs->min_mcs];
txrate <<= MIRA_FP_SHIFT; /* convert to fixed-point */
txrate *= 500; /* convert to kbit/s */
txrate /= 1000; /* convert to mbit/s */
return txrate;
}
/* Based on rt2661_txtime in the ral(4) driver. */
uint16_t
ieee80211_mira_legacy_txtime(uint32_t len, int rate, struct ieee80211com *ic)
{
#define MIRA_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
uint16_t txtime;
if (MIRA_RATE_IS_OFDM(rate)) {
/* IEEE Std 802.11g-2003, pp. 44 */
txtime = (8 + 4 * len + 3 + rate - 1) / rate;
txtime = 16 + 4 + 4 * txtime + 6;
} else {
/* IEEE Std 802.11b-1999, pp. 28 */
txtime = (16 * len + rate - 1) / rate;
if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
txtime += 72 + 24;
else
txtime += 144 + 48;
}
return txtime;
}
uint32_t
ieee80211_mira_ht_txtime(uint32_t len, int mcs, int is2ghz, int sgi, bool is_40mhz)
{
const struct ieee80211_ht_rateset *rs;
/* XXX These constants should be macros in ieee80211.h instead. */
const uint32_t t_lstf = 8; /* usec legacy short training field */
const uint32_t t_lltf = 8; /* usec legacy long training field */
const uint32_t t_lsig = 4; /* usec legacy signal field */
const uint32_t t_htstf = 4; /* usec HT short training field */
const uint32_t t_ltstf = 4; /* usec HT long training field */
const uint32_t t_htsig = 8; /* usec HT signal field */
const uint32_t t_sym = 4; /* usec symbol interval */
const uint32_t t_syms_n = 36; /* usec symbol interval for sgi numerator */
const uint32_t t_syms_d = 10; /* usec symbol interval for sgi denominator */
uint32_t n_sym, n_dbps;
uint32_t t_plcp;
uint32_t t_data;
uint32_t txtime;
/*
* Calculate approximate frame Tx time in usec.
* See 802.11-2012, 20.4.3 "TXTIME calculation" and
* 20.3.11.1 "Equation (20-32)".
* XXX Assumes a 20MHz channel, HT-mixed frame format, no STBC.
*/
t_plcp = t_lstf + t_lltf + t_lsig + t_htstf + 4 * t_ltstf + t_htsig;
rs = ieee80211_mira_get_rateset(mcs, sgi, is_40mhz);
n_dbps = rs->rates[mcs - rs->min_mcs] * 2;
n_sym = ((8 * len + 16 + 6) / n_dbps); /* "Equation (20-32)" */
if (sgi)
t_data = (t_syms_n * n_sym) / t_syms_d;
else
t_data = t_sym * n_sym;
txtime = t_plcp + t_data;
if (is2ghz)
txtime += 6; /* aSignalExtension */
return txtime;
}
int
ieee80211_mira_best_basic_rate(struct ieee80211_node *ni)
{
struct ieee80211_rateset *rs = &ni->ni_rates;
int i, best, rval;
/* Default to 1 Mbit/s on 2GHz and 6 Mbit/s on 5GHz. */
best = IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ? 2 : 12;
for (i = 0; i < rs->rs_nrates; i++) {
if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0)
continue;
rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL);
if (rval > best)
best = rval;
}
return best;
}
/*
* See 802.11-2012, 9.7.6.5 "Rate selection for control response frames".
*/
int
ieee80211_mira_ack_rate(struct ieee80211_node *ni)
{
/*
* Assume the ACK was sent at a mandatory ERP OFDM rate.
* In the worst case, the driver has retried at non-HT rates,
* so for MCS 0 assume we didn't actually send an OFDM frame
* and ACKs arrived at a basic rate.
*/
if (ni->ni_txmcs == 0)
return ieee80211_mira_best_basic_rate(ni);
else if (ni->ni_txmcs == 1)
return 12; /* 6 Mbit/s */
else if (ni->ni_txmcs >= 2)
return 24; /* 12 Mbit/s */
else
return 48; /* 24 Mbit/s */
}
uint64_t
ieee80211_mira_toverhead(struct ieee80211_mira_node *mn,
struct ieee80211com *ic, struct ieee80211_node *ni)
{
/* XXX These should be macros in ieee80211.h. */
#define MIRA_RTSLEN IEEE80211_MIN_LEN
#define MIRA_CTSLEN (sizeof(struct ieee80211_frame_cts) + IEEE80211_CRC_LEN)
uint32_t overhead;
uint64_t toverhead;
int rate, rts;
enum ieee80211_htprot htprot;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
overhead = ieee80211_mira_ht_txtime(0, ni->ni_txmcs,
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan), sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
htprot = (enum ieee80211_htprot)(ic->ic_bss->ni_htop1 & IEEE80211_HTOP1_PROT_MASK);
if (htprot == IEEE80211_HTPROT_NONMEMBER ||
htprot == IEEE80211_HTPROT_NONHT_MIXED)
rts = 1;
else if (htprot == IEEE80211_HTPROT_20MHZ &&
(ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40))
rts = 1;
else
rts = (mn->ampdu_size > ieee80211_mira_get_rts_threshold(mn,
ic, ni, mn->ampdu_size));
if (rts) {
/* Assume RTS/CTS were sent at a basic rate. */
rate = ieee80211_min_basic_rate(ic);
overhead += ieee80211_mira_legacy_txtime(MIRA_RTSLEN, rate, ic);
overhead += ieee80211_mira_legacy_txtime(MIRA_CTSLEN, rate, ic);
}
if (mn->agglen == 1) {
/* Single-frame transmissions must wait for an ACK frame. */
rate = ieee80211_mira_ack_rate(ni);
overhead += ieee80211_mira_legacy_txtime(IEEE80211_ACK_LEN,
rate, ic);
}
toverhead = overhead;
toverhead <<= MIRA_FP_SHIFT; /* convert to fixed-point */
toverhead /= 1000; /* convert to msec */
toverhead /= 1000; /* convert to sec */
#ifdef MIRA_DEBUG
if (mira_debug > 3) {
uint32_t txtime;
txtime = ieee80211_mira_ht_txtime(mn->ampdu_size, ni->ni_txmcs,
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan), sgi, ni->ni_chw == 40);
txtime += overhead - ieee80211_mira_ht_txtime(0, ni->ni_txmcs,
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan), sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
DPRINTFN(4, ("txtime: %u usec\n", txtime));
DPRINTFN(4, ("overhead: %u usec\n", overhead));
DPRINTFN(4, ("toverhead: %s\n", mira_fp_sprintf(toverhead)));
}
#endif
return toverhead;
}
void
ieee80211_mira_update_stats(struct ieee80211_mira_node *mn,
struct ieee80211com *ic, struct ieee80211_node *ni)
{
/* Magic numbers from MiRA paper. */
static const uint64_t alpha = MIRA_FP_1 / 8; /* 1/8 = 0.125 */
static const uint64_t beta = MIRA_FP_1 / 4; /* 1/4 = 0.25 */
uint64_t sfer, delta, toverhead;
uint64_t agglen = mn->agglen;
uint64_t ampdu_size = mn->ampdu_size * 8; /* convert to bits */
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
uint64_t rate = ieee80211_mira_get_txrate(ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
struct ieee80211_mira_goodput_stats *g = &mn->g[ni->ni_txmcs];
if (mn->frames == 0)
return; /* avoid divide-by-zero in sfer calculation below */
g->nprobes += mn->agglen;
g->nprobe_bytes += mn->ampdu_size;
ampdu_size <<= MIRA_FP_SHIFT; /* convert to fixed-point */
agglen <<= MIRA_FP_SHIFT;
/* XXX range checks? */
ampdu_size = ampdu_size / 1000; /* kbit */
ampdu_size = ampdu_size / 1000; /* mbit */
/* Compute Sub-Frame Error Rate (see section 2.2 in MiRA paper). */
sfer = mn->frames * mn->txfail + mn->retries;
if ((sfer >> MIRA_FP_SHIFT) != 0) { /* bug in wifi driver */
if (ic->ic_if.if_flags & IFF_DEBUG) {
#ifdef DIAGNOSTIC
printf("%s: mira sfer overflow\n",
ether_sprintf(ni->ni_macaddr));
#endif
#ifdef MIRA_DEBUG
mira_print_driver_stats(mn, ni);
#endif
}
ieee80211_mira_probe_done(mn);
return;
}
sfer <<= MIRA_FP_SHIFT; /* convert to fixed-point */
sfer /= (mn->txfail + 1) * mn->frames;
if (sfer > MIRA_FP_1) { /* bug in wifi driver */
if (ic->ic_if.if_flags & IFF_DEBUG) {
#ifdef DIAGNOSTIC
printf("%s: mira sfer > 1\n",
ether_sprintf(ni->ni_macaddr));
#endif
#ifdef MIRA_DEBUG
mira_print_driver_stats(mn, ni);
#endif
}
sfer = MIRA_FP_1; /* round down */
}
/* Store current loss percentage SFER. */
g->loss = sfer * 100;
#ifdef MIRA_DEBUG
if (g->loss && ieee80211_mira_probe_valid(mn, ni))
DPRINTFN(2, ("frame error rate at MCS %d: %s%%\n",
ni->ni_txmcs, mira_fp_sprintf(g->loss)));
#endif
/*
* Update goodput statistics (see section 5.1.2 in MiRA paper).
* We use a slightly modified but equivalent calculation which
* is tuned towards our fixed-point number format.
*/
g->average_agg = MIRA_FP_MUL(MIRA_FP_1 - alpha, g->average_agg);
g->average_agg += MIRA_FP_MUL(alpha, agglen);
toverhead = ieee80211_mira_toverhead(mn, ic, ni);
toverhead = MIRA_FP_MUL(toverhead, rate);
g->measured = MIRA_FP_DIV(MIRA_FP_1 - sfer, MIRA_FP_1 +
MIRA_FP_DIV(toverhead, MIRA_FP_MUL(ampdu_size, g->average_agg)));
g->measured = MIRA_FP_MUL(g->measured, rate);
g->average = MIRA_FP_MUL(MIRA_FP_1 - alpha, g->average);
g->average += MIRA_FP_MUL(alpha, g->measured);
g->stddeviation = MIRA_FP_MUL(MIRA_FP_1 - beta, g->stddeviation);
if (g->average > g->measured)
delta = g->average - g->measured;
else
delta = g->measured - g->average;
g->stddeviation += MIRA_FP_MUL(beta, delta);
}
void
ieee80211_mira_reset_goodput_stats(struct ieee80211_mira_node *mn)
{
int i;
for (i = 0; i < nitems(mn->g); i++) {
struct ieee80211_mira_goodput_stats *g = &mn->g[i];
memset(g, 0, sizeof(*g));
g->average_agg = 1;
g->probe_interval = IEEE80211_MIRA_PROBE_TIMEOUT_MIN;
}
}
void
ieee80211_mira_reset_driver_stats(struct ieee80211_mira_node *mn)
{
mn->frames = 0;
mn->retries = 0;
mn->txfail = 0;
mn->ampdu_size = 0;
mn->agglen = 1;
}
/*
* Rate selection.
*/
/* A rate's goodput has to be at least this much larger to be "better". */
#define IEEE80211_MIRA_RATE_THRESHOLD (MIRA_FP_1 / 64) /* ~ 0.015 */
#define IEEE80211_MIRA_LOSS_THRESHOLD 10 /* in percent */
/* Number of (sub-)frames which render a probe valid. */
#define IEEE80211_MIRA_MIN_PROBE_FRAMES 4
/* Number of bytes which, alternatively, render a probe valid. */
#define IEEE80211_MIRA_MIN_PROBE_BYTES (2 * IEEE80211_MAX_LEN)
/* Number of Tx failures which, alternatively, render a probe valid. */
#define IEEE80211_MIRA_MAX_PROBE_TXFAIL 1
/* Number of Tx retries which, alternatively, render a probe valid. */
#define IEEE80211_MIRA_MAX_PROBE_RETRIES 4
int
ieee80211_mira_next_lower_intra_rate(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
const struct ieee80211_ht_rateset *rs;
int i, next;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
rs = ieee80211_mira_get_rateset(ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
if (ni->ni_txmcs == rs->min_mcs)
return rs->min_mcs;
next = ni->ni_txmcs;
for (i = rs->nrates - 1; i >= 0; i--) {
if ((mn->valid_rates & (1 << (i + rs->min_mcs))) == 0)
continue;
if (i + rs->min_mcs < ni->ni_txmcs) {
next = i + rs->min_mcs;
break;
}
}
return next;
}
int
ieee80211_mira_next_intra_rate(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
const struct ieee80211_ht_rateset *rs;
int i, next;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
rs = ieee80211_mira_get_rateset(ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
if (ni->ni_txmcs == rs->max_mcs)
return rs->max_mcs;
next = ni->ni_txmcs;
for (i = 0; i < rs->nrates; i++) {
if ((mn->valid_rates & (1 << (i + rs->min_mcs))) == 0)
continue;
if (i + rs->min_mcs > ni->ni_txmcs) {
next = i + rs->min_mcs;
break;
}
}
return next;
}
const struct ieee80211_ht_rateset *
ieee80211_mira_next_rateset(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
const struct ieee80211_ht_rateset *rs, *rsnext;
int next;
int mcs = ni->ni_txmcs;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
rs = ieee80211_mira_get_rateset(mcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
if (mn->probing & IEEE80211_MIRA_PROBING_UP) {
if (rs->max_mcs == 7) /* MCS 0-7 */
next = sgi ? IEEE80211_HT_RATESET_MIMO2_SGI :
IEEE80211_HT_RATESET_MIMO2;
else if (rs->max_mcs == 15) /* MCS 8-15 */
next = sgi ? IEEE80211_HT_RATESET_MIMO3_SGI :
IEEE80211_HT_RATESET_MIMO3;
else if (rs->max_mcs == 23) /* MCS 16-23 */
next = sgi ? IEEE80211_HT_RATESET_MIMO4_SGI :
IEEE80211_HT_RATESET_MIMO4;
else /* MCS 24-31 */
return NULL;
} else if (mn->probing & IEEE80211_MIRA_PROBING_DOWN) {
if (rs->min_mcs == 24) /* MCS 24-31 */
next = sgi ? IEEE80211_HT_RATESET_MIMO3_SGI :
IEEE80211_HT_RATESET_MIMO3;
else if (rs->min_mcs == 16) /* MCS 16-23 */
next = sgi ? IEEE80211_HT_RATESET_MIMO2_SGI :
IEEE80211_HT_RATESET_MIMO2;
else if (rs->min_mcs == 8) /* MCS 8-15 */
next = sgi ? IEEE80211_HT_RATESET_SISO_SGI :
IEEE80211_HT_RATESET_SISO;
else /* MCS 0-7 */
return NULL;
} else
panic("%s: invalid probing mode %d", __func__, mn->probing);
rsnext = &ieee80211_std_ratesets_11n[next];
if ((rsnext->mcs_mask & mn->valid_rates) == 0)
return NULL;
return rsnext;
}
int
ieee80211_mira_best_mcs_in_rateset(struct ieee80211_mira_node *mn,
const struct ieee80211_ht_rateset *rs)
{
uint64_t gmax = 0;
int i, best_mcs = rs->min_mcs;
for (i = 0; i < rs->nrates; i++) {
int mcs = rs->min_mcs + i;
struct ieee80211_mira_goodput_stats *g = &mn->g[mcs];
if (((1 << mcs) & mn->valid_rates) == 0)
continue;
if (g->measured > gmax + IEEE80211_MIRA_RATE_THRESHOLD) {
gmax = g->measured;
best_mcs = mcs;
}
}
return best_mcs;
}
void
ieee80211_mira_probe_next_rateset(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni, const struct ieee80211_ht_rateset *rsnext)
{
const struct ieee80211_ht_rateset *rs;
struct ieee80211_mira_goodput_stats *g;
int best_mcs, i;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
/* Find most recently measured best MCS from the current rateset. */
rs = ieee80211_mira_get_rateset(ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
best_mcs = ieee80211_mira_best_mcs_in_rateset(mn, rs);
/* Switch to the next rateset. */
ni->ni_txmcs = rsnext->min_mcs;
if ((mn->valid_rates & (1 << rsnext->min_mcs)) == 0)
ni->ni_txmcs = ieee80211_mira_next_intra_rate(mn, ni);
/* Select the lowest rate from the next rateset with loss-free
* goodput close to the current best measurement. */
g = &mn->g[best_mcs];
for (i = 0; i < rsnext->nrates; i++) {
int mcs = rsnext->min_mcs + i;
uint64_t txrate = rsnext->rates[i];
if ((mn->valid_rates & (1 << mcs)) == 0)
continue;
txrate = txrate * 500; /* convert to kbit/s */
txrate <<= MIRA_FP_SHIFT; /* convert to fixed-point */
txrate /= 1000; /* convert to mbit/s */
if (txrate > g->measured + IEEE80211_MIRA_RATE_THRESHOLD) {
ni->ni_txmcs = mcs;
break;
}
}
/* If all rates are lower the maximum rate is the closest match. */
if (i == rsnext->nrates)
ni->ni_txmcs = rsnext->max_mcs;
/* Add rates from the next rateset as candidates. */
mn->candidate_rates |= (1 << ni->ni_txmcs);
if (mn->probing & IEEE80211_MIRA_PROBING_UP) {
mn->candidate_rates |=
(1 << ieee80211_mira_next_intra_rate(mn, ni));
} else if (mn->probing & IEEE80211_MIRA_PROBING_DOWN) {
#ifdef MIRA_AGGRESSIVE_DOWNWARDS_PROBING
mn->candidate_rates |= ieee80211_mira_mcs_below(mn,
ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
#else
mn->candidate_rates |=
(1 << ieee80211_mira_next_lower_intra_rate(mn, ni));
#endif
} else
panic("%s: invalid probing mode %d", __func__, mn->probing);
}
int
ieee80211_mira_next_mcs(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
int next;
if (mn->probing & IEEE80211_MIRA_PROBING_DOWN)
next = ieee80211_mira_next_lower_intra_rate(mn, ni);
else if (mn->probing & IEEE80211_MIRA_PROBING_UP)
next = ieee80211_mira_next_intra_rate(mn, ni);
else
panic("%s: invalid probing mode %d", __func__, mn->probing);
return next;
}
int
ieee80211_mira_prev_mcs(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
int next;
if (mn->probing & IEEE80211_MIRA_PROBING_DOWN)
next = ieee80211_mira_next_intra_rate(mn, ni);
else if (mn->probing & IEEE80211_MIRA_PROBING_UP)
next = ieee80211_mira_next_lower_intra_rate(mn, ni);
else
panic("%s: invalid probing mode %d", __func__, mn->probing);
return next;
}
int
ieee80211_mira_probe_valid(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
struct ieee80211_mira_goodput_stats *g = &mn->g[ni->ni_txmcs];
return (g->nprobes >= IEEE80211_MIRA_MIN_PROBE_FRAMES ||
g->nprobe_bytes >= IEEE80211_MIRA_MIN_PROBE_BYTES ||
mn->txfail >= IEEE80211_MIRA_MAX_PROBE_TXFAIL ||
mn->retries >= IEEE80211_MIRA_MAX_PROBE_RETRIES);
}
void
ieee80211_mira_probe_done(struct ieee80211_mira_node *mn)
{
int mcs;
/* Reset probe interval of the best rate. */
mn->g[mn->best_mcs].probe_interval = IEEE80211_MIRA_PROBE_TIMEOUT_MIN;
mn->g[mn->best_mcs].nprobes = 0;
mn->g[mn->best_mcs].nprobe_bytes = 0;
/* Update probing interval of other probed rates. */
for (mcs = 0; mcs < IEEE80211_HT_RATESET_NUM_MCS; mcs++) {
if (mcs != mn->best_mcs && (mn->probed_rates & (1 << mcs)))
ieee80211_mira_update_probe_interval(&mn->g[mcs]);
}
ieee80211_mira_cancel_timeouts(mn);
ieee80211_mira_reset_driver_stats(mn);
ieee80211_mira_reset_collision_stats(mn);
mn->probing = IEEE80211_MIRA_NOT_PROBING;
mn->probed_rates = 0;
mn->candidate_rates = 0;
}
int
ieee80211_mira_intra_mode_ra_finished(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
const struct ieee80211_ht_rateset *rs;
struct ieee80211_mira_goodput_stats *g = &mn->g[ni->ni_txmcs];
int next_mcs, best_mcs, probed_rates;
uint64_t next_rate;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
bool is_40mhz = (ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
if (!ieee80211_mira_probe_valid(mn, ni))
return 0;
probed_rates = (mn->probed_rates | (1 << ni->ni_txmcs));
/* Check if the min/max MCS in this rateset has been probed. */
rs = ieee80211_mira_get_rateset(ni->ni_txmcs, sgi, is_40mhz);
if (mn->probing & IEEE80211_MIRA_PROBING_DOWN) {
if (ni->ni_txmcs == rs->min_mcs ||
probed_rates & (1 << rs->min_mcs)) {
ieee80211_mira_trigger_next_rateset(mn, ni);
return 1;
}
} else if (mn->probing & IEEE80211_MIRA_PROBING_UP) {
if (ni->ni_txmcs == rs->max_mcs ||
probed_rates & (1 << rs->max_mcs)) {
ieee80211_mira_trigger_next_rateset(mn, ni);
return 1;
}
}
/*
* Check if the measured goodput is loss-free and better than the
* loss-free goodput of the candidate rate.
*/
next_mcs = ieee80211_mira_next_mcs(mn, ni);
if (next_mcs == ni->ni_txmcs) {
ieee80211_mira_trigger_next_rateset(mn, ni);
return 1;
}
next_rate = ieee80211_mira_get_txrate(next_mcs, sgi, is_40mhz);
if (g->loss == 0 &&
g->measured >= next_rate + IEEE80211_MIRA_RATE_THRESHOLD) {
ieee80211_mira_trigger_next_rateset(mn, ni);
return 1;
}
/* Check if we had a better measurement at a previously probed MCS. */
best_mcs = ieee80211_mira_best_mcs_in_rateset(mn, rs);
if (best_mcs != ni->ni_txmcs && (probed_rates & (1 << best_mcs))) {
if ((mn->probing & IEEE80211_MIRA_PROBING_UP) &&
best_mcs < ni->ni_txmcs) {
ieee80211_mira_trigger_next_rateset(mn, ni);
return 1;
}
if ((mn->probing & IEEE80211_MIRA_PROBING_DOWN) &&
best_mcs > ni->ni_txmcs) {
ieee80211_mira_trigger_next_rateset(mn, ni);
return 1;
}
}
/* Check if all rates in the set of candidate rates have been probed. */
if ((mn->candidate_rates & probed_rates) == mn->candidate_rates) {
/* Remain in the current rateset until above checks trigger. */
return 1;
}
return 0;
}
void
ieee80211_mira_trigger_next_rateset(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
const struct ieee80211_ht_rateset *rsnext;
rsnext = ieee80211_mira_next_rateset(mn, ni);
if (rsnext) {
ieee80211_mira_probe_next_rateset(mn, ni, rsnext);
mn->probing |= IEEE80211_MIRA_PROBING_INTER;
} else
mn->probing &= ~IEEE80211_MIRA_PROBING_INTER;
}
int
ieee80211_mira_inter_mode_ra_finished(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
return ((mn->probing & IEEE80211_MIRA_PROBING_INTER) == 0);
}
int
ieee80211_mira_best_rate(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
int i, best = 0;
uint64_t gmax = 0;
for (i = 0; i < nitems(mn->g); i++) {
struct ieee80211_mira_goodput_stats *g = &mn->g[i];
if (((1 << i) & mn->valid_rates) == 0)
continue;
if (g->measured > gmax + IEEE80211_MIRA_RATE_THRESHOLD) {
gmax = g->measured;
best = i;
}
}
#ifdef MIRA_DEBUG
if (mn->best_mcs != best) {
DPRINTF(("MCS %d is best; MCS{Mbps|probe interval}:", best));
for (i = 0; i < IEEE80211_HT_RATESET_NUM_MCS; i++) {
struct ieee80211_mira_goodput_stats *g = &mn->g[i];
if ((mn->valid_rates & (1 << i)) == 0)
continue;
DPRINTF((" %d{%s|%dms}", i,
mira_fp_sprintf(g->measured),
g->probe_interval));
}
DPRINTF(("\n"));
}
#endif
return best;
}
/* See section 5.1.1 (at "Adaptive probing interval") in MiRA paper. */
void
ieee80211_mira_update_probe_interval(struct ieee80211_mira_goodput_stats *g)
{
uint64_t lt;
int intval;
lt = g->loss / IEEE80211_MIRA_LOSS_THRESHOLD;
if (lt < MIRA_FP_1)
lt = MIRA_FP_1;
lt >>= MIRA_FP_SHIFT; /* round to integer */
intval = (1 << g->nprobes); /* 2^nprobes */
if (intval > IEEE80211_MIRA_PROBE_INTVAL_MAX)
intval = IEEE80211_MIRA_PROBE_INTVAL_MAX;
g->probe_interval = IEEE80211_MIRA_PROBE_TIMEOUT_MIN * intval * lt;
}
void
ieee80211_mira_schedule_probe_timers(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
struct ieee80211_mira_goodput_stats *g;
CTimeout **to;
int mcs;
mcs = ieee80211_mira_next_intra_rate(mn, ni);
to = &mn->probe_to[IEEE80211_MIRA_PROBE_TO_UP];
g = &mn->g[mcs];
if (mcs != ni->ni_txmcs && !timeout_pending(to) &&
!mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_UP]) {
timeout_add_msec(to, g->probe_interval);
DPRINTFN(3, ("start probing up for node %s at MCS %d in at "
"least %d msec\n",
ether_sprintf(ni->ni_macaddr), mcs, g->probe_interval));
}
mcs = ieee80211_mira_next_lower_intra_rate(mn, ni);
to = &mn->probe_to[IEEE80211_MIRA_PROBE_TO_DOWN];
g = &mn->g[mcs];
if (mcs != ni->ni_txmcs && !timeout_pending(to) &&
!mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_DOWN]) {
timeout_add_msec(to, g->probe_interval);
DPRINTFN(3, ("start probing down for node %s at MCS %d in at "
"least %d msec\n",
ether_sprintf(ni->ni_macaddr), mcs, g->probe_interval));
}
}
int
ieee80211_mira_check_probe_timers(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
int ret = 0, expired_timer = IEEE80211_MIRA_PROBE_TO_INVALID;
int mcs;
if (mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_UP] &&
mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_DOWN]) {
if (arc4random_uniform(2))
expired_timer = IEEE80211_MIRA_PROBE_TO_UP;
else
expired_timer = IEEE80211_MIRA_PROBE_TO_DOWN;
} else if (mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_DOWN])
expired_timer = IEEE80211_MIRA_PROBE_TO_DOWN;
else if (mn->probe_timer_expired[IEEE80211_MIRA_PROBE_TO_UP])
expired_timer = IEEE80211_MIRA_PROBE_TO_UP;
if (expired_timer != IEEE80211_MIRA_PROBE_TO_INVALID)
mn->probe_timer_expired[expired_timer] = 0;
switch (expired_timer) {
case IEEE80211_MIRA_PROBE_TO_UP:
/* Do time-based upwards probing on next frame. */
DPRINTFN(2, ("probe timer expired: probe upwards\n"));
mn->probing = IEEE80211_MIRA_PROBING_UP;
mcs = ieee80211_mira_next_intra_rate(mn, ni);
mn->candidate_rates = (1 << mcs);
ret = 1;
break;
case IEEE80211_MIRA_PROBE_TO_DOWN:
/* Do time-based downwards probing on next frame. */
DPRINTFN(2, ("probe timer expired: probe downwards\n"));
mn->probing = IEEE80211_MIRA_PROBING_DOWN;
mcs = ieee80211_mira_next_lower_intra_rate(mn, ni);
mn->candidate_rates = (1 << mcs);
ret = 1;
break;
case IEEE80211_MIRA_PROBE_TO_INVALID:
default:
ret = 0;
break;
}
return ret;
}
void
ieee80211_mira_probe_next_rate(struct ieee80211_mira_node *mn,
struct ieee80211_node *ni)
{
/* Select the next rate to probe. */
mn->probed_rates |= (1 << ni->ni_txmcs);
ni->ni_txmcs = ieee80211_mira_next_mcs(mn, ni);
}
int
ieee80211_mira_valid_tx_mcs(struct ieee80211com *ic, int mcs)
{
uint32_t ntxstreams = 1;
static const int max_mcs[] = { 7, 15, 23, 31 };
if ((ic->ic_tx_mcs_set & IEEE80211_TX_RX_MCS_NOT_EQUAL) == 0)
return isset(ic->ic_sup_mcs, mcs);
ntxstreams += ((ic->ic_tx_mcs_set & IEEE80211_TX_SPATIAL_STREAMS) >> 2);
if (ntxstreams < 1 || ntxstreams > 4)
panic("invalid number of Tx streams: %u", ntxstreams);
return (mcs <= max_mcs[ntxstreams - 1] && isset(ic->ic_sup_mcs, mcs));
}
uint32_t
ieee80211_mira_valid_rates(struct ieee80211com *ic, struct ieee80211_node *ni)
{
uint32_t valid_mcs = 0;
int i;
for (i = 0; i < IEEE80211_HT_RATESET_NUM_MCS; i++) {
if (!isset(ni->ni_rxmcs, i))
continue;
if (!ieee80211_mira_valid_tx_mcs(ic, i))
continue;
valid_mcs |= (1 << i);
}
return valid_mcs;
}
uint32_t
ieee80211_mira_mcs_below(struct ieee80211_mira_node *mn, int mcs, int sgi, bool is_40mhz)
{
const struct ieee80211_ht_rateset *rs;
uint32_t mcs_mask;
int i;
rs = ieee80211_mira_get_rateset(mcs, sgi, is_40mhz);
mcs_mask = (1 << rs->min_mcs);
for (i = rs->min_mcs + 1; i < mcs; i++) {
if ((mn->valid_rates & (1 << i)) == 0)
continue;
mcs_mask |= (1 << i);
}
return mcs_mask;
}
/*
* Constants involved in detecting suspected frame collisions.
* See section 5.2 of MiRA paper
*/
#define MIRA_COLLISION_LOSS_PERCENTAGE 10 /* from MiRA paper */
#define MIRA_COLLISION_DETECTED 3 /* from MiRA paper */
/*
* XXX The paper's algorithm assumes aggregated frames. This is particularly
* important for the detection of consecutive frame collisions which indicate
* high competition for air time. Because we do not yet support Tx aggregation,
* we run the algorithm over the result of several frames instead.
* We also aggregate retries across all frames and act upon a percentage of
* retried frames, rather than acting on retries seen for one aggregated frame.
*
* The collision window size (number of frames sent) needs to be short to
* ensure our detection of consecutive collisions remains somewhat accurate.
* We really have no idea how much time passes between frames in the window!
* The good news is that users will only care about collision detection during
* a transmit burst anyway, and we have this case more or less covered.
*/
#define MIRA_COLLISION_MIN_FRAMES 6 /* XXX magic number */
#define MIRA_COLLISION_RETRY_PERCENTAGE 60 /* XXX magic number */
/* Set RTS threshold based on suspected collision from other STAs. */
void
ieee80211_mira_set_rts_threshold(struct ieee80211_mira_node *mn,
struct ieee80211com *ic, struct ieee80211_node *ni)
{
uint16_t rtsthreshold = mn->rts_threshold;
uint32_t loss, retry;
/* Update collision window stats. */
mn->ifwnd_frames += mn->frames;
mn->ifwnd_retries += mn->retries;
mn->ifwnd_txfail += mn->txfail;
if (mn->ifwnd_frames < MIRA_COLLISION_MIN_FRAMES)
return; /* not enough frames yet */
/* Check whether the loss pattern indicates frame collisions. */
loss = (mn->ifwnd_txfail * 100) / mn->ifwnd_frames;
retry = (mn->ifwnd_retries * 100) / mn->ifwnd_frames;
if (retry > MIRA_COLLISION_RETRY_PERCENTAGE &&
loss < MIRA_COLLISION_LOSS_PERCENTAGE) {
if (mn->ifwnd == 0) {
/* First frame collision confirmed. */
mn->ifwnd = MIRA_COLLISION_DETECTED;
} else if (mn->ifwnd == MIRA_COLLISION_DETECTED) {
/* Successive frame collision confirmed. Use RTS. */
rtsthreshold = IEEE80211_RTS_DEFAULT;
}
} else {
if (mn->ifwnd > 0)
mn->ifwnd--;
if (mn->ifwnd == 0)
rtsthreshold = IEEE80211_RTS_MAX;
}
mn->rts_threshold = rtsthreshold;
ieee80211_mira_reset_collision_stats(mn);
}
int
ieee80211_mira_get_rts_threshold(struct ieee80211_mira_node *mn,
struct ieee80211com *ic, struct ieee80211_node *ni, size_t framelen)
{
int rtsrate = ieee80211_min_basic_rate(ic);
uint64_t txtime, rtsoverhead;
/* Magic number from MiRA paper ("cost/benefit ratio"). */
static const uint64_t k = MIRA_FP_1 + (MIRA_FP_1 / 2); /* 1.5 */
if (mn->probing || mn->rts_threshold >= IEEE80211_RTS_MAX)
return IEEE80211_RTS_MAX;
/* Use RTS only if potential gains outweigh overhead. */
txtime = ieee80211_mira_ht_txtime(framelen, ni->ni_txmcs,
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan),
(ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0,
ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
rtsoverhead = ieee80211_mira_legacy_txtime(MIRA_RTSLEN, rtsrate, ic);
rtsoverhead += ieee80211_mira_legacy_txtime(MIRA_CTSLEN, rtsrate, ic);
/* convert to fixed-point */
txtime <<= MIRA_FP_SHIFT;
rtsoverhead <<= MIRA_FP_SHIFT;
if (txtime >= MIRA_FP_MUL(k, rtsoverhead))
return mn->rts_threshold;
return IEEE80211_RTS_MAX;
}
void
ieee80211_mira_reset_collision_stats(struct ieee80211_mira_node *mn)
{
mn->ifwnd_frames = 0;
mn->ifwnd_retries = 0;
mn->ifwnd_txfail = 0;
}
void
ieee80211_mira_choose(struct ieee80211_mira_node *mn, struct ieee80211com *ic,
struct ieee80211_node *ni)
{
struct ieee80211_mira_goodput_stats *g = &mn->g[ni->ni_txmcs];
int s;
int sgi = (ni->ni_flags & (IEEE80211_NODE_HT_SGI20 | IEEE80211_NODE_HT_SGI40)) ? 1 : 0;
const struct ieee80211_ht_rateset *rs;
s = splnet();
if (mn->valid_rates == 0)
mn->valid_rates = ieee80211_mira_valid_rates(ic, ni);
#ifdef MIRA_DEBUG
if (mira_debug >= 5)
mira_print_driver_stats(mn, ni);
#endif
ieee80211_mira_update_stats(mn, ic, ni);
if (mn->probing) {
/* Probe another rate or settle at the best rate. */
if (!ieee80211_mira_intra_mode_ra_finished(mn, ni)) {
if (ieee80211_mira_probe_valid(mn, ni)) {
ieee80211_mira_probe_next_rate(mn, ni);
ieee80211_mira_reset_driver_stats(mn);
}
DPRINTFN(4, ("probing MCS %d\n", ni->ni_txmcs));
} else if (ieee80211_mira_inter_mode_ra_finished(mn, ni)) {
mn->best_mcs = ieee80211_mira_best_rate(mn, ni);
ni->ni_txmcs = mn->best_mcs;
ieee80211_mira_probe_done(mn);
}
splx(s);
return;
} else {
ieee80211_mira_set_rts_threshold(mn, ic, ni);
ieee80211_mira_reset_driver_stats(mn);
ieee80211_mira_schedule_probe_timers(mn, ni);
}
if (ieee80211_mira_check_probe_timers(mn, ni)) {
/* Time-based probing has triggered. */
splx(s);
return;
}
/* Check if event-based probing should be triggered. */
rs = ieee80211_mira_get_rateset(ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
if (g->measured < g->average - 2 * g->stddeviation &&
ni->ni_txmcs != rs->min_mcs) {
/* Channel becomes bad. Probe downwards. */
DPRINTFN(2, ("channel becomes bad; probe downwards\n"));
DPRINTFN(3, ("measured: %s Mbit/s\n",
mira_fp_sprintf(g->measured)));
DPRINTFN(3, ("average: %s Mbit/s\n",
mira_fp_sprintf(g->average)));
DPRINTFN(3, ("stddeviation: %s\n",
mira_fp_sprintf(g->stddeviation)));
mn->probing = IEEE80211_MIRA_PROBING_DOWN;
mn->probed_rates = 0;
#ifdef MIRA_AGGRESSIVE_DOWNWARDS_PROBING
/* Allow for probing all the way down within this rateset. */
mn->candidate_rates = ieee80211_mira_mcs_below(mn,
ni->ni_txmcs, sgi, ni->ni_chw == IEEE80211_CHAN_WIDTH_40);
#else
/* Probe the lower candidate rate to see if it's any better. */
mn->candidate_rates =
(1 << ieee80211_mira_next_lower_intra_rate(mn, ni));
#endif
ieee80211_mira_cancel_timeouts(mn);
} else if (g->measured > g->average + 2 * g->stddeviation &&
ni->ni_txmcs != rs->max_mcs) {
/* Channel becomes good. */
DPRINTFN(2, ("channel becomes good; probe upwards\n"));
DPRINTFN(3, ("measured: %s Mbit/s\n",
mira_fp_sprintf(g->measured)));
DPRINTFN(3, ("average: %s Mbit/s\n",
mira_fp_sprintf(g->average)));
DPRINTFN(3, ("stddeviation: %s\n",
mira_fp_sprintf(g->stddeviation)));
mn->probing = IEEE80211_MIRA_PROBING_UP;
mn->probed_rates = 0;
/* Probe the upper candidate rate to see if it's any better. */
mn->candidate_rates =
(1 << ieee80211_mira_next_intra_rate(mn, ni));
ieee80211_mira_cancel_timeouts(mn);
} else {
/* Remain at current rate. */
mn->probing = IEEE80211_MIRA_NOT_PROBING;
mn->probed_rates = 0;
mn->candidate_rates = 0;
}
splx(s);
}
void
ieee80211_mira_node_init(struct ieee80211_mira_node *mn)
{
memset(mn, 0, sizeof(*mn));
mn->agglen = 1;
mn->rts_threshold = IEEE80211_RTS_MAX;
ieee80211_mira_reset_goodput_stats(mn);
ieee80211_mira_reset_collision_stats(mn);
memset(mn->probe_to, 0, sizeof(mn->probe_to) * nitems(mn->probe_to));
timeout_set(&mn->probe_to[IEEE80211_MIRA_PROBE_TO_UP],
ieee80211_mira_probe_timeout_up, mn);
timeout_set(&mn->probe_to[IEEE80211_MIRA_PROBE_TO_DOWN],
ieee80211_mira_probe_timeout_down, mn);
}
void
ieee80211_mira_cancel_timeouts(struct ieee80211_mira_node *mn)
{
int t;
for (t = 0; t < nitems(mn->probe_to); t++)
timeout_del(&mn->probe_to[t]);
}
void
ieee80211_mira_node_free(struct ieee80211_mira_node *mn)
{
int t;
for (t = 0; t < nitems(mn->probe_to); t++) {
timeout_del(&mn->probe_to[t]);
timeout_free(&mn->probe_to[t]);
}
}
int
ieee80211_mira_is_probing(struct ieee80211_mira_node *mn)
{
return mn->probing != IEEE80211_MIRA_NOT_PROBING;
}
int
ieee80211_mira_get_best_mcs(struct ieee80211_mira_node *mn)
{
return mn->best_mcs;
}