source: rtems-libbsd/freebsd/sys/net80211/ieee80211_amrr.c @ a241ea8

55-freebsd-126-freebsd-12
Last change on this file since a241ea8 was a241ea8, checked in by Christian Mauderer <Christian.Mauderer@…>, on 11/14/16 at 12:46:13

Import IEEE 802.11 from FreeBSD.

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