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