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