Home | History | Annotate | Line # | Download | only in net80211
ieee80211_amrr.c revision 1.3.18.5
      1 /*	$NetBSD: ieee80211_amrr.c,v 1.3.18.5 2019/06/10 22:09:46 christos Exp $ */
      2 
      3 /*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
      4 
      5 /*-
      6  * Copyright (c) 2010 Rui Paulo <rpaulo (at) FreeBSD.org>
      7  * Copyright (c) 2006
      8  *	Damien Bergamini <damien.bergamini (at) free.fr>
      9  *
     10  * Permission to use, copy, modify, and distribute this software for any
     11  * purpose with or without fee is hereby granted, provided that the above
     12  * copyright notice and this permission notice appear in all copies.
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     21  */
     22 
     23 #include <sys/cdefs.h>
     24 #ifdef __NetBSD__
     25 __KERNEL_RCSID(0, "$NetBSD: ieee80211_amrr.c,v 1.3.18.5 2019/06/10 22:09:46 christos Exp $");
     26 #endif
     27 
     28 /*-
     29  * Naive implementation of the Adaptive Multi Rate Retry algorithm:
     30  *
     31  * "IEEE 802.11 Rate Adaptation: A Practical Approach"
     32  *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
     33  *  INRIA Sophia - Projet Planete
     34  *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
     35  */
     36 #ifdef _KERNEL_OPT
     37 #include "opt_wlan.h"
     38 #endif
     39 
     40 #include <sys/param.h>
     41 #include <sys/kernel.h>
     42 #include <sys/malloc.h>
     43 #include <sys/module.h>
     44 #include <sys/sbuf.h>
     45 #include <sys/socket.h>
     46 #include <sys/sysctl.h>
     47 
     48 #include <net/if.h>
     49 #ifdef __FreeBSD__
     50 #include <net/if_var.h>
     51 #endif
     52 #include <net/if_media.h>
     53 #ifdef __FreeBSD__
     54 #include <net/ethernet.h>
     55 #endif
     56 #ifdef __NetBSD__
     57 #include <net/route.h>
     58 #endif
     59 
     60 #ifdef INET
     61 #include <netinet/in.h>
     62 #ifdef __FreeBSD__
     63 #include <netinet/if_ether.h>
     64 #endif
     65 #endif
     66 
     67 #include <net80211/ieee80211_var.h>
     68 #include <net80211/ieee80211_ht.h>
     69 #include <net80211/ieee80211_amrr.h>
     70 #include <net80211/ieee80211_ratectl.h>
     71 
     72 #ifdef __NetBSD__
     73 #undef  KASSERT
     74 #define KASSERT(__cond, __complaint) FBSDKASSERT(__cond, __complaint)
     75 #endif
     76 
     77 #define is_success(amn)	\
     78 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
     79 #define is_failure(amn)	\
     80 	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
     81 #define is_enough(amn)		\
     82 	((amn)->amn_txcnt > 10)
     83 
     84 static void	amrr_setinterval(const struct ieee80211vap *, int);
     85 static void	amrr_init(struct ieee80211vap *);
     86 static void	amrr_deinit(struct ieee80211vap *);
     87 static void	amrr_node_init(struct ieee80211_node *);
     88 static void	amrr_node_deinit(struct ieee80211_node *);
     89 static int	amrr_update(struct ieee80211_amrr *,
     90     			struct ieee80211_amrr_node *, struct ieee80211_node *);
     91 static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
     92 static void	amrr_tx_complete(const struct ieee80211_node *,
     93 			const struct ieee80211_ratectl_tx_status *);
     94 static void	amrr_tx_update_cb(void *, struct ieee80211_node *);
     95 static void	amrr_tx_update(struct ieee80211vap *vap,
     96 			struct ieee80211_ratectl_tx_stats *);
     97 #ifdef notyet
     98 static void	amrr_sysctlattach(struct ieee80211vap *,
     99 			struct sysctl_ctx_list *, struct sysctl_oid *);
    100 #endif
    101 static void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
    102 
    103 /* number of references from net80211 layer */
    104 static	int nrefs = 0;
    105 
    106 #if __FreeBSD__
    107 static const struct ieee80211_ratectl amrr = {
    108 #else
    109 const struct ieee80211_ratectl ratectl_amrr = {
    110 #endif
    111 	.ir_name	= "amrr",
    112 	.ir_attach	= NULL,
    113 	.ir_detach	= NULL,
    114 	.ir_init	= amrr_init,
    115 	.ir_deinit	= amrr_deinit,
    116 	.ir_node_init	= amrr_node_init,
    117 	.ir_node_deinit	= amrr_node_deinit,
    118 	.ir_rate	= amrr_rate,
    119 	.ir_tx_complete	= amrr_tx_complete,
    120 	.ir_tx_update	= amrr_tx_update,
    121 	.ir_setinterval	= amrr_setinterval,
    122 	.ir_node_stats	= amrr_node_stats,
    123 };
    124 #if __FreeBSD__
    125 IEEE80211_RATECTL_MODULE(amrr, 1);
    126 IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
    127 #endif
    128 
    129 static void
    130 amrr_setinterval(const struct ieee80211vap *vap, int msecs)
    131 {
    132 	struct ieee80211_amrr *amrr = vap->iv_rs;
    133 	int t;
    134 
    135 	if (msecs < 100)
    136 		msecs = 100;
    137 	t = msecs_to_ticks(msecs);
    138 	amrr->amrr_interval = (t < 1) ? 1 : t;
    139 }
    140 
    141 static void
    142 amrr_init(struct ieee80211vap *vap)
    143 {
    144 	struct ieee80211_amrr *amrr;
    145 
    146 	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
    147 
    148 	nrefs++;		/* XXX locking */
    149 	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
    150 	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
    151 	if (amrr == NULL) {
    152 		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
    153 		return;
    154 	}
    155 	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
    156 	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
    157 	amrr_setinterval(vap, 500 /* ms */);
    158 #ifdef notyet
    159 	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
    160 #endif
    161 }
    162 
    163 static void
    164 amrr_deinit(struct ieee80211vap *vap)
    165 {
    166 	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
    167 	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
    168 	nrefs--;		/* XXX locking */
    169 }
    170 
    171 /*
    172  * Return whether 11n rates are possible.
    173  *
    174  * Some 11n devices may return HT information but no HT rates.
    175  * Thus, we shouldn't treat them as an 11n node.
    176  */
    177 static int
    178 amrr_node_is_11n(struct ieee80211_node *ni)
    179 {
    180 
    181 	if (ni->ni_chan == NULL)
    182 		return (0);
    183 	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
    184 		return (0);
    185 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
    186 		return (0);
    187 	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
    188 }
    189 
    190 static void
    191 amrr_node_init(struct ieee80211_node *ni)
    192 {
    193 	const struct ieee80211_rateset *rs = NULL;
    194 	struct ieee80211vap *vap = ni->ni_vap;
    195 	struct ieee80211_amrr *amrr = vap->iv_rs;
    196 	struct ieee80211_amrr_node *amn;
    197 	uint8_t rate;
    198 
    199 	if (ni->ni_rctls == NULL) {
    200 		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
    201 		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
    202 		if (amn == NULL) {
    203 			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
    204 			    "structure\n");
    205 			return;
    206 		}
    207 	} else
    208 		amn = ni->ni_rctls;
    209 	amn->amn_amrr = amrr;
    210 	amn->amn_success = 0;
    211 	amn->amn_recovery = 0;
    212 	amn->amn_txcnt = amn->amn_retrycnt = 0;
    213 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
    214 
    215 	/* 11n or not? Pick the right rateset */
    216 	if (amrr_node_is_11n(ni)) {
    217 		/* XXX ew */
    218 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
    219 		    "%s: 11n node", __func__);
    220 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
    221 	} else {
    222 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
    223 		    "%s: non-11n node", __func__);
    224 		rs = &ni->ni_rates;
    225 	}
    226 
    227 	/* Initial rate - lowest */
    228 	rate = rs->rs_rates[0];
    229 
    230 	/* XXX clear the basic rate flag if it's not 11n */
    231 	if (! amrr_node_is_11n(ni))
    232 		rate &= IEEE80211_RATE_VAL;
    233 
    234 	/* pick initial rate from the rateset - HT or otherwise */
    235 	/* Pick something low that's likely to succeed */
    236 	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
    237 	    amn->amn_rix--) {
    238 		/* legacy - anything < 36mbit, stop searching */
    239 		/* 11n - stop at MCS4 */
    240 		if (amrr_node_is_11n(ni)) {
    241 			if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
    242 				break;
    243 		} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
    244 			break;
    245 	}
    246 	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
    247 
    248 	/* if the rate is an 11n rate, ensure the MCS bit is set */
    249 	if (amrr_node_is_11n(ni))
    250 		rate |= IEEE80211_RATE_MCS;
    251 
    252 	/* Assign initial rate from the rateset */
    253 	ni->ni_txrate = rate;
    254 	amn->amn_ticks = ticks;
    255 
    256 	/* XXX TODO: we really need a rate-to-string method */
    257 	/* XXX TODO: non-11n rate should be divided by two.. */
    258 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
    259 	    "AMRR: nrates=%d, initial rate %s%d",
    260 	    rs->rs_nrates,
    261 	    amrr_node_is_11n(ni) ? "MCS " : "",
    262 	    rate & IEEE80211_RATE_VAL);
    263 }
    264 
    265 static void
    266 amrr_node_deinit(struct ieee80211_node *ni)
    267 {
    268 	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
    269 }
    270 
    271 static int
    272 amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
    273     struct ieee80211_node *ni)
    274 {
    275 	int rix = amn->amn_rix;
    276 	const struct ieee80211_rateset *rs = NULL;
    277 
    278 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
    279 
    280 	/* 11n or not? Pick the right rateset */
    281 	if (amrr_node_is_11n(ni)) {
    282 		/* XXX ew */
    283 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
    284 	} else {
    285 		rs = &ni->ni_rates;
    286 	}
    287 
    288 	/* XXX TODO: we really need a rate-to-string method */
    289 	/* XXX TODO: non-11n rate should be divided by two.. */
    290 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
    291 	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
    292 	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
    293 	    amn->amn_txcnt,
    294 	    amn->amn_retrycnt);
    295 
    296 	/*
    297 	 * XXX This is totally bogus for 11n, as although high MCS
    298 	 * rates for each stream may be failing, the next stream
    299 	 * should be checked.
    300 	 *
    301 	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
    302 	 * MCS23, we should skip 6/7 and try 8 onwards.
    303 	 */
    304 	if (is_success(amn)) {
    305 		amn->amn_success++;
    306 		if (amn->amn_success >= amn->amn_success_threshold &&
    307 		    rix + 1 < rs->rs_nrates) {
    308 			amn->amn_recovery = 1;
    309 			amn->amn_success = 0;
    310 			rix++;
    311 			/* XXX TODO: we really need a rate-to-string method */
    312 			/* XXX TODO: non-11n rate should be divided by two.. */
    313 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
    314 			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
    315 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
    316 			    amn->amn_txcnt, amn->amn_retrycnt);
    317 		} else {
    318 			amn->amn_recovery = 0;
    319 		}
    320 	} else if (is_failure(amn)) {
    321 		amn->amn_success = 0;
    322 		if (rix > 0) {
    323 			if (amn->amn_recovery) {
    324 				amn->amn_success_threshold *= 2;
    325 				if (amn->amn_success_threshold >
    326 				    amrr->amrr_max_success_threshold)
    327 					amn->amn_success_threshold =
    328 					    amrr->amrr_max_success_threshold;
    329 			} else {
    330 				amn->amn_success_threshold =
    331 				    amrr->amrr_min_success_threshold;
    332 			}
    333 			rix--;
    334 			/* XXX TODO: we really need a rate-to-string method */
    335 			/* XXX TODO: non-11n rate should be divided by two.. */
    336 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
    337 			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
    338 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
    339 			    amn->amn_txcnt, amn->amn_retrycnt);
    340 		}
    341 		amn->amn_recovery = 0;
    342 	}
    343 
    344 	/* reset counters */
    345 	amn->amn_txcnt = 0;
    346 	amn->amn_retrycnt = 0;
    347 
    348 	return rix;
    349 }
    350 
    351 /*
    352  * Return the rate index to use in sending a data frame.
    353  * Update our internal state if it's been long enough.
    354  * If the rate changes we also update ni_txrate to match.
    355  */
    356 static int
    357 amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
    358 {
    359 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
    360 	struct ieee80211_amrr *amrr = amn->amn_amrr;
    361 	const struct ieee80211_rateset *rs = NULL;
    362 	int rix;
    363 
    364 	/* 11n or not? Pick the right rateset */
    365 	if (amrr_node_is_11n(ni)) {
    366 		/* XXX ew */
    367 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
    368 	} else {
    369 		rs = &ni->ni_rates;
    370 	}
    371 
    372 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
    373 		rix = amrr_update(amrr, amn, ni);
    374 		if (rix != amn->amn_rix) {
    375 			/* update public rate */
    376 			ni->ni_txrate = rs->rs_rates[rix];
    377 			/* XXX strip basic rate flag from txrate, if non-11n */
    378 			if (amrr_node_is_11n(ni))
    379 				ni->ni_txrate |= IEEE80211_RATE_MCS;
    380 			else
    381 				ni->ni_txrate &= IEEE80211_RATE_VAL;
    382 			amn->amn_rix = rix;
    383 		}
    384 		amn->amn_ticks = ticks;
    385 	} else
    386 		rix = amn->amn_rix;
    387 	return rix;
    388 }
    389 
    390 /*
    391  * Update statistics with tx complete status.  Ok is non-zero
    392  * if the packet is known to be ACK'd.  Retries has the number
    393  * retransmissions (i.e. xmit attempts - 1).
    394  */
    395 static void
    396 amrr_tx_complete(const struct ieee80211_node *ni,
    397     const struct ieee80211_ratectl_tx_status *status)
    398 {
    399 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
    400 	int retries;
    401 
    402 	retries = 0;
    403 	if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
    404 		retries = status->long_retries;
    405 
    406 	amn->amn_txcnt++;
    407 	if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
    408 		amn->amn_success++;
    409 	amn->amn_retrycnt += retries;
    410 }
    411 
    412 static void
    413 amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
    414 {
    415 	struct ieee80211_ratectl_tx_stats *stats = arg;
    416 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
    417 	int txcnt, success, retrycnt;
    418 
    419 	txcnt = stats->nframes;
    420 	success = stats->nsuccess;
    421 	retrycnt = 0;
    422 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
    423 		retrycnt = stats->nretries;
    424 
    425 	amn->amn_txcnt += txcnt;
    426 	amn->amn_success += success;
    427 	amn->amn_retrycnt += retrycnt;
    428 }
    429 
    430 /*
    431  * Set tx count/retry statistics explicitly.  Intended for
    432  * drivers that poll the device for statistics maintained
    433  * in the device.
    434  */
    435 static void
    436 amrr_tx_update(struct ieee80211vap *vap,
    437     struct ieee80211_ratectl_tx_stats *stats)
    438 {
    439 
    440 	if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
    441 		amrr_tx_update_cb(stats, stats->ni);
    442 	else {
    443 		ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
    444 		    amrr_tx_update_cb, stats);
    445 	}
    446 }
    447 
    448 #ifdef notyet
    449 static int
    450 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
    451 {
    452 	struct ieee80211vap *vap = arg1;
    453 	struct ieee80211_amrr *amrr = vap->iv_rs;
    454 	int msecs = ticks_to_msecs(amrr->amrr_interval);
    455 	int error;
    456 
    457 	error = sysctl_handle_int(oidp, &msecs, 0, req);
    458 	if (error || !req->newptr)
    459 		return error;
    460 	amrr_setinterval(vap, msecs);
    461 	return 0;
    462 }
    463 #endif
    464 
    465 #ifdef notyet
    466 static void
    467 amrr_sysctlattach(struct ieee80211vap *vap,
    468     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
    469 {
    470 	struct ieee80211_amrr *amrr = vap->iv_rs;
    471 
    472 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
    473 	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
    474 	    0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
    475 	/* XXX bounds check values */
    476 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
    477 	    "amrr_max_sucess_threshold", CTLFLAG_RW,
    478 	    &amrr->amrr_max_success_threshold, 0, "");
    479 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
    480 	    "amrr_min_sucess_threshold", CTLFLAG_RW,
    481 	    &amrr->amrr_min_success_threshold, 0, "");
    482 }
    483 #endif
    484 
    485 static void
    486 amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
    487 {
    488 	int rate;
    489 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
    490 	struct ieee80211_rateset *rs;
    491 
    492 	/* XXX TODO: check locking? */
    493 
    494 	/* XXX TODO: this should be a method */
    495 	if (amrr_node_is_11n(ni)) {
    496 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
    497 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
    498 		sbuf_printf(s, "rate: MCS %d\n", rate);
    499 	} else {
    500 		rs = &ni->ni_rates;
    501 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
    502 		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
    503 	}
    504 
    505 	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
    506 	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
    507 	sbuf_printf(s, "success: %u\n", amn->amn_success);
    508 	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
    509 	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
    510 	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
    511 }
    512 
    513