1 /* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */
4 * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
6 * Damien Bergamini <damien.bergamini@free.fr>
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.
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.
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD$");
25 * Naive implementation of the Adaptive Multi Rate Retry algorithm:
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
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
39 #include <sys/socket.h>
40 #include <sys/sysctl.h>
43 #include <net/if_var.h>
44 #include <net/if_media.h>
45 #include <net/ethernet.h>
47 #include <netproto/802_11/ieee80211_var.h>
48 #include <netproto/802_11/ieee80211_ht.h>
49 #include <netproto/802_11/ieee80211_amrr.h>
50 #include <netproto/802_11/ieee80211_ratectl.h>
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)
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,
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 static void amrr_node_stats(struct ieee80211_node
*ni
, struct sbuf
*s
);
76 /* number of references from net80211 layer */
79 static const struct ieee80211_ratectl amrr
= {
84 .ir_deinit
= amrr_deinit
,
85 .ir_node_init
= amrr_node_init
,
86 .ir_node_deinit
= amrr_node_deinit
,
88 .ir_tx_complete
= amrr_tx_complete
,
89 .ir_tx_update
= amrr_tx_update
,
90 .ir_setinterval
= amrr_setinterval
,
91 .ir_node_stats
= amrr_node_stats
,
93 IEEE80211_RATECTL_MODULE(amrr
, 1);
94 IEEE80211_RATECTL_ALG(amrr
, IEEE80211_RATECTL_AMRR
, amrr
);
97 amrr_setinterval(const struct ieee80211vap
*vap
, int msecs
)
99 struct ieee80211_amrr
*amrr
= vap
->iv_rs
;
104 t
= msecs_to_ticks(msecs
);
105 amrr
->amrr_interval
= (t
< 1) ? 1 : t
;
109 amrr_init(struct ieee80211vap
*vap
)
111 struct ieee80211_amrr
*amrr
;
113 KASSERT(vap
->iv_rs
== NULL
, ("%s called multiple times", __func__
));
115 #if defined(__DragonFly__)
116 amrr
= vap
->iv_rs
= kmalloc(sizeof(struct ieee80211_amrr
),
117 M_80211_RATECTL
, M_INTWAIT
|M_ZERO
);
119 amrr
= vap
->iv_rs
= IEEE80211_MALLOC(sizeof(struct ieee80211_amrr
),
120 M_80211_RATECTL
, IEEE80211_M_NOWAIT
| IEEE80211_M_ZERO
);
123 if_printf(vap
->iv_ifp
, "couldn't alloc ratectl structure\n");
126 amrr
->amrr_min_success_threshold
= IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD
;
127 amrr
->amrr_max_success_threshold
= IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD
;
128 amrr_setinterval(vap
, 500 /* ms */);
129 amrr_sysctlattach(vap
, vap
->iv_sysctl
, vap
->iv_oid
);
133 amrr_deinit(struct ieee80211vap
*vap
)
135 IEEE80211_FREE(vap
->iv_rs
, M_80211_RATECTL
);
139 * Return whether 11n rates are possible.
141 * Some 11n devices may return HT information but no HT rates.
142 * Thus, we shouldn't treat them as an 11n node.
145 amrr_node_is_11n(struct ieee80211_node
*ni
)
148 if (ni
->ni_chan
== NULL
)
150 if (ni
->ni_chan
== IEEE80211_CHAN_ANYC
)
152 if (IEEE80211_IS_CHAN_HT(ni
->ni_chan
) && ni
->ni_htrates
.rs_nrates
== 0)
154 return (IEEE80211_IS_CHAN_HT(ni
->ni_chan
));
158 amrr_node_init(struct ieee80211_node
*ni
)
160 const struct ieee80211_rateset
*rs
= NULL
;
161 struct ieee80211vap
*vap
= ni
->ni_vap
;
162 struct ieee80211_amrr
*amrr
= vap
->iv_rs
;
163 struct ieee80211_amrr_node
*amn
;
166 if (ni
->ni_rctls
== NULL
) {
167 #if defined(__DragonFly__)
168 ni
->ni_rctls
= amn
= kmalloc(sizeof(struct ieee80211_amrr_node
),
169 M_80211_RATECTL
, M_INTWAIT
|M_ZERO
);
171 ni
->ni_rctls
= amn
= IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node
),
172 M_80211_RATECTL
, IEEE80211_M_NOWAIT
| IEEE80211_M_ZERO
);
175 if_printf(vap
->iv_ifp
, "couldn't alloc per-node ratectl "
181 amn
->amn_amrr
= amrr
;
182 amn
->amn_success
= 0;
183 amn
->amn_recovery
= 0;
184 amn
->amn_txcnt
= amn
->amn_retrycnt
= 0;
185 amn
->amn_success_threshold
= amrr
->amrr_min_success_threshold
;
187 /* 11n or not? Pick the right rateset */
188 if (amrr_node_is_11n(ni
)) {
190 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
191 "%s: 11n node", __func__
);
192 rs
= (struct ieee80211_rateset
*) &ni
->ni_htrates
;
194 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
195 "%s: non-11n node", __func__
);
199 /* Initial rate - lowest */
200 rate
= rs
->rs_rates
[0];
202 /* XXX clear the basic rate flag if it's not 11n */
203 if (! amrr_node_is_11n(ni
))
204 rate
&= IEEE80211_RATE_VAL
;
206 /* pick initial rate from the rateset - HT or otherwise */
207 /* Pick something low that's likely to succeed */
208 for (amn
->amn_rix
= rs
->rs_nrates
- 1; amn
->amn_rix
> 0;
210 /* legacy - anything < 36mbit, stop searching */
211 /* 11n - stop at MCS4 */
212 if (amrr_node_is_11n(ni
)) {
213 if ((rs
->rs_rates
[amn
->amn_rix
] & 0x1f) < 4)
215 } else if ((rs
->rs_rates
[amn
->amn_rix
] & IEEE80211_RATE_VAL
) <= 72)
218 rate
= rs
->rs_rates
[amn
->amn_rix
] & IEEE80211_RATE_VAL
;
220 /* if the rate is an 11n rate, ensure the MCS bit is set */
221 if (amrr_node_is_11n(ni
))
222 rate
|= IEEE80211_RATE_MCS
;
224 /* Assign initial rate from the rateset */
225 ni
->ni_txrate
= rate
;
226 amn
->amn_ticks
= ticks
;
228 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
229 "AMRR: nrates=%d, initial rate %d",
235 amrr_node_deinit(struct ieee80211_node
*ni
)
237 IEEE80211_FREE(ni
->ni_rctls
, M_80211_RATECTL
);
241 amrr_update(struct ieee80211_amrr
*amrr
, struct ieee80211_amrr_node
*amn
,
242 struct ieee80211_node
*ni
)
244 int rix
= amn
->amn_rix
;
245 const struct ieee80211_rateset
*rs
= NULL
;
247 KASSERT(is_enough(amn
), ("txcnt %d", amn
->amn_txcnt
));
249 /* 11n or not? Pick the right rateset */
250 if (amrr_node_is_11n(ni
)) {
252 rs
= (struct ieee80211_rateset
*) &ni
->ni_htrates
;
257 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
258 "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
259 rs
->rs_rates
[rix
] & IEEE80211_RATE_VAL
,
264 * XXX This is totally bogus for 11n, as although high MCS
265 * rates for each stream may be failing, the next stream
268 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
269 * MCS23, we should skip 6/7 and try 8 onwards.
271 if (is_success(amn
)) {
273 if (amn
->amn_success
>= amn
->amn_success_threshold
&&
274 rix
+ 1 < rs
->rs_nrates
) {
275 amn
->amn_recovery
= 1;
276 amn
->amn_success
= 0;
278 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
279 "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
280 rs
->rs_rates
[rix
] & IEEE80211_RATE_VAL
,
281 amn
->amn_txcnt
, amn
->amn_retrycnt
);
283 amn
->amn_recovery
= 0;
285 } else if (is_failure(amn
)) {
286 amn
->amn_success
= 0;
288 if (amn
->amn_recovery
) {
289 amn
->amn_success_threshold
*= 2;
290 if (amn
->amn_success_threshold
>
291 amrr
->amrr_max_success_threshold
)
292 amn
->amn_success_threshold
=
293 amrr
->amrr_max_success_threshold
;
295 amn
->amn_success_threshold
=
296 amrr
->amrr_min_success_threshold
;
299 IEEE80211_NOTE(ni
->ni_vap
, IEEE80211_MSG_RATECTL
, ni
,
300 "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
301 rs
->rs_rates
[rix
] & IEEE80211_RATE_VAL
,
302 amn
->amn_txcnt
, amn
->amn_retrycnt
);
304 amn
->amn_recovery
= 0;
309 amn
->amn_retrycnt
= 0;
315 * Return the rate index to use in sending a data frame.
316 * Update our internal state if it's been long enough.
317 * If the rate changes we also update ni_txrate to match.
320 amrr_rate(struct ieee80211_node
*ni
, void *arg __unused
, uint32_t iarg __unused
)
322 struct ieee80211_amrr_node
*amn
= ni
->ni_rctls
;
323 struct ieee80211_amrr
*amrr
= amn
->amn_amrr
;
324 const struct ieee80211_rateset
*rs
= NULL
;
327 /* 11n or not? Pick the right rateset */
328 if (amrr_node_is_11n(ni
)) {
330 rs
= (struct ieee80211_rateset
*) &ni
->ni_htrates
;
335 if (is_enough(amn
) && (ticks
- amn
->amn_ticks
) > amrr
->amrr_interval
) {
336 rix
= amrr_update(amrr
, amn
, ni
);
337 if (rix
!= amn
->amn_rix
) {
338 /* update public rate */
339 ni
->ni_txrate
= rs
->rs_rates
[rix
];
340 /* XXX strip basic rate flag from txrate, if non-11n */
341 if (amrr_node_is_11n(ni
))
342 ni
->ni_txrate
|= IEEE80211_RATE_MCS
;
344 ni
->ni_txrate
&= IEEE80211_RATE_VAL
;
347 amn
->amn_ticks
= ticks
;
354 * Update statistics with tx complete status. Ok is non-zero
355 * if the packet is known to be ACK'd. Retries has the number
356 * retransmissions (i.e. xmit attempts - 1).
359 amrr_tx_complete(const struct ieee80211vap
*vap
,
360 const struct ieee80211_node
*ni
, int ok
,
361 void *arg1
, void *arg2 __unused
)
363 struct ieee80211_amrr_node
*amn
= ni
->ni_rctls
;
364 int retries
= *(int *)arg1
;
369 amn
->amn_retrycnt
+= retries
;
373 * Set tx count/retry statistics explicitly. Intended for
374 * drivers that poll the device for statistics maintained
378 amrr_tx_update(const struct ieee80211vap
*vap
, const struct ieee80211_node
*ni
,
379 void *arg1
, void *arg2
, void *arg3
)
381 struct ieee80211_amrr_node
*amn
= ni
->ni_rctls
;
382 int txcnt
= *(int *)arg1
, success
= *(int *)arg2
, retrycnt
= *(int *)arg3
;
384 amn
->amn_txcnt
= txcnt
;
385 amn
->amn_success
= success
;
386 amn
->amn_retrycnt
= retrycnt
;
390 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS
)
392 struct ieee80211vap
*vap
= arg1
;
393 struct ieee80211_amrr
*amrr
= vap
->iv_rs
;
394 int msecs
= ticks_to_msecs(amrr
->amrr_interval
);
397 error
= sysctl_handle_int(oidp
, &msecs
, 0, req
);
398 if (error
|| !req
->newptr
)
400 amrr_setinterval(vap
, msecs
);
405 amrr_sysctlattach(struct ieee80211vap
*vap
,
406 struct sysctl_ctx_list
*ctx
, struct sysctl_oid
*tree
)
408 struct ieee80211_amrr
*amrr
= vap
->iv_rs
;
410 SYSCTL_ADD_PROC(ctx
, SYSCTL_CHILDREN(tree
), OID_AUTO
,
411 "amrr_rate_interval", CTLTYPE_INT
| CTLFLAG_RW
, vap
,
412 0, amrr_sysctl_interval
, "I", "amrr operation interval (ms)");
413 /* XXX bounds check values */
414 SYSCTL_ADD_UINT(ctx
, SYSCTL_CHILDREN(tree
), OID_AUTO
,
415 "amrr_max_sucess_threshold", CTLFLAG_RW
,
416 &amrr
->amrr_max_success_threshold
, 0, "");
417 SYSCTL_ADD_UINT(ctx
, SYSCTL_CHILDREN(tree
), OID_AUTO
,
418 "amrr_min_sucess_threshold", CTLFLAG_RW
,
419 &amrr
->amrr_min_success_threshold
, 0, "");
423 amrr_node_stats(struct ieee80211_node
*ni
, struct sbuf
*s
)
426 struct ieee80211_amrr_node
*amn
= ni
->ni_rctls
;
427 struct ieee80211_rateset
*rs
;
429 /* XXX TODO: check locking? */
431 /* XXX TODO: this should be a method */
432 if (amrr_node_is_11n(ni
)) {
433 rs
= (struct ieee80211_rateset
*) &ni
->ni_htrates
;
434 rate
= rs
->rs_rates
[amn
->amn_rix
] & IEEE80211_RATE_VAL
;
435 sbuf_printf(s
, "rate: MCS %d\n", rate
);
438 rate
= rs
->rs_rates
[amn
->amn_rix
] & IEEE80211_RATE_VAL
;
439 sbuf_printf(s
, "rate: %d Mbit\n", rate
/ 2);
442 sbuf_printf(s
, "ticks: %d\n", amn
->amn_ticks
);
443 sbuf_printf(s
, "txcnt: %u\n", amn
->amn_txcnt
);
444 sbuf_printf(s
, "success: %u\n", amn
->amn_success
);
445 sbuf_printf(s
, "success_threshold: %u\n", amn
->amn_success_threshold
);
446 sbuf_printf(s
, "recovery: %u\n", amn
->amn_recovery
);
447 sbuf_printf(s
, "retry_cnt: %u\n", amn
->amn_retrycnt
);