2 * Copyright (c) 2004 INRIA
3 * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer,
11 * without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 * redistribution must be conditioned upon including a substantially
15 * similar Disclaimer requirement for further binary redistribution.
16 * 3. Neither the names of the above-listed copyright holders nor the names
17 * of any contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
30 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
33 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGES.
37 * $FreeBSD: src/sys/dev/ath/ath_rate/amrr/amrr.c,v 1.8.2.3 2006/02/24 19:51:11 sam Exp $
38 * $DragonFly: src/sys/netproto/802_11/wlan_ratectl/amrr/ieee80211_ratectl_amrr.c,v 1.3 2006/09/05 03:48:12 dillon Exp $
42 * AMRR rate control. See:
43 * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
44 * "IEEE 802.11 Rate Adaptation: A Practical Approach" by
45 * Mathieu Lacage, Hossein Manshaei, Thierry Turletti
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/module.h>
52 #include <sys/sysctl.h>
53 #include <sys/serialize.h>
56 #include <net/if_media.h>
57 #include <net/if_arp.h>
59 #include <netproto/802_11/ieee80211_var.h>
60 #include <netproto/802_11/wlan_ratectl/amrr/ieee80211_ratectl_amrr.h>
64 #define DPRINTF(asc, lv, fmt, ...) do { \
65 if ((asc)->debug >= lv) \
66 printf(fmt, __VA_ARGS__); \
69 #define DPRINTF(asc, lv, fmt, ...)
72 static void *amrr_attach(struct ieee80211com *);
73 static void amrr_detach(void *);
74 static void amrr_data_alloc(struct ieee80211_node *);
75 static void amrr_data_free(struct ieee80211_node *);
76 static void amrr_data_dup(const struct ieee80211_node *,
77 struct ieee80211_node *);
78 static void amrr_newstate(void *, enum ieee80211_state);
79 static void amrr_tx_complete(void *, struct ieee80211_node *, int,
80 const struct ieee80211_ratectl_res[],
82 static void amrr_newassoc(void *, struct ieee80211_node *, int);
83 static int amrr_findrate(void *, struct ieee80211_node *, int,
86 static void amrr_sysctl_attach(struct amrr_softc *);
87 static void amrr_update(struct amrr_softc *, struct ieee80211_node *, int);
88 static void amrr_start(struct amrr_softc *, struct ieee80211_node *);
89 static void amrr_tick(void *);
90 static void amrr_ratectl(void *, struct ieee80211_node *);
92 static const struct ieee80211_ratectl amrr = {
94 .rc_ratectl = IEEE80211_RATECTL_AMRR,
95 .rc_attach = amrr_attach,
96 .rc_detach = amrr_detach,
97 .rc_data_alloc = amrr_data_alloc,
98 .rc_data_free = amrr_data_free,
99 .rc_data_dup = amrr_data_dup,
100 .rc_newstate = amrr_newstate,
101 .rc_tx_complete = amrr_tx_complete,
102 .rc_newassoc = amrr_newassoc,
103 .rc_findrate = amrr_findrate
106 static u_int amrr_nrefs;
108 MALLOC_DEFINE(M_AMRR_RATECTL_DATA, "amrr_ratectl_data",
109 "amrr rate control data");
112 amrr_findrate(void *arg __unused, struct ieee80211_node *ni,
113 int frame_len __unused, int rateidx[], int rateidx_len)
115 int i, rate_idx = ni->ni_txrate;
117 for (i = 0; i < rateidx_len && i < IEEE80211_AMRR_RATEIDX_MAX; ++i) {
120 rateidx[i] = rate_idx--;
123 rateidx[rateidx_len - 1] = 0;
128 amrr_tx_complete(void *arg __unused, struct ieee80211_node *ni,
129 int frame_len __unused,
130 const struct ieee80211_ratectl_res res[],
131 int res_len, int short_retries __unused,
132 int long_retries __unused, int is_fail)
134 struct amrr_data *ad = ni->ni_rate_data;
140 for (i = 0; i < res_len && i < IEEE80211_AMRR_RATEIDX_MAX; ++i)
141 ad->ad_tx_try_cnt[i]++;
143 ad->ad_tx_failure_cnt++;
147 amrr_newassoc(void *arg, struct ieee80211_node *ni, int isnew)
154 * The code below assumes that we are dealing with hardware multi rate retry
155 * I have no idea what will happen if you try to use this module with another
156 * type of hardware. Your machine might catch fire or it might work with
157 * horrible performance...
160 amrr_update(struct amrr_softc *asc, struct ieee80211_node *ni, int rate)
162 struct amrr_data *ad = ni->ni_rate_data;
164 DPRINTF(asc, 5, "%s: set xmit rate for %6D to %dM\n",
165 __func__, ni->ni_macaddr, ":",
166 ni->ni_rates.rs_nrates > 0 ?
167 IEEE80211_RS_RATE(&ni->ni_rates, rate) / 2 : 0);
169 ni->ni_txrate = rate;
173 ad = ni->ni_rate_data;
178 ad->ad_tx_try_cnt[0] = 0;
179 ad->ad_tx_try_cnt[1] = 0;
180 ad->ad_tx_try_cnt[2] = 0;
181 ad->ad_tx_try_cnt[3] = 0;
182 ad->ad_tx_failure_cnt = 0;
185 ad->ad_success_threshold = asc->min_success_threshold;
189 * Set the starting transmit rate for a node.
192 amrr_start(struct amrr_softc *asc, struct ieee80211_node *ni)
194 #define RATE(_ix) IEEE80211_RS_RATE(&ni->ni_rates, (_ix))
195 struct ieee80211com *ic = asc->ic;
198 KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
200 if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
202 * For adhoc or ibss mode, start from the lowest rate.
204 if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
205 ic->ic_opmode == IEEE80211_M_IBSS) {
206 amrr_update(asc, ni, 0);
211 * No fixed rate is requested. For 11b start with
212 * the highest negotiated rate; otherwise, for 11g
213 * and 11a, we start "in the middle" at 24Mb or 36Mb.
215 srate = ni->ni_rates.rs_nrates - 1;
216 if (ic->ic_curmode != IEEE80211_MODE_11B) {
218 * Scan the negotiated rate set to find the
221 /* NB: the rate set is assumed sorted */
222 for (; srate >= 0 && RATE(srate) > 72; srate--)
224 KASSERT(srate >= 0, ("bogus rate set"));
228 * A fixed rate is to be used; ic_fixed_rate is an
229 * index into the supported rate set. Convert this
230 * to the index into the negotiated rate set for
231 * the node. We know the rate is there because the
232 * rate set is checked when the station associates.
234 const struct ieee80211_rateset *rs =
235 &ic->ic_sup_rates[ic->ic_curmode];
236 int r = IEEE80211_RS_RATE(rs, ic->ic_fixed_rate);
238 /* NB: the rate set is assumed sorted */
239 srate = ni->ni_rates.rs_nrates - 1;
240 for (; srate >= 0 && RATE(srate) != r; srate--)
243 ("fixed rate %d not in rate set", ic->ic_fixed_rate));
245 amrr_update(asc, ni, srate);
250 amrr_rate_cb(void *arg, struct ieee80211_node *ni)
252 amrr_update(arg, ni, 0);
256 * Reset the rate control state for each 802.11 state transition.
259 amrr_newstate(void *arg, enum ieee80211_state state)
261 struct amrr_softc *asc = arg;
262 struct ieee80211com *ic = asc->ic;
263 struct ieee80211_node *ni;
265 if (state == IEEE80211_S_INIT) {
266 callout_stop(&asc->timer);
270 if (ic->ic_opmode == IEEE80211_M_STA) {
272 * Reset local xmit state; this is really only
273 * meaningful when operating in station mode.
276 if (state == IEEE80211_S_RUN)
279 amrr_update(asc, ni, 0);
282 * When operating as a station the node table holds
283 * the AP's that were discovered during scanning.
284 * For any other operating mode we want to reset the
285 * tx rate state of each node.
287 ieee80211_iterate_nodes(&ic->ic_sta, amrr_rate_cb, asc);
288 amrr_update(asc, ic->ic_bss, 0);
290 if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE &&
291 state == IEEE80211_S_RUN) {
295 * Start the background rate control thread if we
296 * are not configured to use a fixed xmit rate.
298 interval = asc->interval;
299 if (ic->ic_opmode == IEEE80211_M_STA)
301 callout_reset(&asc->timer, (interval * hz) / 1000,
307 * Examine and potentially adjust the transmit rate.
310 amrr_ratectl(void *arg, struct ieee80211_node *ni)
312 struct amrr_softc *asc = arg;
313 struct amrr_data *ad = ni->ni_rate_data;
317 /* We are not ready to go, set TX rate to lowest one */
322 #define is_success(ad) \
323 (ad->ad_tx_try_cnt[1] < (ad->ad_tx_try_cnt[0] / 10))
324 #define is_enough(ad) \
325 (ad->ad_tx_try_cnt[0] > 10)
326 #define is_failure(ad) \
327 (ad->ad_tx_try_cnt[1] > (ad->ad_tx_try_cnt[0] / 3))
328 #define is_max_rate(ni) \
329 ((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)
330 #define is_min_rate(ni) \
333 old_rate = ni->ni_txrate;
335 DPRINTF(asc, 10, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- "
337 ad->ad_tx_try_cnt[0],
338 ad->ad_tx_try_cnt[1],
339 ad->ad_tx_try_cnt[2],
340 ad->ad_tx_try_cnt[3],
341 ad->ad_success_threshold);
343 if (is_success(ad) && is_enough(ad)) {
345 if (ad->ad_success == ad->ad_success_threshold &&
350 DPRINTF(asc, 5, "increase rate to %d\n", ni->ni_txrate);
354 } else if (is_failure(ad)) {
356 if (!is_min_rate(ni)) {
357 if (ad->ad_recovery) {
358 /* recovery failure. */
359 ad->ad_success_threshold *= 2;
360 ad->ad_success_threshold =
361 min(ad->ad_success_threshold,
362 (u_int)asc->max_success_threshold);
363 DPRINTF(asc, 5, "decrease rate recovery thr: "
364 "%d\n", ad->ad_success_threshold);
366 /* simple failure. */
367 ad->ad_success_threshold =
368 asc->min_success_threshold;
369 DPRINTF(asc, 5, "decrease rate normal thr: "
370 "%d\n", ad->ad_success_threshold);
378 if (is_enough(ad) || old_rate != ni->ni_txrate) {
379 /* reset counters. */
380 ad->ad_tx_try_cnt[0] = 0;
381 ad->ad_tx_try_cnt[1] = 0;
382 ad->ad_tx_try_cnt[2] = 0;
383 ad->ad_tx_try_cnt[3] = 0;
384 ad->ad_tx_failure_cnt = 0;
386 if (old_rate != ni->ni_txrate)
387 amrr_update(asc, ni, ni->ni_txrate);
393 struct amrr_softc *asc = arg;
394 struct ieee80211com *ic = asc->ic;
395 struct ifnet *ifp = &ic->ic_if;
398 lwkt_serialize_enter(ifp->if_serializer);
400 if (ifp->if_flags & IFF_RUNNING) {
401 if (ic->ic_opmode == IEEE80211_M_STA)
402 amrr_ratectl(asc, ic->ic_bss); /* NB: no reference */
404 ieee80211_iterate_nodes(&ic->ic_sta, amrr_ratectl, asc);
406 interval = asc->interval;
407 if (ic->ic_opmode == IEEE80211_M_STA)
409 callout_reset(&asc->timer, (interval * hz) / 1000, amrr_tick, asc);
411 lwkt_serialize_exit(ifp->if_serializer);
415 amrr_sysctl_attach(struct amrr_softc *asc)
418 asc->interval = 1000;
419 asc->max_success_threshold = 10;
420 asc->min_success_threshold = 1;
422 sysctl_ctx_init(&asc->sysctl_ctx);
423 asc->sysctl_oid = SYSCTL_ADD_NODE(&asc->sysctl_ctx,
424 SYSCTL_CHILDREN(asc->ic->ic_sysctl_oid),
425 OID_AUTO, "amrr_ratectl", CTLFLAG_RD, 0, "");
426 if (asc->sysctl_oid == NULL) {
427 printf("wlan_ratectl_amrr: create sysctl tree failed\n");
431 SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
432 OID_AUTO, "interval", CTLFLAG_RW,
434 "rate control: operation interval (ms)");
436 /* XXX bounds check values */
437 SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
438 OID_AUTO, "max_sucess_threshold", CTLFLAG_RW,
439 &asc->max_success_threshold, 0, "");
441 SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
442 OID_AUTO, "min_sucess_threshold", CTLFLAG_RW,
443 &asc->min_success_threshold, 0, "");
445 SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
446 OID_AUTO, "debug", CTLFLAG_RW,
447 &asc->debug, 0, "debug level");
451 amrr_attach(struct ieee80211com *ic)
453 struct amrr_softc *asc;
457 asc = kmalloc(sizeof(struct amrr_softc), M_DEVBUF, M_WAITOK | M_ZERO);
460 callout_init(&asc->timer);
461 amrr_sysctl_attach(asc);
463 amrr_newstate(asc, ic->ic_state);
469 _amrr_data_free(void *arg __unused, struct ieee80211_node *ni)
475 amrr_detach(void *arg)
477 struct amrr_softc *asc = arg;
478 struct ieee80211com *ic = asc->ic;
480 amrr_newstate(asc, IEEE80211_S_INIT);
482 ieee80211_iterate_nodes(&ic->ic_sta, _amrr_data_free, NULL);
483 ieee80211_iterate_nodes(&ic->ic_scan, _amrr_data_free, NULL);
485 if (asc->sysctl_oid != NULL)
486 sysctl_ctx_free(&asc->sysctl_ctx);
487 kfree(asc, M_DEVBUF);
493 amrr_data_free(struct ieee80211_node *ni)
495 if (ni->ni_rate_data != NULL) {
496 kfree(ni->ni_rate_data, M_AMRR_RATECTL_DATA);
497 ni->ni_rate_data = NULL;
502 amrr_data_alloc(struct ieee80211_node *ni)
504 KKASSERT(ni->ni_rate_data == NULL);
505 ni->ni_rate_data = kmalloc(sizeof(struct amrr_data),
506 M_AMRR_RATECTL_DATA, M_NOWAIT | M_ZERO);
510 amrr_data_dup(const struct ieee80211_node *oni, struct ieee80211_node *nni)
512 if (oni->ni_rate_data == NULL || nni->ni_rate_data == NULL)
515 bcopy(oni->ni_rate_data, nni->ni_rate_data, sizeof(struct amrr_data));
522 amrr_modevent(module_t mod, int type, void *unused)
526 ieee80211_ratectl_register(&amrr);
530 printf("wlan_ratectl_amrr: still in use "
531 "(%u dynamic refs)\n", amrr_nrefs);
534 ieee80211_ratectl_unregister(&amrr);
540 static moduledata_t amrr_mod = {
545 DECLARE_MODULE(wlan_ratectl_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
546 MODULE_VERSION(wlan_ratectl_amrr, 1);
547 MODULE_DEPEND(wlan_ratectl_amrr, wlan, 1, 1, 1);