xref: /freebsd/sys/net80211/ieee80211_amrr.c (revision aa0a1e58)
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$");
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_amrr.h>
50 #include <net80211/ieee80211_ratectl.h>
51 
52 #define is_success(amn)	\
53 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
54 #define is_failure(amn)	\
55 	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
56 #define is_enough(amn)		\
57 	((amn)->amn_txcnt > 10)
58 
59 static void	amrr_setinterval(const struct ieee80211vap *, int);
60 static void	amrr_init(struct ieee80211vap *);
61 static void	amrr_deinit(struct ieee80211vap *);
62 static void	amrr_node_init(struct ieee80211_node *);
63 static void	amrr_node_deinit(struct ieee80211_node *);
64 static int	amrr_update(struct ieee80211_amrr *,
65     			struct ieee80211_amrr_node *, struct ieee80211_node *);
66 static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
67 static void	amrr_tx_complete(const struct ieee80211vap *,
68     			const struct ieee80211_node *, int,
69 			void *, void *);
70 static void	amrr_tx_update(const struct ieee80211vap *vap,
71 			const struct ieee80211_node *, void *, void *, void *);
72 static void	amrr_sysctlattach(struct ieee80211vap *,
73 			struct sysctl_ctx_list *, struct sysctl_oid *);
74 
75 /* number of references from net80211 layer */
76 static	int nrefs = 0;
77 
78 static const struct ieee80211_ratectl amrr = {
79 	.ir_name	= "amrr",
80 	.ir_attach	= NULL,
81 	.ir_detach	= NULL,
82 	.ir_init	= amrr_init,
83 	.ir_deinit	= amrr_deinit,
84 	.ir_node_init	= amrr_node_init,
85 	.ir_node_deinit	= amrr_node_deinit,
86 	.ir_rate	= amrr_rate,
87 	.ir_tx_complete	= amrr_tx_complete,
88 	.ir_tx_update	= amrr_tx_update,
89 	.ir_setinterval	= amrr_setinterval,
90 };
91 IEEE80211_RATECTL_MODULE(amrr, 1);
92 IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
93 
94 static void
95 amrr_setinterval(const struct ieee80211vap *vap, int msecs)
96 {
97 	struct ieee80211_amrr *amrr = vap->iv_rs;
98 	int t;
99 
100 	if (msecs < 100)
101 		msecs = 100;
102 	t = msecs_to_ticks(msecs);
103 	amrr->amrr_interval = (t < 1) ? 1 : t;
104 }
105 
106 static void
107 amrr_init(struct ieee80211vap *vap)
108 {
109 	struct ieee80211_amrr *amrr;
110 
111 	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
112 
113 	amrr = vap->iv_rs = malloc(sizeof(struct ieee80211_amrr),
114 	    M_80211_RATECTL, M_NOWAIT|M_ZERO);
115 	if (amrr == NULL) {
116 		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
117 		return;
118 	}
119 	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
120 	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
121 	amrr_setinterval(vap, 500 /* ms */);
122 	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
123 }
124 
125 static void
126 amrr_deinit(struct ieee80211vap *vap)
127 {
128 	free(vap->iv_rs, M_80211_RATECTL);
129 }
130 
131 static void
132 amrr_node_init(struct ieee80211_node *ni)
133 {
134 	const struct ieee80211_rateset *rs = &ni->ni_rates;
135 	struct ieee80211vap *vap = ni->ni_vap;
136 	struct ieee80211_amrr *amrr = vap->iv_rs;
137 	struct ieee80211_amrr_node *amn;
138 
139 	if (ni->ni_rctls == NULL) {
140 		ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node),
141 		    M_80211_RATECTL, M_NOWAIT|M_ZERO);
142 		if (amn == NULL) {
143 			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
144 			    "structure\n");
145 			return;
146 		}
147 	} else
148 		amn = ni->ni_rctls;
149 	amn->amn_amrr = amrr;
150 	amn->amn_success = 0;
151 	amn->amn_recovery = 0;
152 	amn->amn_txcnt = amn->amn_retrycnt = 0;
153 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
154 
155 	/* pick initial rate */
156 	for (amn->amn_rix = rs->rs_nrates - 1;
157 	     amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
158 	     amn->amn_rix--)
159 		;
160 	ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
161 	amn->amn_ticks = ticks;
162 
163 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
164 	    "AMRR initial rate %d", ni->ni_txrate);
165 }
166 
167 static void
168 amrr_node_deinit(struct ieee80211_node *ni)
169 {
170 	free(ni->ni_rctls, M_80211_RATECTL);
171 }
172 
173 static int
174 amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
175     struct ieee80211_node *ni)
176 {
177 	int rix = amn->amn_rix;
178 
179 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
180 
181 	if (is_success(amn)) {
182 		amn->amn_success++;
183 		if (amn->amn_success >= amn->amn_success_threshold &&
184 		    rix + 1 < ni->ni_rates.rs_nrates) {
185 			amn->amn_recovery = 1;
186 			amn->amn_success = 0;
187 			rix++;
188 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
189 			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
190 			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
191 			    amn->amn_txcnt, amn->amn_retrycnt);
192 		} else {
193 			amn->amn_recovery = 0;
194 		}
195 	} else if (is_failure(amn)) {
196 		amn->amn_success = 0;
197 		if (rix > 0) {
198 			if (amn->amn_recovery) {
199 				amn->amn_success_threshold *= 2;
200 				if (amn->amn_success_threshold >
201 				    amrr->amrr_max_success_threshold)
202 					amn->amn_success_threshold =
203 					    amrr->amrr_max_success_threshold;
204 			} else {
205 				amn->amn_success_threshold =
206 				    amrr->amrr_min_success_threshold;
207 			}
208 			rix--;
209 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
210 			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
211 			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
212 			    amn->amn_txcnt, amn->amn_retrycnt);
213 		}
214 		amn->amn_recovery = 0;
215 	}
216 
217 	/* reset counters */
218 	amn->amn_txcnt = 0;
219 	amn->amn_retrycnt = 0;
220 
221 	return rix;
222 }
223 
224 /*
225  * Return the rate index to use in sending a data frame.
226  * Update our internal state if it's been long enough.
227  * If the rate changes we also update ni_txrate to match.
228  */
229 static int
230 amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
231 {
232 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
233 	struct ieee80211_amrr *amrr = amn->amn_amrr;
234 	int rix;
235 
236 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
237 		rix = amrr_update(amrr, amn, ni);
238 		if (rix != amn->amn_rix) {
239 			/* update public rate */
240 			ni->ni_txrate =
241 			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
242 			amn->amn_rix = rix;
243 		}
244 		amn->amn_ticks = ticks;
245 	} else
246 		rix = amn->amn_rix;
247 	return rix;
248 }
249 
250 /*
251  * Update statistics with tx complete status.  Ok is non-zero
252  * if the packet is known to be ACK'd.  Retries has the number
253  * retransmissions (i.e. xmit attempts - 1).
254  */
255 static void
256 amrr_tx_complete(const struct ieee80211vap *vap,
257     const struct ieee80211_node *ni, int ok,
258     void *arg1, void *arg2 __unused)
259 {
260 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
261 	int retries = *(int *)arg1;
262 
263 	amn->amn_txcnt++;
264 	if (ok)
265 		amn->amn_success++;
266 	amn->amn_retrycnt += retries;
267 }
268 
269 /*
270  * Set tx count/retry statistics explicitly.  Intended for
271  * drivers that poll the device for statistics maintained
272  * in the device.
273  */
274 static void
275 amrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni,
276     void *arg1, void *arg2, void *arg3)
277 {
278 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
279 	int txcnt = *(int *)arg1, success = *(int *)arg2, retrycnt = *(int *)arg3;
280 
281 	amn->amn_txcnt = txcnt;
282 	amn->amn_success = success;
283 	amn->amn_retrycnt = retrycnt;
284 }
285 
286 static int
287 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
288 {
289 	struct ieee80211vap *vap = arg1;
290 	struct ieee80211_amrr *amrr = vap->iv_rs;
291 	int msecs = ticks_to_msecs(amrr->amrr_interval);
292 	int error;
293 
294 	error = sysctl_handle_int(oidp, &msecs, 0, req);
295 	if (error || !req->newptr)
296 		return error;
297 	amrr_setinterval(vap, msecs);
298 	return 0;
299 }
300 
301 static void
302 amrr_sysctlattach(struct ieee80211vap *vap,
303     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
304 {
305 	struct ieee80211_amrr *amrr = vap->iv_rs;
306 
307 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
308 	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
309 	    0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
310 	/* XXX bounds check values */
311 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
312 	    "amrr_max_sucess_threshold", CTLFLAG_RW,
313 	    &amrr->amrr_max_success_threshold, 0, "");
314 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
315 	    "amrr_min_sucess_threshold", CTLFLAG_RW,
316 	    &amrr->amrr_min_success_threshold, 0, "");
317 }
318