Date: Sun, 15 Sep 2013 12:08:37 +0800 From: Chenchong Qin <qinchenchong@gmail.com> To: Adrian Chadd <adrian@freebsd.org> Cc: "freebsd-wireless@freebsd.org" <freebsd-wireless@freebsd.org> Subject: Re: Chenchong's work on net80211_ratectl Message-ID: <CAFnsE3fnApV_=FNggEuhy1B=idXLxgymqqmJ=9XXrjspLoe%2BWA@mail.gmail.com> In-Reply-To: <CAJ-Vmonbj9KE_oM6xXPnKksA1u=9FH41e4_bQE1cgwebydZgtA@mail.gmail.com> References: <CAFnsE3dYdPf5yGTFH683Q1Zh0mc-g%2B_YtCTraNNt28z2vBoSKw@mail.gmail.com> <CAJ-Vmom4sY7jcNwWmJkrDwfWjsok2fk8UEwTi5A=egj1JyerLw@mail.gmail.com> <CAFnsE3cyg=msBfQqqKUMmLABSL=j24VoMBwbBjxQ6b7Dyy7Mqg@mail.gmail.com> <CAJ-Vmo=k8NddAYyAJCkx4eOaA_8XsSxg6uKrdddx%2BgmeT%2BX9KA@mail.gmail.com> <CAFnsE3eaOyRcO3LXSi3L=jbzpyMv5Nt_jRGKt_mmA0WV-EV5vA@mail.gmail.com> <CAJ-VmokdxLhK5x6kO=jJzk-dv61EDK-ZgmndOimoyWWf76HiZA@mail.gmail.com> <CAJ-VmonMjR5iVTMVN9532d%2BPqOXWNUoZvxPtQir5h=yGxU-XdQ@mail.gmail.com> <CAFnsE3d9nG-X2b=z1srKfTtpxC3w5L%2B6Hg3TbOnAQrJN%2Bt19GA@mail.gmail.com> <CAJ-VmokF6hPtg9FoEdeJXLLaZRNhzd=nr_o6nHE%2BjYiQKTg3zQ@mail.gmail.com> <CAFnsE3eMwX-GiRzJt8jk4r9mxwSAQkcrDwk%2BnWVG7q6dabeA3A@mail.gmail.com> <CAJ-Vmo=mzvS0UBC7fGx2t501%2Bfioi4DJcw8qobOpbYOUiraqGg@mail.gmail.com> <CAFnsE3df=1WEuLZh5355v_K2eBgcuBbpoza74Y-5vvNupBz22A@mail.gmail.com> <CAJ-VmokyXwkKLdsJw74bux7G5EJSRvFhugTcLR9BgXfw4ysYRg@mail.gmail.com> <CAJ-VmokU=ZXysjZfAJ-REZL7kwg-_Z-LeAKca7AefONW_O1E5A@mail.gmail.com> <CAFnsE3cS_2Ad1geQQ0UB4doEgqVfhNBMi6j4fCRpFgQqcu2kJw@mail.gmail.com> <CAFnsE3eMrwpo=hcFb9XfpLL53Ppso%2B%2BXTBfpieP8FGejAKW1_w@mail.gmail.com> <CAJ-Vmo=s=0u7VO00vgzxmR7bE4aTtMrshZ3j5F312dya284V1g@mail.gmail.com> <CAFnsE3c%2BOg_2hVXO3zU7gmY4xFQjnxbv=ehJm=pbbpLJcgwJrQ@mail.gmail.com> <CAJ-Vmo=D3BNhhSn9D3x6hBry=fDecOd7BwttQ5Yw2iUKGDBN3w@mail.gmail.com> <CAFnsE3dw5bOyVRqMb9K3ME%2BHoDL61-e_XNJjjt_nRTaY=4dnhw@mail.gmail.com> <CAJ-Vmo=0tkTKQnOjrmYKqWqC%2B_nnFLqimdp06STETxLsFgxKxQ@mail.gmail.com> <CAFnsE3dm27483GVp4PRiwk=sxagT7NXQz4fmyOpth2zpCM2MwQ@mail.gmail.com> <CAFnsE3fvpjCXEC1gN46VXfPdeXG5RSVzNiJkZJiQ6xSYQFb89g@mail.gmail.com> <CAFnsE3e1r6Ah5SSrqnrxoqXMRm3yCaUxCjSD90epWQJEZU-pzw@mail.gmail.com> <CAJ-Vmok3dAHiwbeyEU6B4oWm=n36ME2yyGE69twjdEygr8iZSw@mail.gmail.com> <CAFnsE3dxXr05%2Bv7F2hF7Qauoz-qqDzxkXiZrhUAtaxf0ObNhDg@mail.gmail.com> <CAJ-Vmo=Se2JziV8D2PYNgxmZro-3JRpp3ZsRkTTWvUQ0YigRPw@mail.gmail.com> <CAFnsE3cFFHxOwLY2OFxhCUAM-Bg2zZvjrC8LMx17N-dfND74KQ@mail.gmail.com> <CAJ-Vmonbj9KE_oM6xXPnKksA1u=9FH41e4_bQE1cgwebydZgtA@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --] Hi! Yes, a call to ieee80211_ratectl_rc_info_set() is needed. To make other drivers work, the __init__ and __findrate__ parts also need to be adapted. When initialize the ratectl state, a cap flag must be properly set and feed to ieee80211_ratectl_init(). __findrate__ part should be repalced with our ieee80211_ratectl_rates(). I've added ieee80211_ratectl_rc_info_get() to be used to get the ieee80211_rc_info. If found the tag, use it; if not, add a new one and use it. Then we don't need to free it explicitly (the tag is freed when associated mbuf is freed) and this interface is unified to both __findrate__ and __complete__. Thanks! Chenchong On Sat, Sep 14, 2013 at 11:21 PM, Adrian Chadd <adrian@freebsd.org> wrote: > Ah, cool! I see you've only just made the other drivers compile; what's > required to make them work? i guess a call > to ieee80211_ratectl_rc_info_set() ? > > Maybe you should add a ieee80211_ratectl_rc_info_set_mbuf() helper that > does the "lookup tag; if one exists use it else use a temporary one" code > that you put in if_ath.c, if_ath_tx.c. > > Other than that, this is looking very good! thankyou! > > > -adrian > > > > On 13 September 2013 20:52, Chenchong Qin <qinchenchong@gmail.com> wrote: > >> Hi, >> >> Here is latest update. Per-device ratectl statistics is implemented in >> ath and attached when ath is attaching. >> >> Thanks! >> >> Chenchong >> >> >> On Sat, Sep 14, 2013 at 3:37 AM, Adrian Chadd <adrian@freebsd.org> wrote: >> >>> Sweet, thanks! >>> >>> >>> >>> -adrian >>> >>> >>> >>> On 13 September 2013 09:11, Chenchong Qin <qinchenchong@gmail.com>wrote: >>> >>>> Hi! >>>> >>>> Here is some updates. >>>> >>>> Another member is added to ieee80211_rc_info to record value of the >>>> maximum aggregate size. Then, in aggregation scenario, ratectl algo can >>>> inform aggregation selection code of proper maximum aggregate size. >>>> >>>> Per-vap ratectl statistics is exported through sysctl. When >>>> ieee80211_ratectl_init() is called, this statistics api is attached. It's >>>> convenient to implement the per-device api -- just traverse the vap list >>>> and call per-vap api for each vap. But, we know that ratectl of net80211 >>>> provides service to vap-granularity object, not to device directly. So, is >>>> it more suitable to implement the per-device api in device driver (i.e. >>>> attach per-device api when attaching the device)? >>>> >>>> Code will be posted later. >>>> >>>> Thanks! >>>> >>>> Chenchong >>>> >>>> >>>> On Thu, Sep 12, 2013 at 2:05 AM, Adrian Chadd <adrian@freebsd.org>wrote: >>>> >>>>> Hi, >>>>> >>>>> For now, yes, you have to assume that you won't always get a response >>>>> for a rate lookup. The buffer may be sent with NOACK set, it may be deleted >>>>> during a channel change or scan, etc. >>>>> >>>>> And yes - the rate control lookup stuff for aggregate frames is a bit >>>>> messy. It would be nice for the rate control code to return the rate _and_ >>>>> the maximum aggregate size, in case the aggregation selection wants to cap >>>>> how long frames are at the given choice. >>>>> >>>>> >>>>> >>>>> -adrian >>>>> >>>>> >>>>> >>>>> On 11 September 2013 10:29, Chenchong Qin <qinchenchong@gmail.com>wrote: >>>>> >>>>>> Hi! >>>>>> >>>>>> I've added some aggregation support here! >>>>>> >>>>>> At first I intend to pass subframe informations(nframes, per-subframe >>>>>> length etc.) >>>>>> to the ratectl api. But it seems to be a paradox that rate lookup >>>>>> must be performed >>>>>> before the ampdu is formed (aggregation limit based on the rate >>>>>> control decision >>>>>> is need) and subframe informations can be obtain only after the ampdu >>>>>> is formed. >>>>>> So, I add a new ieee80211_rc_info flag to ieee80211_ratectl to let it >>>>>> distinguish >>>>>> aggregation and non-aggregation scenarios. If rate lookup is called >>>>>> in an aggregation >>>>>> scenario, this flag is set. Then, ratectl algo knows that it's now >>>>>> finding rates for an >>>>>> ampdu and the framelen which records len of the first frame can be >>>>>> ignored. When >>>>>> it comes to complete period, tx status that shows number of subframes >>>>>> been sent >>>>>> and number of subframes been successfully received is passed to the >>>>>> ratectl api. >>>>>> >>>>>> I also get a question here - whether one tx that doesn't perform rate >>>>>> lookup will call >>>>>> the complete procedure? >>>>>> >>>>>> Thanks! >>>>>> >>>>>> Chenchong >>>>>> >>>>>> >>>>>> On Sun, Sep 8, 2013 at 11:18 PM, Chenchong Qin < >>>>>> qinchenchong@gmail.com> wrote: >>>>>> >>>>>>> Hi! >>>>>>> >>>>>>> I've added the common ratectl state as an mbuf tag! >>>>>>> >>>>>>> After days of frustration (compile errors, boot failed, kernel >>>>>>> panics, suddenly kernel freezing...), it seems that ath now can use >>>>>>> 11n-aware net80211 ratectl api to do rate control. Attachment[0] is the >>>>>>> diff of modifications to dev/ath. Changes to net80211 is minor this time. >>>>>>> Just add some debug msgs to it. Please reference my gsoc svn repo<https://svnweb.freebsd.org/socsvn/soc2013/ccqin/head/> >>>>>>> . >>>>>>> >>>>>>> It's worth mentioning that sometimes the kernel will "freezing" (it >>>>>>> looks like all things stop working, screen is freezing, keyboard and mouse >>>>>>> are not responding) after wireless stuff start working for a while. At >>>>>>> first, I consider it caused by my modification to ath. But this strange >>>>>>> thing can also happen in a head kernel (r255382). Attachment[1] is some >>>>>>> useful messages just before it happens. By the way, I use a AR9227 device. >>>>>>> >>>>>>> And, I found that, for aggregation scenario, ath gathers tx >>>>>>> information and update the ratectl states. So, what we can do to net80211 >>>>>>> to let it support aggregation? >>>>>>> >>>>>>> Thanks! >>>>>>> >>>>>>> Chenchong >>>>>>> >>>>>>> >>>>>>> On Tue, Sep 3, 2013 at 9:29 AM, Chenchong Qin < >>>>>>> qinchenchong@gmail.com> wrote: >>>>>>> >>>>>>>> OK! >>>>>>>> >>>>>>>> Thanks! :-) >>>>>>>> >>>>>>>> Chenchong >>>>>>>> >>>>>>>> >>>>>>>> On Tue, Sep 3, 2013 at 1:56 AM, Adrian Chadd <adrian@freebsd.org>wrote: >>>>>>>> >>>>>>>>> Hi! >>>>>>>>> >>>>>>>>> You can declare an mbuf tag and use that. Look at M_TXCB in >>>>>>>>> net80211 and how mbuf tags are added. >>>>>>>>> >>>>>>>>> I've long thought about adding a net80211 mbuf tag to represent >>>>>>>>> -all- of the tx related state - TX callback, rate control, rate completion, >>>>>>>>> aggregation state, retry count, etc. That way all the drivers can use it. >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> -adrian >>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>> >>>>>> >>>>> >>>> >>> >> > [-- Attachment #2 --] Index: net80211/ieee80211_amrr.c =================================================================== --- net80211/ieee80211_amrr.c (revision 254826) +++ net80211/ieee80211_amrr.c (working copy) @@ -47,8 +47,8 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ht.h> +#include <net80211/ieee80211_ratectl.h> #include <net80211/ieee80211_amrr.h> -#include <net80211/ieee80211_ratectl.h> #define is_success(amn) \ ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) @@ -58,18 +58,19 @@ ((amn)->amn_txcnt > 10) static void amrr_setinterval(const struct ieee80211vap *, int); -static void amrr_init(struct ieee80211vap *); +static void amrr_init(struct ieee80211vap *, uint32_t); static void amrr_deinit(struct ieee80211vap *); static void amrr_node_init(struct ieee80211_node *); static void amrr_node_deinit(struct ieee80211_node *); static int amrr_update(struct ieee80211_amrr *, struct ieee80211_amrr_node *, struct ieee80211_node *); static int amrr_rate(struct ieee80211_node *, void *, uint32_t); +static void amrr_rates(struct ieee80211_node *, struct ieee80211_rc_info *); static void amrr_tx_complete(const struct ieee80211vap *, - const struct ieee80211_node *, int, - void *, void *); -static void amrr_tx_update(const struct ieee80211vap *vap, + const struct ieee80211_node *, struct ieee80211_rc_info *); +static void amrr_tx_update(const struct ieee80211vap *, const struct ieee80211_node *, void *, void *, void *); +static void amrr_stats(const struct ieee80211vap *); static void amrr_sysctlattach(struct ieee80211vap *, struct sysctl_ctx_list *, struct sysctl_oid *); @@ -85,9 +86,11 @@ .ir_node_init = amrr_node_init, .ir_node_deinit = amrr_node_deinit, .ir_rate = amrr_rate, + .ir_rates = amrr_rates, .ir_tx_complete = amrr_tx_complete, .ir_tx_update = amrr_tx_update, .ir_setinterval = amrr_setinterval, + .ir_stats = amrr_stats, }; IEEE80211_RATECTL_MODULE(amrr, 1); IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr); @@ -105,7 +108,7 @@ } static void -amrr_init(struct ieee80211vap *vap) +amrr_init(struct ieee80211vap *vap, uint32_t capabilities) { struct ieee80211_amrr *amrr; @@ -117,6 +120,10 @@ if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); return; } + + struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap); + irs->irs_capabilities = capabilities; + amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD; amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD; amrr_setinterval(vap, 500 /* ms */); @@ -129,17 +136,6 @@ free(vap->iv_rs, M_80211_RATECTL); } -static int -amrr_node_is_11n(struct ieee80211_node *ni) -{ - - if (ni->ni_chan == NULL) - return (0); - if (ni->ni_chan == IEEE80211_CHAN_ANYC) - return (0); - return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); -} - static void amrr_node_init(struct ieee80211_node *ni) { @@ -159,29 +155,20 @@ } } else amn = ni->ni_rctls; + amn->amn_amrr = amrr; amn->amn_success = 0; amn->amn_recovery = 0; amn->amn_txcnt = amn->amn_retrycnt = 0; amn->amn_success_threshold = amrr->amrr_min_success_threshold; - /* 11n or not? Pick the right rateset */ - if (amrr_node_is_11n(ni)) { - /* XXX ew */ - IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "%s: 11n node", __func__); - rs = (struct ieee80211_rateset *) &ni->ni_htrates; - } else { - IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "%s: non-11n node", __func__); - rs = &ni->ni_rates; - } + rs = ieee80211_ratectl_get_rateset(ni); /* Initial rate - lowest */ rate = rs->rs_rates[0]; /* XXX clear the basic rate flag if it's not 11n */ - if (! amrr_node_is_11n(ni)) + if (! ieee80211_ratectl_node_is11n(ni)) rate &= IEEE80211_RATE_VAL; /* pick initial rate from the rateset - HT or otherwise */ @@ -189,7 +176,7 @@ amn->amn_rix--) { /* legacy - anything < 36mbit, stop searching */ /* 11n - stop at MCS4 / MCS12 / MCS28 */ - if (amrr_node_is_11n(ni) && + if (ieee80211_ratectl_node_is11n(ni) && (rs->rs_rates[amn->amn_rix] & 0x7) < 4) break; else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) @@ -198,7 +185,7 @@ } /* if the rate is an 11n rate, ensure the MCS bit is set */ - if (amrr_node_is_11n(ni)) + if (ieee80211_ratectl_node_is11n(ni)) rate |= IEEE80211_RATE_MCS; /* Assign initial rate from the rateset */ @@ -206,9 +193,8 @@ amn->amn_ticks = ticks; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "AMRR: nrates=%d, initial rate %d", - rs->rs_nrates, - rate); + "%s: AMRR: nrates=%d, initial rate %d", + __func__, rs->rs_nrates, rate); } static void @@ -226,17 +212,12 @@ KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); - /* 11n or not? Pick the right rateset */ - if (amrr_node_is_11n(ni)) { - /* XXX ew */ - rs = (struct ieee80211_rateset *) &ni->ni_htrates; - } else { - rs = &ni->ni_rates; - } + rs = ieee80211_ratectl_get_rateset(ni); + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "AMRR: current rate %d, txcnt=%d, retrycnt=%d", - rs->rs_rates[rix] & IEEE80211_RATE_VAL, + "%s: AMRR: current rate %d, txcnt=%d, retrycnt=%d", + __func__, rs->rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); @@ -256,8 +237,8 @@ amn->amn_success = 0; rix++; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", - rs->rs_rates[rix] & IEEE80211_RATE_VAL, + "%s: AMRR increasing rate %d (txcnt=%d retrycnt=%d)", + __func__, rs->rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); } else { amn->amn_recovery = 0; @@ -277,8 +258,8 @@ } rix--; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, - "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", - rs->rs_rates[rix] & IEEE80211_RATE_VAL, + "%s: AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", + __func__, rs->rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); } amn->amn_recovery = 0; @@ -304,13 +285,7 @@ const struct ieee80211_rateset *rs = NULL; int rix; - /* 11n or not? Pick the right rateset */ - if (amrr_node_is_11n(ni)) { - /* XXX ew */ - rs = (struct ieee80211_rateset *) &ni->ni_htrates; - } else { - rs = &ni->ni_rates; - } + rs = ieee80211_ratectl_get_rateset(ni); if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) { rix = amrr_update(amrr, amn, ni); @@ -318,7 +293,7 @@ /* update public rate */ ni->ni_txrate = rs->rs_rates[rix]; /* XXX strip basic rate flag from txrate, if non-11n */ - if (amrr_node_is_11n(ni)) + if (ieee80211_ratectl_node_is11n(ni)) ni->ni_txrate |= IEEE80211_RATE_MCS; else ni->ni_txrate &= IEEE80211_RATE_VAL; @@ -330,6 +305,76 @@ return rix; } +static void +amrr_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) +{ +#define RATE(_ix) (rs->rs_rates[(_ix)] & IEEE80211_RATE_VAL) +#define MCS(_ix) (rs->rs_rates[(_ix)] | IEEE80211_RATE_MCS) + struct ieee80211_rc_series *rc = rc_info->iri_rc; + const struct ieee80211_rateset *rs = NULL; + const struct ieee80211_rate_table *rt = NULL; + int rix, code; + + rs = ieee80211_ratectl_get_rateset(ni); + rt = ieee80211_get_ratetable(ni->ni_ic->ic_curchan); + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_RATECTL, + "%s: channel flags: 0x%08x\n", __func__, + ni->ni_ic->ic_curchan->ic_flags); + + rix = amrr_rate(ni, NULL, 0); + + rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; + + if (rs->rs_nrates > 0) { + code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix); + rc[0].rix = rt->rateCodeToIndex[code]; + + if (IEEE80211_RATECTL_HASCAP_MRR(ni->ni_vap)) { + rc[0].tries = 1; + rc[1].tries = 1; + rc[2].tries = 1; + rc[3].tries = 1; + if (--rix >= 0) { + code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix); + rc[1].rix = rt->rateCodeToIndex[code]; + } else { + rc[1].rix = rt->rateCodeToIndex[0]; + } + if (--rix >= 0) { + code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix); + rc[2].rix = rt->rateCodeToIndex[code]; + } else { + rc[2].rix = rt->rateCodeToIndex[0]; + } + if (rix >= 0) { + /* NB: only do this if we didn't already do it above */ + code = ieee80211_ratectl_node_is11n(ni)? MCS(0) : RATE(0); + rc[3].rix = rt->rateCodeToIndex[code]; + } else { + rc[3].rix = rt->rateCodeToIndex[0]; + } + } else { + rc[0].tries = IEEE80211_RATECTL_TXMAXTRY; + + rc[1].tries = 0; + rc[2].tries = 0; + rc[3].tries = 0; + rc[1].rix = rt->rateCodeToIndex[0]; + rc[2].rix = rt->rateCodeToIndex[0]; + rc[3].rix = rt->rateCodeToIndex[0]; + } + } + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: AMRR rate sets(rix, tries). rc[0]:(%d, %d), rc[1]:(%d, %d), " + "rc[2]:(%d, %d), rc[3]:(%d, %d)", __func__, + rc[0].rix, rc[0].tries, + rc[1].rix, rc[1].tries, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries); +#undef RATE +#undef MCS +} + /* * Update statistics with tx complete status. Ok is non-zero * if the packet is known to be ACK'd. Retries has the number @@ -337,16 +382,22 @@ */ static void amrr_tx_complete(const struct ieee80211vap *vap, - const struct ieee80211_node *ni, int ok, - void *arg1, void *arg2 __unused) + const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) { struct ieee80211_amrr_node *amn = ni->ni_rctls; - int retries = *(int *)arg1; - amn->amn_txcnt++; - if (ok) - amn->amn_success++; - amn->amn_retrycnt += retries; + /* update per vap statistics */ + ieee80211_ratectl_update_stat(vap, rc_info); + + amn->amn_txcnt += rc_info->iri_txcnt; + amn->amn_success += (rc_info->iri_txcnt - rc_info->iri_failcnt); + amn->amn_retrycnt += rc_info->iri_retrycnt; + + IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, + "%s: AMRR tx complete. txcnt=%d(%d) success=%d(%d) retrycnt=%d(%d)\n", + __func__, amn->amn_txcnt, rc_info->iri_txcnt, + amn->amn_success, (rc_info->iri_txcnt - rc_info->iri_failcnt), + amn->amn_retrycnt, rc_info->iri_retrycnt); } /* @@ -364,8 +415,21 @@ amn->amn_txcnt = txcnt; amn->amn_success = success; amn->amn_retrycnt = retrycnt; + IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, + "%s: AMRR tx update. txcnt=%d success=%d retrycnt=%d\n", + __func__, txcnt, success, retrycnt); } +static void +amrr_stats(const struct ieee80211vap *vap) +{ + struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap); + printf("tx count: %d (ok count: %d, fail count: %d)\n" + "retry count: %d (short retry: %d, long retry: %d)\n", + irs->irs_txcnt, irs->irs_txcnt - irs->irs_failcnt, irs->irs_failcnt, + irs->irs_retrycnt, irs->irs_shortretry, irs->irs_longretry); +} + static int amrr_sysctl_interval(SYSCTL_HANDLER_ARGS) { Index: net80211/ieee80211_ratectl.c =================================================================== --- net80211/ieee80211_ratectl.c (revision 254826) +++ net80211/ieee80211_ratectl.c (working copy) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> + * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,6 +50,40 @@ MALLOC_DEFINE(M_80211_RATECTL, "80211ratectl", "802.11 rate control"); +enum { + MCS_HT20, + MCS_HT20_SGI, + MCS_HT40, + MCS_HT40_SGI, +}; + +int max_4ms_framelen[4][32] = { + [MCS_HT20] = { + 3212, 6432, 9648, 12864, 19300, 25736, 28952, 32172, + 6424, 12852, 19280, 25708, 38568, 51424, 57852, 64280, + 9628, 19260, 28896, 38528, 57792, 65532, 65532, 65532, + 12828, 25656, 38488, 51320, 65532, 65532, 65532, 65532, + }, + [MCS_HT20_SGI] = { + 3572, 7144, 10720, 14296, 21444, 28596, 32172, 35744, + 7140, 14284, 21428, 28568, 42856, 57144, 64288, 65532, + 10700, 21408, 32112, 42816, 64228, 65532, 65532, 65532, + 14256, 28516, 42780, 57040, 65532, 65532, 65532, 65532, + }, + [MCS_HT40] = { + 6680, 13360, 20044, 26724, 40092, 53456, 60140, 65532, + 13348, 26700, 40052, 53400, 65532, 65532, 65532, 65532, + 20004, 40008, 60016, 65532, 65532, 65532, 65532, 65532, + 26644, 53292, 65532, 65532, 65532, 65532, 65532, 65532, + }, + [MCS_HT40_SGI] = { + 7420, 14844, 22272, 29696, 44544, 59396, 65532, 65532, + 14832, 29668, 44504, 59340, 65532, 65532, 65532, 65532, + 22232, 44464, 65532, 65532, 65532, 65532, 65532, 65532, + 29616, 59232, 65532, 65532, 65532, 65532, 65532, 65532, + } +}; + void ieee80211_ratectl_register(int type, const struct ieee80211_ratectl *ratectl) { @@ -65,12 +100,36 @@ ratectls[type] = NULL; } +static int +ratectl_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + int error, v; + + v = 0; + error = sysctl_handle_int(oidp, &v, 0, req); + if (error || !req->newptr) + return error; + + ieee80211_ratectl_stats(vap); + return 0; +} + void -ieee80211_ratectl_init(struct ieee80211vap *vap) +ieee80211_ratectl_init(struct ieee80211vap *vap, uint32_t capabilities) { if (vap->iv_rate == ratectls[IEEE80211_RATECTL_NONE]) ieee80211_ratectl_set(vap, IEEE80211_RATECTL_AMRR); - vap->iv_rate->ir_init(vap); + vap->iv_rate->ir_init(vap, capabilities); + + SYSCTL_ADD_PROC(vap->iv_sysctl, SYSCTL_CHILDREN(vap->iv_oid), + OID_AUTO, "ratectl_stats", CTLTYPE_INT | CTLFLAG_RW, + vap, 0, ratectl_sysctl_stats, "I", + "per-vap net80211 ratectl statistics"); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL, + "%s: ratectl initialized. caps=0x%08x\n", + __func__, capabilities); } void @@ -90,3 +149,110 @@ } vap->iv_rate = ratectls[type]; } + +void +ieee80211_ratectl_complete_rcflags(struct ieee80211_node *ni, + struct ieee80211_rc_info *rc_info) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_rc_series *rc = rc_info->iri_rc; + const struct ieee80211_rate_table * rt = NULL; + uint8_t rate; + int i; + + rt = ieee80211_get_ratetable(ni->ni_ic->ic_curchan); + + /* Make sure that rate control code doesn't mess it up. + * If enable rts/cts and is pre-802.11n, blank tries 1, 2, 3 + */ + + if (! IEEE80211_RATECTL_HASCAP_MRRPROT(vap)) + { + for (i = 1; i < IEEE80211_RATECTL_NUM; i++) + { + if (rc[0].flags & IEEE80211_RATECTL_FLAG_RTSCTS) + rc[i].tries = 0; + rc[i].flags &= ~IEEE80211_RATECTL_FLAG_RTSCTS; + } + } + + for (i = 0; i < IEEE80211_RATECTL_NUM; i++) { + + if (rc[i].tries == 0) + continue; + + rate = rt->info[rc[i].rix].dot11Rate; + + /* Only enable dual-stream, shortgi, 2040 if HT is set */ + + if (IS_HT_RATE(rate)) { + rc[i].flags |= IEEE80211_RATECTL_FLAG_HT; + + if (ieee80211_ratectl_hascap_cw40(ni)) + rc[i].flags |= IEEE80211_RATECTL_FLAG_CW40; + + if (ieee80211_ratectl_hascap_shortgi(ni)) + rc[i].flags |= IEEE80211_RATECTL_FLAG_SGI; + /* + * If we have STBC TX enabled and the receiver + * can receive (at least) 1 stream STBC, AND it's + * MCS 0-7, AND we have at least two chains enabled, + * enable STBC. + */ + if (ieee80211_ratectl_hascap_stbc(ni) && + (rate & IEEE80211_RATE_VAL) < 8 && + HT_RC_2_STREAMS(rate) == 1) + rc[i].flags |= IEEE80211_RATECTL_FLAG_STBC; + + /* + * Dual / Triple stream rate? + */ + if (HT_RC_2_STREAMS(rate) == 2) + rc[i].flags |= IEEE80211_RATECTL_FLAG_DS; + else if (HT_RC_2_STREAMS(rate) == 3) + rc[i].flags |= IEEE80211_RATECTL_FLAG_TS; + + /* + * Calculate the maximum 4ms frame length based + * on the MCS rate, SGI and channel width flags. + */ + if (HT_RC_2_MCS(rate) < 32) { + int j; + if (rc[i].flags & IEEE80211_RATECTL_FLAG_CW40) { + if (rc[i].flags & IEEE80211_RATECTL_FLAG_SGI) + j = MCS_HT40_SGI; + else + j = MCS_HT40; + } else { + if (rc[i].flags & IEEE80211_RATECTL_FLAG_SGI) + j = MCS_HT20_SGI; + else + j = MCS_HT20; + } + rc[i].max4msframelen = + max_4ms_framelen[j][HT_RC_2_MCS(rate)]; + } + } else { + rc[i].max4msframelen = 0; + + /* + * Only enable short preamble for legacy rates + */ + if (rc_info->iri_flags & IEEE80211_RATECTL_INFO_SP) + rc[i].flags |= IEEE80211_RATECTL_FLAG_SP; + } + + /* + * Calculate the maximum TX power cap for the current + * node. + * Rate control algo can't control TX power by now. + */ + rc[i].tx_power_cap = ieee80211_get_node_txpower(ni); + + } + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: flags: rc[0]:0x%08x, rc[1]:0x%08x, " + "rc[2]:0x%08x, rc[3]:0x%08x\n", __func__, + rc[0].flags, rc[1].flags, rc[2].flags, rc[3].flags); +} + Index: net80211/ieee80211_ratectl.h =================================================================== --- net80211/ieee80211_ratectl.h (revision 254826) +++ net80211/ieee80211_ratectl.h (working copy) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org> + * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +25,8 @@ * * $FreeBSD$ */ +#ifndef _NET80211_IEEE80211_RATECTL_H_ +#define _NET80211_IEEE80211_RATECTL_H_ enum ieee80211_ratealgs { IEEE80211_RATECTL_AMRR = 0, @@ -37,28 +40,115 @@ #define IEEE80211_RATECTL_TX_SUCCESS 1 #define IEEE80211_RATECTL_TX_FAILURE 0 +#define IEEE80211_RATECTL_TRUE 1 +#define IEEE80211_RATECTL_FALSE 0 + +#define IEEE80211_RATECTL_NUM 4 +#define IEEE80211_RATECTL_TXMAXTRY 11 + +#define IEEE80211_RATECTL_FLAG_DS 0x01 /* dual-stream rate */ +#define IEEE80211_RATECTL_FLAG_CW40 0x02 /* use HT40 */ +#define IEEE80211_RATECTL_FLAG_SGI 0x04 /* use short-GI */ +#define IEEE80211_RATECTL_FLAG_HT 0x08 /* use HT */ +#define IEEE80211_RATECTL_FLAG_RTSCTS 0x10 /* enable RTS/CTS protection */ +#define IEEE80211_RATECTL_FLAG_STBC 0x20 /* enable STBC */ +#define IEEE80211_RATECTL_FLAG_TS 0x40 /* triple-stream rate */ +#define IEEE80211_RATECTL_FLAG_SP 0x80 /* short preamble */ + +/* Hardware CAPs offered to rate control algo */ +#define IEEE80211_RATECTL_CAP_MRR 0x01 /* support MRR */ +#define IEEE80211_RATECTL_CAP_MRRPROT 0x02 /* support MRR + protect */ +#define IEEE80211_RATECTL_CAP_MULTXCHAIN 0x04 /* has more than 1 txchain */ + +#define IS_VAP_HT(vap) ((vap)->iv_htcaps & IEEE80211_HTC_HT) +#define IS_HT_RATE(_rate) ((_rate) & 0x80) +#define HT_RC_2_MCS(_rc) ((_rc) & 0x7f) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) + +extern int max_4ms_framelen[4][32]; + +struct ieee80211_rc_series { + uint8_t rix; /* ratetable index, not rate code */ + uint8_t ratecode; /* hardware rate code */ + uint8_t tries; + uint8_t tx_power_cap; + uint16_t flags; + uint16_t max4msframelen; +}; + +struct ieee80211_rc_info { + struct ieee80211_rc_series iri_rc[IEEE80211_RATECTL_NUM]; + uint32_t iri_framelen; + uint16_t iri_flags; /* See below */ + uint16_t iri_maxaggrsize; /* Maximum aggregate size */ + + /* TX info */ + uint8_t iri_txcnt; /* TX count */ + uint8_t iri_failcnt; /* TX retry-fail count */ + uint8_t iri_okcnt; /* TX ok with or without retry */ + uint8_t iri_retrycnt; /* TX retry count */ + uint8_t iri_shortretry; + uint8_t iri_longretry; + uint8_t iri_finaltsi; + uint8_t iri_txrate; /* HW tx rate */ +}; + +/* ieee80211_rc_info flags */ +#define IEEE80211_RATECTL_INFO_SP 0x01 /* short preamble */ +#define IEEE80211_RATECTL_INFO_AGGR 0x02 /* aggregation scenario */ + +#define NET80211_TAG_RATECTL 1 /* net80211 ratectl state */ + +/* + * net80211 ratectl statistics. + * per vap ratectl seeting must start with this common state. + */ +struct ieee80211_rc_stat { + uint32_t irs_capabilities; /* hardware capabilities offered to rc */ + + /* ratectl statistics */ + uint32_t irs_txcnt; + uint32_t irs_failcnt; + uint32_t irs_retrycnt; + uint32_t irs_shortretry; + uint32_t irs_longretry; +}; + +#define IEEE80211_RATECTL_STAT(_vap) \ + ((struct ieee80211_rc_stat *)((_vap)->iv_rs)) + +#define IEEE80211_RATECTL_HASCAP_MRR(_vap) \ + (IEEE80211_RATECTL_STAT(_vap)->irs_capabilities & IEEE80211_RATECTL_CAP_MRR) +#define IEEE80211_RATECTL_HASCAP_MRRPROT(_vap) \ + (IEEE80211_RATECTL_STAT(_vap)->irs_capabilities & IEEE80211_RATECTL_CAP_MRRPROT) +#define IEEE80211_RATECTL_HASCAP_MULTXCHAIN(_vap) \ + (IEEE80211_RATECTL_STAT(_vap)->irs_capabilities & IEEE80211_RATECTL_CAP_MULTXCHAIN) + struct ieee80211_ratectl { const char *ir_name; int (*ir_attach)(const struct ieee80211vap *); void (*ir_detach)(const struct ieee80211vap *); - void (*ir_init)(struct ieee80211vap *); + void (*ir_init)(struct ieee80211vap *, uint32_t); void (*ir_deinit)(struct ieee80211vap *); void (*ir_node_init)(struct ieee80211_node *); void (*ir_node_deinit)(struct ieee80211_node *); int (*ir_rate)(struct ieee80211_node *, void *, uint32_t); + void (*ir_rates)(struct ieee80211_node *, struct ieee80211_rc_info *); void (*ir_tx_complete)(const struct ieee80211vap *, - const struct ieee80211_node *, int, - void *, void *); + const struct ieee80211_node *, struct ieee80211_rc_info *); void (*ir_tx_update)(const struct ieee80211vap *, const struct ieee80211_node *, void *, void *, void *); void (*ir_setinterval)(const struct ieee80211vap *, int); + void (*ir_stats)(const struct ieee80211vap *); }; void ieee80211_ratectl_register(int, const struct ieee80211_ratectl *); void ieee80211_ratectl_unregister(int); -void ieee80211_ratectl_init(struct ieee80211vap *); +void ieee80211_ratectl_init(struct ieee80211vap *, uint32_t); void ieee80211_ratectl_set(struct ieee80211vap *, int); +void ieee80211_ratectl_complete_rcflags(struct ieee80211_node *, + struct ieee80211_rc_info*); MALLOC_DECLARE(M_80211_RATECTL); @@ -74,6 +164,8 @@ const struct ieee80211vap *vap = ni->ni_vap; vap->iv_rate->ir_node_init(ni); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL, + "%s: net80211 ratectl node inited.\n", __func__); } static void __inline @@ -82,6 +174,8 @@ const struct ieee80211vap *vap = ni->ni_vap; vap->iv_rate->ir_node_deinit(ni); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL, + "%s: net80211 ratectl node deinited.\n", __func__); } static int __inline @@ -93,10 +187,26 @@ } static void __inline +ieee80211_ratectl_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) +{ + const struct ieee80211vap *vap = ni->ni_vap; + + if (rc_info->iri_flags & IEEE80211_RATECTL_INFO_AGGR) + rc_info->iri_framelen = 0; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL, + "%s: find rate sets for %saggregation scenario.\n", __func__, + (rc_info->iri_flags & IEEE80211_RATECTL_INFO_AGGR)? "" : "non-"); + + vap->iv_rate->ir_rates(ni, rc_info); + ieee80211_ratectl_complete_rcflags(ni, rc_info); +} + +static void __inline ieee80211_ratectl_tx_complete(const struct ieee80211vap *vap, - const struct ieee80211_node *ni, int status, void *arg1, void *arg2) + const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) { - vap->iv_rate->ir_tx_complete(vap, ni, status, arg1, arg2); + vap->iv_rate->ir_tx_complete(vap, ni, rc_info); } static void __inline @@ -115,3 +225,135 @@ return; vap->iv_rate->ir_setinterval(vap, msecs); } + +static void __inline +ieee80211_ratectl_stats(const struct ieee80211vap *vap) +{ + printf("\n[%s]: net80211 ratectl statistics (%s)\n", + vap->iv_ifp->if_xname, vap->iv_rate->ir_name); + if (vap->iv_rate->ir_stats == NULL) + return; + vap->iv_rate->ir_stats(vap); +} + +static int __inline +ieee80211_ratectl_hascap_cw40(const struct ieee80211_node *ni) +{ + return ni->ni_chw == 40; +} + +static int __inline +ieee80211_ratectl_hascap_shortgi(const struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + if (ni->ni_chw == 40 && + ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 && + ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) + return IEEE80211_RATECTL_TRUE; + + if (ni->ni_chw == 20 && + ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20 && + ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) + return IEEE80211_RATECTL_TRUE; + + return IEEE80211_RATECTL_FALSE; +} + +static int __inline +ieee80211_ratectl_hascap_stbc(const struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + + if (ic->ic_htcaps & IEEE80211_HTCAP_TXSTBC && + ni->ni_htcap & IEEE80211_HTCAP_RXSTBC_1STREAM && + IEEE80211_RATECTL_HASCAP_MULTXCHAIN(ni->ni_vap)) + return IEEE80211_RATECTL_TRUE; + + return IEEE80211_RATECTL_FALSE; +} + +static int __inline +ieee80211_ratectl_node_is11n(const struct ieee80211_node *ni) +{ + if (ni->ni_chan == NULL) + return (0); + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return (0); + return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); +} + +__inline static const struct ieee80211_rateset * +ieee80211_ratectl_get_rateset(const struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs = NULL; + /* 11n or not? Pick the right rateset */ + if (ieee80211_ratectl_node_is11n(ni)) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: 11n node", __func__); + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + } else { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: non-11n node", __func__); + rs = &ni->ni_rates; + } + return rs; +} + +static void __inline +ieee80211_ratectl_update_stat(const struct ieee80211vap *vap, + const struct ieee80211_rc_info *rc_info) +{ + struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap); + irs->irs_txcnt += rc_info->iri_txcnt; + irs->irs_failcnt += rc_info->iri_failcnt; + irs->irs_retrycnt += rc_info->iri_retrycnt; + irs->irs_shortretry += rc_info->iri_shortretry; + irs->irs_longretry += rc_info->iri_longretry; +} + +static void __inline +ieee80211_ratectl_rc_info_set(struct ieee80211_rc_info *rc_info, + uint8_t txcnt, uint8_t failcnt, uint32_t framelen, + uint8_t shortretry, uint8_t longretry, + uint8_t finaltsi, uint8_t txrate) +{ + rc_info->iri_txcnt = txcnt; + rc_info->iri_failcnt = failcnt; + rc_info->iri_okcnt = txcnt - failcnt; + rc_info->iri_framelen = framelen; + rc_info->iri_shortretry = shortretry; + rc_info->iri_longretry = longretry; + rc_info->iri_retrycnt = shortretry + longretry; + rc_info->iri_finaltsi = finaltsi; + rc_info->iri_txrate = txrate; +} + +__inline static struct ieee80211_rc_info * +ieee80211_ratectl_rc_info_get(struct ieee80211_node *ni, + struct mbuf *m) +{ + struct m_tag *mtag; + + mtag = m_tag_locate(m, MTAG_ABI_NET80211, + NET80211_TAG_RATECTL, NULL); + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: %sratectl mbuf tag found.\n", __func__, + (NULL == mtag? "no ":"")); +again: + if (NULL == mtag) { + mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_RATECTL, + sizeof(struct ieee80211_rc_info), M_NOWAIT); + if (NULL == mtag) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, + "%s: can't alloc mbuf tag for ratectl.\n", __func__); + goto again; + } + bzero(mtag + 1, mtag->m_tag_len); + m_tag_prepend(m, mtag); + } + return (struct ieee80211_rc_info*)(mtag + 1); +} + +#endif Index: net80211/ieee80211_freebsd.c =================================================================== --- net80211/ieee80211_freebsd.c (revision 254826) +++ net80211/ieee80211_freebsd.c (working copy) @@ -262,7 +262,9 @@ "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0, "driver capabilities"); #ifdef IEEE80211_DEBUG + ieee80211_debug |= (IEEE80211_MSG_RATECTL|IEEE80211_MSG_NODE|IEEE80211_MSG_SCAN); vap->iv_debug = ieee80211_debug; + if_printf(ifp, "%s: iv_debug=0x%08x\n", __func__, vap->iv_debug); SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "debug", CTLFLAG_RW, &vap->iv_debug, 0, "control debugging printfs"); Index: net80211/ieee80211_ratectl_none.c =================================================================== --- net80211/ieee80211_ratectl_none.c (revision 254826) +++ net80211/ieee80211_ratectl_none.c (working copy) @@ -44,10 +44,26 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ratectl.h> +#include <net80211/ieee80211_ratectl_none.h> static void -none_init(struct ieee80211vap *vap) +none_init(struct ieee80211vap *vap, uint32_t capabilities) { + struct ieee80211_node *none; + + KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); + + none = vap->iv_rs = malloc(sizeof(struct ieee80211_none), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (none == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); + return; + } + + struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap); + irs->irs_capabilities = capabilities; + + /* ... */ } static void @@ -59,6 +75,25 @@ static void none_node_init(struct ieee80211_node *ni) { + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_none *none = vap->iv_rs; + struct ieee80211_none_node *non; + + if (ni->ni_rctls == NULL) { + ni->ni_rctls = non = malloc(sizeof(struct ieee80211_none_node), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (non == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " + "structure\n"); + return; + } + } else + non = ni->ni_rctls; + + non->non_none = none; + + /* ... */ + ni->ni_txrate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL; } @@ -78,8 +113,7 @@ static void none_tx_complete(const struct ieee80211vap *vap, - const struct ieee80211_node *ni, int ok, - void *arg1, void *arg2 __unused) + const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) { } Index: net80211/ieee80211_rssadapt.c =================================================================== --- net80211/ieee80211_rssadapt.c (revision 254826) +++ net80211/ieee80211_rssadapt.c (working copy) @@ -74,7 +74,7 @@ parm##_denom) static void rssadapt_setinterval(const struct ieee80211vap *, int); -static void rssadapt_init(struct ieee80211vap *); +static void rssadapt_init(struct ieee80211vap *, uint32_t); static void rssadapt_deinit(struct ieee80211vap *); static void rssadapt_updatestats(struct ieee80211_rssadapt_node *); static void rssadapt_node_init(struct ieee80211_node *); @@ -84,8 +84,7 @@ static void rssadapt_raise_rate(struct ieee80211_rssadapt_node *, int, int); static void rssadapt_tx_complete(const struct ieee80211vap *, - const struct ieee80211_node *, int, - void *, void *); + const struct ieee80211_node *, struct ieee80211_rc_info *); static void rssadapt_sysctlattach(struct ieee80211vap *, struct sysctl_ctx_list *, struct sysctl_oid *); @@ -121,7 +120,7 @@ } static void -rssadapt_init(struct ieee80211vap *vap) +rssadapt_init(struct ieee80211vap *vap, uint32_t capabilities) { struct ieee80211_rssadapt *rs; @@ -134,6 +133,10 @@ if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); return; } + + struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap); + irs->irs_capabilities = capabilities; + rs->vap = vap; rssadapt_setinterval(vap, 500 /* msecs */); rssadapt_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); @@ -307,10 +310,11 @@ static void rssadapt_tx_complete(const struct ieee80211vap *vap, - const struct ieee80211_node *ni, int success, void *arg1, void *arg2) + const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) { struct ieee80211_rssadapt_node *ra = ni->ni_rctls; - int pktlen = *(int *)arg1, rssi = *(int *)arg2; + /*int pktlen = *(int *)arg1, rssi = *(int *)arg2;*/ + int pktlen = 0, rssi = 0, success = 0; /* just make it compiled. qcc */ if (success) { ra->ra_nok++; Index: net80211/ieee80211_rc_sample.c =================================================================== --- net80211/ieee80211_rc_sample.c (revision 0) +++ net80211/ieee80211_rc_sample.c (revision 256825) @@ -0,0 +1,1141 @@ +/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.c 248573 2013-02-27 04:33:06Z adrian $*/ + +/*- + * Copyright (c) 2005 John Bicket + * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_ether.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_ht.h> +#include <net80211/ieee80211_ratectl.h> +#include <net80211/ieee80211_rc_sample.h> +#include <net80211/ieee80211_rc_sample_txsched.h> + +static void sample_init(struct ieee80211vap *, uint32_t); +static void sample_deinit(struct ieee80211vap *); +static void sample_node_init(struct ieee80211_node *); +static void sample_node_deinit(struct ieee80211_node *); +static int sample_rate(struct ieee80211_node *, void *, uint32_t); +static void sample_tx_complete(const struct ieee80211vap *, + const struct ieee80211_node *, int, + void *, void *); +static void sample_tx_update(const struct ieee80211vap *vap, + const struct ieee80211_node *, void *, void *, void *); +static void sample_setinterval(const struct ieee80211vap *, int); + +/* number of references from net80211 layer */ +static int nrefs = 0; + +static const struct ieee80211_ratectl sample = { + .ir_name = "sample", + .ir_attach = NULL, + .ir_detach = NULL, + .ir_init = sample_init, + .ir_deinit = sample_deinit, + .ir_node_init = sample_node_init, + .ir_node_deinit = sample_node_deinit, + .ir_rate = sample_rate, + .ir_rates = sample_rates, + .ir_tx_complete = sample_tx_complete, + .ir_tx_update = sample_tx_update, + .ir_setinterval = sample_setinterval, +}; +IEEE80211_RATECTL_MODULE(sample, 1); +IEEE80211_RATECTL_ALG(sample, IEEE80211_RATECTL_SAMPLE, sample); + +static void +sample_init(struct ieee80211vap *vap, uint32_t capabilities) +{ + struct ieee80211_sample *sample; + + KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__)); + + sample = vap->iv_rs = malloc(sizeof(struct ieee80211_sample), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (sample == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n"); + return; + } + + struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap); + irs->irs_capabilities = capabilities; + + sample->sample_smoothing_rate = 75; /* ewma percentage ([0..99]) */ + sample->sample_smoothing_minpackets = 100 / (100 - sample->sample_smoothing_rate); + sample->sample_rate = 10; /* %time to try diff tx rates */ + sample->sample_max_successive_failures = 3; /* threshold for rate sampling*/ + sample->sample_stale_failure_timeout = 10 * hz; /* 10 seconds */ + sample->sample_min_switch = hz; /* 1 second */ + sample_setinterval(vap, 500 /* ms */); /* actually do nothing */ + sample_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid); +} + +static void +sample_deinit(struct ieee80211vap *vap) +{ + free(vap->iv_rs, M_80211_RATECTL); +} + +static const struct txschedule *mrr_schedules[IEEE80211_MODE_MAX+2] = { + NULL, /* IEEE80211_MODE_AUTO */ + series_11a, /* IEEE80211_MODE_11A */ + series_11g, /* IEEE80211_MODE_11B */ + series_11g, /* IEEE80211_MODE_11G */ + NULL, /* IEEE80211_MODE_FH */ + series_11a, /* IEEE80211_MODE_TURBO_A */ + series_11g, /* IEEE80211_MODE_TURBO_G */ + series_11a, /* IEEE80211_MODE_STURBO_A */ + series_11na, /* IEEE80211_MODE_11NA */ + series_11ng, /* IEEE80211_MODE_11NG */ + series_half, /* IEEE80211_MODE_HALF */ + series_quarter, /* IEEE80211_MODE_QUARTER */ +}; + +static void +sample_node_init(struct ieee80211_node *ni) +{ +#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) +#define DOT11RATE(_ix) (rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL) +#define MCS(_ix) (ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_sample *sample = vap->iv_rs; + struct ieee80211_sample_node *san = NULL; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + enum ieee80211_phymode curmode = ieee80211_chan2mode(vap->iv_ic->ic_curchan); + int x, y, rix; + + if (ni->ni_rctls == NULL) { + ni->ni_rctls = san = malloc(sizeof(struct ieee80211_sample_node), + M_80211_RATECTL, M_NOWAIT|M_ZERO); + if (san == NULL) { + if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " + "structure\n"); + return; + } + } else + san = ni->ni_rctls; + + san->san_sample = sample; + + KASSERT(rt != NULL, ("no rate table, mode %u", curmode)); + + san->sched = mrr_schedules[curmode]; + KASSERT(san->sched != NULL, ("no mrr schedule for mode %u", curmode)); + + san->static_rix = -1; + sample_update_static_rix(ni); + + /* + * Construct a bitmask of usable rates. This has all + * negotiated rates minus those marked by the hal as + * to be ignored for doing rate control. + */ + san->ratemask = 0; + + /* MCS rates */ + if (ni->ni_flags & IEEE80211_NODE_HT) { + for (x = 0; x < ni->ni_htrates.rs_nrates; x++) { + rix = rt->rateCodeToIndex[MCS(x)]; + if (rix == 0xff) + continue; + /* skip rates marked broken by hal */ + if (!rt->info[rix].valid) + continue; + KASSERT(rix < SAMPLE_MAXRATES, + ("mcs %u has rix %d", MCS(x), rix)); + san->ratemask |= (uint64_t) 1<<rix; + } + } + + /* Legacy rates */ + for (x = 0; x < ni->ni_rates.rs_nrates; x++) { + rix = rt->rateCodeToIndex[RATE(x)]; + if (rix == 0xff) + continue; + /* skip rates marked broken by hal */ + if (!rt->info[rix].valid) + continue; + KASSERT(rix < SAMPLE_MAXRATES, + ("rate %u has rix %d", RATE(x), rix)); + san->ratemask |= (uint64_t) 1<<rix; + } + + for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { + int size = bin_to_size(y); + uint64_t mask; + + san->packets_sent[y] = 0; + san->current_sample_rix[y] = -1; + san->last_sample_rix[y] = 0; + /* XXX start with first valid rate */ + san->current_rix[y] = ffs(san->ratemask)-1; + + /* + * Initialize the statistics buckets; these are + * indexed by the rate code index. + */ + for (rix = 0, mask = san->ratemask; mask != 0; rix++, mask >>= 1) { + if ((mask & 1) == 0) /* not a valid rate */ + continue; + san->stats[y][rix].successive_failures = 0; + san->stats[y][rix].tries = 0; + san->stats[y][rix].total_packets = 0; + san->stats[y][rix].packets_acked = 0; + san->stats[y][rix].last_tx = 0; + san->stats[y][rix].ewma_pct = 0; + + san->stats[y][rix].perfect_tx_time = + calc_usecs_unicast_packet(vap, size, rix, 0, 0, + (ni->ni_chw == 40)); + san->stats[y][rix].average_tx_time = + san->stats[y][rix].perfect_tx_time; + } + } + + /* set the visible bit-rate */ + if (san->static_rix != -1) + ni->ni_txrate = DOT11RATE(san->static_rix); + else + ni->ni_txrate = RATE(0); +#undef RATE +#undef DOT11RATE +#undef MCS +} + +static void +sample_node_deinit(struct ieee80211_node *ni) +{ + free(ni->ni_rctls, M_80211_RATECTL); +} + +static int +dot11rate(const ieee80211_rate_table *rt, int rix) +{ + if (rix < 0) + return -1; + return rt->info[rix].phy == IEEE80211_T_HT ? + rt->info[rix].dot11Rate : (rt->info[rix].dot11Rate & IEEE80211_RATE_VAL) / 2; +} + +static const char * +dot11rate_label(const ieee80211_rate_table *rt, int rix) +{ + if (rix < 0) + return ""; + return rt->info[rix].phy == IEEE80211_T_HT ? "MCS" : "Mb "; +} + +/* + * Return the rix with the lowest average_tx_time, + * or -1 if all the average_tx_times are 0. + */ +static __inline int +pick_best_rate(const struct ieee80211_node *ni, const struct ieee80211_rate_table *rt, + int size_bin, int require_acked_before) +{ + struct ieee80211_sample_node *san = ni->ni_rctls; + int best_rate_rix, best_rate_tt, best_rate_pct; + uint64_t mask; + int rix, tt, pct; + + best_rate_rix = 0; + best_rate_tt = 0; + best_rate_pct = 0; + + for (mask = san->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { + if ((mask & 1) == 0) /* not a supported rate */ + continue; + + /* Don't pick a non-HT rate for a HT node */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + (rt->info[rix].phy != IEEE80211_T_HT)) { + continue; + } + + tt = san->stats[size_bin][rix].average_tx_time; + if (tt <= 0 || + (require_acked_before && + !san->stats[size_bin][rix].packets_acked)) + continue; + + /* Calculate percentage if possible */ + if (san->stats[size_bin][rix].total_packets > 0) { + pct = san->stats[size_bin][rix].ewma_pct; + } else { + /* XXX for now, assume 95% ok */ + pct = 95; + } + + /* don't use a bit-rate that has been failing */ + if (san->stats[size_bin][rix].successive_failures > 3) + continue; + + /* + * For HT, Don't use a bit rate that is much more + * lossy than the best. + * + * XXX this isn't optimal; it's just designed to + * eliminate rates that are going to be obviously + * worse. + */ + if (ni->ni_flags & IEEE80211_NODE_HT) { + if (best_rate_pct > (pct + 50)) + continue; + } + + /* + * For non-MCS rates, use the current average txtime for + * comparison. + */ + if (! (ni->ni_flags & IEEE80211_NODE_HT)) { + if (best_rate_tt == 0 || tt <= best_rate_tt) { + best_rate_tt = tt; + best_rate_rix = rix; + best_rate_pct = pct; + } + } + + /* + * Since 2 stream rates have slightly higher TX times, + * allow a little bit of leeway. This should later + * be abstracted out and properly handled. + */ + if (ni->ni_flags & IEEE80211_NODE_HT) { + if (best_rate_tt == 0 || (tt * 8 <= best_rate_tt * 10)) { + best_rate_tt = tt; + best_rate_rix = rix; + best_rate_pct = pct; + } + } + } + return (best_rate_tt ? best_rate_rix : -1); +} + +/* + * Pick a good "random" bit-rate to sample other than the current one. + */ +static __inline int +pick_sample_rate(const struct ieee80211_node *ni, const ieee80211_rate_table *rt, + int size_bin) +{ +#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) +#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) + struct ieee80211_sample_node *san = ni->ni_rctls; + struct ieee80211_sample *sample = san->san_sample; + int current_rix, rix; + unsigned current_tt; + uint64_t mask; + + current_rix = san->current_rix[size_bin]; + if (current_rix < 0) { + /* no successes yet, send at the lowest bit-rate */ + /* XXX should return MCS0 if HT */ + return 0; + } + + current_tt = san->stats[size_bin][current_rix].average_tx_time; + + rix = san->last_sample_rix[size_bin]+1; /* next sample rate */ + mask = san->ratemask &~ ((uint64_t) 1<<current_rix);/* don't sample current rate */ + while (mask != 0) { + if ((mask & ((uint64_t) 1<<rix)) == 0) { /* not a supported rate */ + nextrate: + if (++rix >= rt->rateCount) + rix = 0; + continue; + } + + /* + * The following code stops trying to sample + * non-MCS rates when speaking to an MCS node. + * However, at least for CCK rates in 2.4GHz mode, + * the non-MCS rates MAY actually provide better + * PER at the very far edge of reception. + * + * However! Until ath_rate_form_aggr() grows + * some logic to not form aggregates if the + * selected rate is non-MCS, this won't work. + * + * So don't disable this code until you've taught + * ath_rate_form_aggr() to drop out if any of + * the selected rates are non-MCS. + */ +#if 1 + /* if the node is HT and the rate isn't HT, don't bother sample */ + if ((ni->ni_flags & IEEE80211_NODE_HT) && + (rt->info[rix].phy != IEEE80211_T_HT)) { + mask &= ~((uint64_t) 1<<rix); + goto nextrate; + } +#endif + + /* this bit-rate is always worse than the current one */ + if (san->stats[size_bin][rix].perfect_tx_time > current_tt) { + mask &= ~((uint64_t) 1<<rix); + goto nextrate; + } + + /* rarely sample bit-rates that fail a lot */ + if (san->stats[size_bin][rix].successive_failures > sample->sample_max_successive_failures && + ticks - san->stats[size_bin][rix].last_tx < sample->sample_stale_failure_timeout) { + mask &= ~((uint64_t) 1<<rix); + goto nextrate; + } + + /* + * For HT, only sample a few rates on either side of the + * current rix; there's quite likely a lot of them. + */ + if (ni->ni_flags & IEEE80211_NODE_HT) { + if (rix < (current_rix - 3) || + rix > (current_rix + 3)) { + mask &= ~((uint64_t) 1<<rix); + goto nextrate; + } + } + + /* Don't sample more than 2 rates higher for rates > 11M for non-HT rates */ + if (! (ni->ni_flags & IEEE80211_NODE_HT)) { + if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) { + mask &= ~((uint64_t) 1<<rix); + goto nextrate; + } + } + + san->last_sample_rix[size_bin] = rix; + return rix; + } + return current_rix; +#undef DOT11RATE +#undef MCS +} + +static int +sample_get_static_rix(const struct ieee80211_node *ni) +{ +#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) +#define MCS(_ix) (ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS) + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + const struct ieee80211_txparam *tp = ni->ni_txparms; + int srate; + + /* Check MCS rates */ + for (srate = ni->ni_htrates.rs_nrates - 1; srate >= 0; srate--) { + if (MCS(srate) == tp->ucastrate) + return rt->rateCodeToIndex[tp->ucastrate]; + } + + /* Check legacy rates */ + for (srate = ni->ni_rates.rs_nrates - 1; srate >= 0; srate--) { + if (RATE(srate) == tp->ucastrate) + return rt->rateCodeToIndex[tp->ucastrate]; + } + return -1; +#undef RATE +#undef MCS +} + +static void +sample_update_static_rix(struct ieee80211_node *ni) +{ + struct ieee80211_sample_node *san = ni->ni_rctls; + const struct ieee80211_txparam *tp = ni->ni_txparms; + + if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + /* + * A fixed rate is to be used; ucastrate is the IEEE code + * for this rate (sans basic bit). Check this against the + * negotiated rate set for the node. Note the fixed rate + * may not be available for various reasons so we only + * setup the static rate index if the lookup is successful. + */ + san->static_rix = sample_get_static_rix(ni); + } else { + san->static_rix = -1; + } +} + +/* + * Pick a non-HT rate to begin using. + */ +static int +sample_pick_seed_rate_legacy(const struct ieee80211_node *ni, int frameLen) +{ +#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_sample_node *san = ni->ni_rctls; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + + const int size_bin = size_to_bin(frameLen); + int rix = -1; + + /* no packet has been sent successfully yet */ + for (rix = rt->rateCount-1; rix > 0; rix--) { + if ((san->ratemask & ((uint64_t) 1<<rix)) == 0) + continue; + + /* Skip HT rates */ + if (rt->info[rix].phy == IEEE80211_T_HT) + continue; + + /* + * Pick the highest rate <= 36 Mbps + * that hasn't failed. + */ + if (DOT11RATE(rix) <= 72 && + san->stats[size_bin][rix].successive_failures == 0) { + break; + } + } + return rix; +#undef DOT11RATE +} + +/* + * Pick a HT rate to begin using. + * + * Don't use any non-HT rates; only consider HT rates. + */ +static int +sample_pick_seed_rate_ht(const struct ieee80211_node *ni, int frameLen) +{ +#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_sample_node *san = ni->ni_rctls; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + + const int size_bin = size_to_bin(frameLen); + int rix = -1, ht_rix = -1; + + /* no packet has been sent successfully yet */ + for (rix = rt->rateCount-1; rix > 0; rix--) { + /* Skip rates we can't use */ + if ((san->ratemask & ((uint64_t) 1<<rix)) == 0) + continue; + + /* Keep a copy of the last seen HT rate index */ + if (rt->info[rix].phy == IEEE80211_T_HT) + ht_rix = rix; + + /* Skip non-HT rates */ + if (rt->info[rix].phy != IEEE80211_T_HT) + continue; + + /* + * Pick a medium-speed rate regardless of stream count + * which has not seen any failures. Higher rates may fail; + * we'll try them later. + */ + if (((MCS(rix) & 0x7) <= 4) && + san->stats[size_bin][rix].successive_failures == 0) { + break; + } + } + + /* + * If all the MCS rates have successive failures, rix should be + * > 0; otherwise use the lowest MCS rix (hopefully MCS 0.) + */ + return MAX(rix, ht_rix); +#undef MCS +} + +static int +sample_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused) +{ +#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) +#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) +#define RATE(ix) (DOT11RATE(ix) / 2) + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_sample_node *san = ni->ni_rctls; + struct ieee80211_sample *sample = san->san_sample; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + + int rix, mrr, best_rix, change_rates; + unsigned average_tx_time; + + size_t frameLen = (size_t)iarg; + const int size_bin = size_to_bin(frameLen); + + sample_update_static_rix(ni); + + if (san->static_rix != -1) { + rix = san->static_rix; + goto done; + } + + if (IEEE80211_RATECTL_HASCAP_MRR(vap)) + mrr = 1; + if (! IEEE80211_RATECTL_HASCAP_MRRPROT(vap)) + mrr = 0; + + best_rix = pick_best_rate(ni, rt, size_bin, !mrr); + if (best_rix >= 0) { + average_tx_time = san->stats[size_bin][best_rix].average_tx_time; + } else { + average_tx_time = 0; + } + + /* + * Limit the time measuring the performance of other tx + * rates to sample_rate% of the total transmission time. + */ + if (san->sample_tt[size_bin] < average_tx_time * (san->packets_since_sample[size_bin] * + sample->sample_rate/100)) { + rix = pick_sample_rate(ni, rt, size_bin); + IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, + &ni, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s", + average_tx_time, + san->sample_tt[size_bin], + bin_to_size(size_bin), + dot11rate(rt, rix), + dot11rate_label(rt, rix), + dot11rate(rt, san->current_rix[size_bin]), + dot11rate_label(rt, san->current_rix[size_bin])); + if (rix != san->current_rix[size_bin]) { + san->current_sample_rix[size_bin] = rix; + } else { + san->current_sample_rix[size_bin] = -1; + } + san->packets_since_sample[size_bin] = 0; + } else { + change_rates = 0; + if (!san->packets_sent[size_bin] || best_rix == -1) { + /* no packet has been sent successfully yet */ + change_rates = 1; + if (ni->ni_flags & IEEE80211_NODE_HT) + best_rix = sample_pick_seed_rate_ht(ni, frameLen); + else + best_rix = sample_pick_seed_rate_legacy(ni, frameLen); + } else if (san->packets_sent[size_bin] < 20) { + /* let the bit-rate switch quickly during the first few packets */ + IEEE80211_NOTE(vap, + IEEE80211_MSG_RATECTL, &ni, + "%s: switching quickly..", __func__); + change_rates = 1; + } else if (ticks - sample->sample_min_switch > san->ticks_since_switch[size_bin]) { + /* min_switch seconds have gone by */ + IEEE80211_NOTE(vap, + IEEE80211_MSG_RATECTL, &ni, + "%s: min_switch %d > ticks_since_switch %d..", + __func__, ticks - sample->sample_min_switch, san->ticks_since_switch[size_bin]); + change_rates = 1; + } else if ((! (ni->ni_flags & IEEE80211_NODE_HT)) && + (2*average_tx_time < san->stats[size_bin][san->current_rix[size_bin]].average_tx_time)) { + /* the current bit-rate is twice as slow as the best one */ + IEEE80211_NOTE(vap, + IEEE80211_MSG_RATECTL, &ni, + "%s: 2x att (= %d) < cur_rix att %d", + __func__, + 2 * average_tx_time, san->stats[size_bin][san->current_rix[size_bin]].average_tx_time); + change_rates = 1; + } else if ((ni->ni_flags & IEEE80211_NODE_HT)) { + int cur_rix = san->current_rix[size_bin]; + int cur_att = san->stats[size_bin][cur_rix].average_tx_time; + /* + * If the node is HT, upgrade it if the MCS rate is + * higher and the average tx time is within 20% of + * the current rate. It can fail a little. + * + * This is likely not optimal! + */ +#if 0 + printf("cur rix/att %x/%d, best rix/att %x/%d\n", + MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time); +#endif + if ((MCS(best_rix) > MCS(cur_rix)) && + (average_tx_time * 8) <= (cur_att * 10)) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_RATECTL, &ni, + "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d", + __func__, + MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att); + change_rates = 1; + } + } + + san->packets_since_sample[size_bin]++; + + if (change_rates) { + if (best_rix != san->current_rix[size_bin]) { + IEEE80211_NOTE(vap, + IEEE80211_MSG_RATECTL, + &ni, +"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d", + __func__, + bin_to_size(size_bin), + RATE(san->current_rix[size_bin]), + san->stats[size_bin][san->current_rix[size_bin]].average_tx_time, + san->stats[size_bin][san->current_rix[size_bin]].perfect_tx_time, + RATE(best_rix), + san->stats[size_bin][best_rix].average_tx_time, + san->stats[size_bin][best_rix].perfect_tx_time, + san->packets_since_switch[size_bin], + mrr); + } + san->packets_since_switch[size_bin] = 0; + san->current_rix[size_bin] = best_rix; + san->ticks_since_switch[size_bin] = ticks; + /* + * Set the visible txrate for this node. + */ + ni->ni_txrate = dot11rate(rt, best_rix); + } + rix = san->current_rix[size_bin]; + san->packets_since_switch[size_bin]++; + } + // *try0 = mrr ? san->sched[rix].t0 : ATH_TXMAXTRY; +done: + + /* + * This bug totally sucks and should be fixed. + * + * For now though, let's not panic, so we can start to figure + * out how to better reproduce it. + */ + if (rix < 0 || rix >= rt->rateCount) { + printf("%s: ERROR: rix %d out of bounds (rateCount=%d)\n", + __func__, + rix, + rt->rateCount); + rix = 0; /* XXX just default for now */ + } + KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix)); + + // *rix0 = rix; + // *txrate = rt->info[rix].rateCode + // | (shortPreamble ? rt->info[rix].shortPreamble : 0); + san->packets_sent[size_bin]++; + + return rix; +#undef DOT11RATE +#undef MCS +#undef RATE +} + +static void +sample_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info) +{ + struct ieee80211_sample_node *san = ni->ni_rctls; + uint8_t rix0 = sample_rate(ni, NULL, 0); + const struct txschedule *sched = &san->sched[rix0]; + struct ieee80211_rc_series *rc = rc_info->iri_rc; + + KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n", + rix0, sched->r0)); + /* XXX */ + rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0; + + rc[0].rix = sched->r0; + rc[1].rix = sched->r1; + rc[2].rix = sched->r2; + rc[3].rix = sched->r3; + + rc[0].tries = sched->t0; + rc[1].tries = sched->t1; + rc[2].tries = sched->t2; + rc[3].tries = sched->t3; +} + +static void +update_stats(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, + int frame_size, + int rix0, int tries0, + int rix1, int tries1, + int rix2, int tries2, + int rix3, int tries3, + int short_tries, int tries, + int nframes, int nbad) +{ + struct ieee80211_sample_node *san = ni->ni_rctls; + struct ieee80211_sample *sample = san->san_sample; + + const int size_bin = size_to_bin(frame_size); + const int size = bin_to_size(size_bin); + + int is_ht40 = ieee80211_ratectl_hascap_cw40(vap, ni); + int tt, tries_so_far; + int pct; + + if (!IS_RATE_DEFINED(san, rix0)) + return; + tt = calc_usecs_unicast_packet(vap, size, rix0, short_tries, + MIN(tries0, tries) - 1, is_ht40); + tries_so_far = tries0; + + if (tries1 && tries_so_far < tries) { + if (!IS_RATE_DEFINED(san, rix1)) + return; + tt += calc_usecs_unicast_packet(vap, size, rix1, short_tries, + MIN(tries1 + tries_so_far, tries) - tries_so_far - 1, is_ht40); + tries_so_far += tries1; + } + + if (tries2 && tries_so_far < tries) { + if (!IS_RATE_DEFINED(san, rix2)) + return; + tt += calc_usecs_unicast_packet(vap, size, rix2, short_tries, + MIN(tries2 + tries_so_far, tries) - tries_so_far - 1, is_ht40); + tries_so_far += tries2; + } + + if (tries3 && tries_so_far < tries) { + if (!IS_RATE_DEFINED(san, rix3)) + return; + tt += calc_usecs_unicast_packet(vap, size, rix3, short_tries, + MIN(tries3 + tries_so_far, tries) - tries_so_far - 1, is_ht40); + } + + if (san->stats[size_bin][rix0].total_packets < sample->sanple_smoothing_minpackets) { + /* just average the first few packets */ + int avg_tx = san->stats[size_bin][rix0].average_tx_time; + int packets = san->stats[size_bin][rix0].total_packets; + san->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes); + } else { + /* use a ewma */ + san->stats[size_bin][rix0].average_tx_time = + ((san->stats[size_bin][rix0].average_tx_time * sample->sanple_smoothing_rate) + + (tt * (100 - sample->sanple_smoothing_rate))) / 100; + } + + /* + * XXX Don't mark the higher bit rates as also having failed; as this + * unfortunately stops those rates from being tasted when trying to + * TX. This happens with 11n aggregation. + */ + if (nframes == nbad) { + san->stats[size_bin][rix0].successive_failures += nbad; + + } else { + san->stats[size_bin][rix0].packets_acked += (nframes - nbad); + san->stats[size_bin][rix0].successive_failures = 0; + } + san->stats[size_bin][rix0].tries += tries; + san->stats[size_bin][rix0].last_tx = ticks; + san->stats[size_bin][rix0].total_packets += nframes; + + /* update EWMA for this rix */ + + /* Calculate percentage based on current rate */ + if (nframes == 0) + nframes = nbad = 1; + pct = ((nframes - nbad) * 1000) / nframes; + + if (san->stats[size_bin][rix0].total_packets < + sample->sanple_smoothing_minpackets) { + /* just average the first few packets */ + int a_pct = (san->stats[size_bin][rix0].packets_acked * 1000) / + (san->stats[size_bin][rix0].total_packets); + san->stats[size_bin][rix0].ewma_pct = a_pct; + } else { + /* use a ewma */ + san->stats[size_bin][rix0].ewma_pct = + ((san->stats[size_bin][rix0].ewma_pct * sample->sanple_smoothing_rate) + + (pct * (100 - sample->sanple_smoothing_rate))) / 100; + } + + if (rix0 == san->current_sample_rix[size_bin]) { + san->sample_tt[size_bin] = tt; + san->current_sample_rix[size_bin] = -1; + } +} + +static void +sample_tx_complete(const struct ieee80211vap *vap, + const struct ieee80211_node *ni, int ok, + void *arg1, void *arg2) +{ + struct ieee80211_sample_node *san = ni->ni_rctls; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + + /* XXX need to change arg2 to pointer of ieee80211_rc_info */ + struct ieee80211_rc_info *rc_info = (struct ieee80211_rc_info*)arg2; + + int final_rix, short_tries, long_tries; + int nframes, nbad; + int frame_size, mrr; + + /* update per vap statistics */ + ieee80211_ratectl_update_stat(vap, rc_info); + + final_rix = rt->rateCodeToIndex[rc_info->iri_txrate]; + short_tries = rc_info->iri_shortretry; + /* XXX why plus 1 here? */ + long_tries = rc_info->iri_longretry + 1; + + nframes = rc_info->iri_txcnt; + nbad = rc_info->iri_failcnt; + + frame_size = rc_info->iri_framelen; + mrr = 0; + + if (nframes == 0) { + return; + } + + if (frame_size == 0) /* NB: should not happen */ + frame_size = 1500; + + if (san->ratemask == 0) { + return; + } + + if (IEEE80211_RATECTL_HASCAP_MRR(vap)) + mrr = 1; + /* XXX check HT protmode too */ + if (mrr && !IEEE80211_RATECTL_HASCAP_MRRPROT(vap)) + mrr = 0; + + if (!mrr || rc_info->iri_finaltsi == 0) { + if (!IS_RATE_DEFINED(san, final_rix)) { + return; + } + /* + * Only one rate was used; optimize work. + */ + update_stats(vap, ni, frame_size, + final_rix, long_tries, + 0, 0, + 0, 0, + 0, 0, + short_tries, long_tries, + nframes, nbad); + + } else { + int finalTSIdx = rc_info->iri_finaltsi; + int i; + + /* + * NB: series > 0 are not penalized for failure + * based on the try counts under the assumption + * that losses are often bursty and since we + * sample higher rates 1 try at a time doing so + * may unfairly penalize them. + */ + if (rc[0].tries) { + update_stats(vap, ni, frame_size, + rc[0].rix, rc[0].tries, + rc[1].rix, rc[1].tries, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries, + short_tries, long_tries, + nframes, nbad); + long_tries -= rc[0].tries; + } + + if (rc[1].tries && finalTSIdx > 0) { + update_stats(vap, ni, frame_size, + rc[1].rix, rc[1].tries, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries, + 0, 0, + short_tries, long_tries, + nframes, nbad); + long_tries -= rc[1].tries; + } + + if (rc[2].tries && finalTSIdx > 1) { + update_stats(vap, ni, frame_size, + rc[2].rix, rc[2].tries, + rc[3].rix, rc[3].tries, + 0, 0, + 0, 0, + short_tries, long_tries, + nframes, nbad); + long_tries -= rc[2].tries; + } + + if (rc[3].tries && finalTSIdx > 2) { + update_stats(vap, ni, frame_size, + rc[3].rix, rc[3].tries, + 0, 0, + 0, 0, + 0, 0, + short_tries, long_tries, + nframes, nbad); + } + } +} + +static void +sample_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni, + void *arg1, void *arg2, void *arg3) +{ + /* nothing here. */ +} + +static void +sample_setinterval(const struct ieee80211vap *vap, int msecs) +{ + struct ieee80211_sample *sample = vap->iv_rs; + int t; + + if (msecs < 100) + msecs = 100; + t = msecs_to_ticks(msecs); + /* ieee80211_sample doesn't have the sample_interval field by now */ + // sample->sample_interval = (t < 1) ? 1 : t; +} + +static void +sample_stats(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_sample_node *san = ni->ni_rctls; + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + uint64_t mask; + int rix, y; + + printf("\n[%s] refcnt %d static_rix (%d %s) ratemask 0x%jx\n", + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni), + dot11rate(rt, san->static_rix), + dot11rate_label(rt, san->static_rix), + (uintmax_t)san->ratemask); + for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { + printf("[%4u] cur rix %d (%d %s) since switch: packets %d ticks %u\n", + bin_to_size(y), san->current_rix[y], + dot11rate(rt, san->current_rix[y]), + dot11rate_label(rt, san->current_rix[y]), + san->packets_since_switch[y], san->ticks_since_switch[y]); + printf("[%4u] last sample (%d %s) cur sample (%d %s) packets sent %d\n", + bin_to_size(y), + dot11rate(rt, san->last_sample_rix[y]), + dot11rate_label(rt, san->last_sample_rix[y]), + dot11rate(rt, san->current_sample_rix[y]), + dot11rate_label(rt, san->current_sample_rix[y]), + san->packets_sent[y]); + printf("[%4u] packets since sample %d sample tt %u\n", + bin_to_size(y), san->packets_since_sample[y], + san->sample_tt[y]); + } + for (mask = san->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { + if ((mask & 1) == 0) + continue; + for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { + if (san->stats[y][rix].total_packets == 0) + continue; + printf("[%2u %s:%4u] %8ju:%-8ju (%3d%%) (EWMA %3d.%1d%%) T %8ju F %4d avg %5u last %u\n", + dot11rate(rt, rix), dot11rate_label(rt, rix), + bin_to_size(y), + (uintmax_t) san->stats[y][rix].total_packets, + (uintmax_t) san->stats[y][rix].packets_acked, + (int) ((san->stats[y][rix].packets_acked * 100ULL) / + san->stats[y][rix].total_packets), + san->stats[y][rix].ewma_pct / 10, + san->stats[y][rix].ewma_pct % 10, + (uintmax_t) san->stats[y][rix].tries, + san->stats[y][rix].successive_failures, + san->stats[y][rix].average_tx_time, + ticks - san->stats[y][rix].last_tx); + } + } +} + +static int +sample_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211com *ic = vap->iv_ifp->if_l2com; + int error, v; + + v = 0; + error = sysctl_handle_int(oidp, &v, 0, req); + if (error || !req->newptr) + return error; + ieee80211_iterate_nodes(&ic->ic_sta, sample_stats, NULL); + return 0; +} + +static int +sample_sysctl_smoothing_rate(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211_sample *sample = vap->iv_rs; + int rate, error; + + rate = sample->sample_smoothing_rate; + error = sysctl_handle_int(oidp, &rate, 0, req); + if (error || !req->newptr) + return error; + if (!(0 <= rate && rate < 100)) + return EINVAL; + sample->sample_smoothing_rate = rate; + sample->sample_smoothing_minpackets = 100 / (100 - rate); + return 0; +} + +static int +sample_sysctl_sample_rate(SYSCTL_HANDLER_ARGS) +{ + struct ieee80211vap *vap = arg1; + struct ieee80211_sample *sample = vap->iv_rs; + int rate, error; + + rate = sample->sample_rate; + error = sysctl_handle_int(oidp, &rate, 0, req); + if (error || !req->newptr) + return error; + if (!(2 <= rate && rate <= 100)) + return EINVAL; + sample->sample_rate = rate; + return 0; +} + +static void +sample_sysctlattach(struct ieee80211vap *vap, + struct sysctl_ctx_list *ctx, struct sysctl_oid *tree) +{ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "sample_smoothing_rate", CTLTYPE_INT | CTLFLAG_RW, vap, 0, + sample_sysctl_smoothing_rate, "I", + "sample: smoothing rate for avg tx time (%%)"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "sample_rate", CTLTYPE_INT | CTLFLAG_RW, vap, 0, + sample_sysctl_sample_rate, "I", + "sample: percent air time devoted to sampling new rates (%%)"); + /* XXX max_successive_failures, stale_failure_timeout, min_switch */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "sample_stats", CTLTYPE_INT | CTLFLAG_RW, vap, 0, + sample_sysctl_stats, "I", "sample: print statistics"); +} Index: net80211/ieee80211_rc_sample.h =================================================================== --- net80211/ieee80211_rc_sample.h (revision 0) +++ net80211/ieee80211_rc_sample.h (revision 256825) @@ -0,0 +1,287 @@ +/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.h 240382 2012-08-15 07:10:10Z adrian $*/ + +/*- + * Copyright (c) 2005 John Bicket + * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _NET80211_IEEE80211_RATECTL_SAMPLE_H_ +#define _NET80211_IEEE80211_RATECTL_SAMPLE_H_ + +#include <net80211/ieee80211_ratectl.h> + +/* + * for now, we track performance for three different packet + * size buckets + */ +#define NUM_PACKET_SIZE_BINS 2 +#define SAMPLE_MAXRATES 64 /* NB: corresponds to hal info[32] */ + +/* + * Rate control settings. + */ +struct ieee80211_sample { + struct ieee80211_rc_stat sample_stat; + int sample_smoothing_rate; /* ewma percentage [0..99] */ + int sample_smoothing_minpackets; + int sample_rate; /* %time to try different tx rates */ + int sample_max_successive_failures; + int sample_stale_failure_timeout; /* how long to honor max_successive_failures */ + int sample_min_switch; /* min time between rate changes */ + int sample_min_good_pct; /* min good percentage for a rate to be considered */ +}; + +struct rate_stats { + unsigned average_tx_time; + int successive_failures; + uint64_t tries; + uint64_t total_packets; /* pkts total since assoc */ + uint64_t packets_acked; /* pkts acked since assoc */ + int ewma_pct; /* EWMA percentage */ + unsigned perfect_tx_time; /* transmit time for 0 retries */ + int last_tx; +}; + +struct txschedule { + uint8_t t0, r0; /* series 0: tries, rate code */ + uint8_t t1, r1; /* series 1: tries, rate code */ + uint8_t t2, r2; /* series 2: tries, rate code */ + uint8_t t3, r3; /* series 3: tries, rate code */ +}; + +/* + * Rate control state for a given node. + */ +/* XXX change naming conversion? */ +struct ieee80211_sample_node { + struct ieee80211_sample *san_sample;/* backpointer */ + int static_rix; /* rate index of fixed tx rate */ + uint64_t ratemask; /* bit mask of valid rate indices */ + const struct txschedule *sched; /* tx schedule table */ + + struct rate_stats stats[NUM_PACKET_SIZE_BINS][SAMPLE_MAXRATES]; + int last_sample_rix[NUM_PACKET_SIZE_BINS]; + + int current_sample_rix[NUM_PACKET_SIZE_BINS]; + int packets_sent[NUM_PACKET_SIZE_BINS]; + + int current_rix[NUM_PACKET_SIZE_BINS]; + int packets_since_switch[NUM_PACKET_SIZE_BINS]; + unsigned ticks_since_switch[NUM_PACKET_SIZE_BINS]; + + int packets_since_sample[NUM_PACKET_SIZE_BINS]; + unsigned sample_tt[NUM_PACKET_SIZE_BINS]; +}; + +#define IS_RATE_DEFINED(san, rix) (((san)->ratemask & (1<<(rix))) != 0) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 }; + +static inline int +bin_to_size(int index) +{ + return packet_size_bins[index]; +} + +static inline int +size_to_bin(int size) +{ +#if NUM_PACKET_SIZE_BINS > 1 + if (size <= packet_size_bins[0]) + return 0; +#endif +#if NUM_PACKET_SIZE_BINS > 2 + if (size <= packet_size_bins[1]) + return 1; +#endif +#if NUM_PACKET_SIZE_BINS > 3 + if (size <= packet_size_bins[2]) + return 2; +#endif +#if NUM_PACKET_SIZE_BINS > 4 +#error "add support for more packet sizes" +#endif + return NUM_PACKET_SIZE_BINS-1; +} + +static uint32_t sample_pkt_txtime(const struct ieee80211_rate_table *rt, + uint32_t frameLen, uint16_t rateix, int isht40, int isShortPreamble) +{ + uint8_t rc; + int numStreams; + + rc = rt->info[rateix].rateCode; + + /* Legacy rate? Return the old way */ + if (! IS_HT_RATE(rc)) + return ieee80211_compute_duration(rt, frameLen, rateix, isShortPreamble); + + /* 11n frame - extract out the number of spatial streams */ + numStreams = HT_RC_2_STREAMS(rc); + KASSERT(numStreams > 0 && numStreams <= 4, + ("number of spatial streams needs to be 1..3: MCS rate 0x%x!", + rateix)); + + return ieee80211_compute_duration_ht(frameLen, rc, numStreams, isht40, isShortPreamble); +} + +#define WIFI_CW_MIN 31 +#define WIFI_CW_MAX 1023 + +/* + * Calculate the transmit duration of a frame. + */ +static unsigned calc_usecs_unicast_packet(const struct ieee80211vap *vap, + int length, + int rix, int short_retries, + int long_retries, int is_ht40) +{ + const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan); + struct ieee80211com *ic = vap->iv_ic; + int curmode = ieee80211_chan2mode(vap->iv_ic->ic_curchan); + + unsigned t_slot, t_difs, t_sifs; + int rts, cts; + int tt, x, cw, cix; + + int tt = 0; + int x = 0; + int cw = WIFI_CW_MIN; + + KASSERT(rt != NULL, ("no rate table, mode %u", curmode)); + + if (rix >= rt->rateCount) { + printf("bogus rix %d, max %u, mode %u\n", + rix, rt->rateCount, curmode); + return 0; + } + cix = rt->info[rix].controlRate; + /* + * XXX getting mac/phy level timings should be fixed for turbo + * rates, and there is probably a way to get this from the + * hal... + */ + switch (rt->info[rix].phy) { + case IEEE80211_T_OFDM: + t_slot = 9; + t_sifs = 16; + t_difs = 28; + /* fall through */ + case IEEE80211_T_TURBO: + t_slot = 9; + t_sifs = 8; + t_difs = 28; + break; + case IEEE80211_T_HT: + t_slot = 9; + t_sifs = 8; + t_difs = 28; + break; + case IEEE80211_T_DS: + /* fall through to default */ + default: + /* pg 205 ieee.802.11.pdf */ + t_slot = 20; + t_difs = 50; + t_sifs = 10; + } + + rts = cts = 0; + + if ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt->info[rix].phy == IEEE80211_T_OFDM) { + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + rts = 1; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + cts = 1; + + int protrix; + if (curmode == IEEE80211_MODE_11G) + protrix = rt->rateCodeToIndex[2*2]; + else + protrix = rt->rateCodeToIndex[2*1]; + if (0xff == protrix) + protrix = 0; + + cix = rt->info[protrix].controlRate; + } + + if (0 /*length > ic->ic_rtsthreshold */) { + rts = 1; + } + + if (rts || cts) { + int ctsrate; + int ctsduration = 0; + + /* NB: this is intentionally not a runtime check */ + KASSERT(cix < rt->rateCount, + ("bogus cix %d, max %u, mode %u\n", cix, rt->rateCount, + curmode)); + + ctsrate = rt->info[cix].rateCode | rt->info[cix].shortPreamble; + if (rts) /* SIFS + CTS */ + ctsduration += rt->info[cix].spAckDuration; + + /* XXX assumes short preamble */ + ctsduration += sample_pkt_txtime(rt, length, rix, is_ht40, 0); + + if (cts) /* SIFS + ACK */ + ctsduration += rt->info[cix].spAckDuration; + + tt += (short_retries + 1) * ctsduration; + } + tt += t_difs; + + /* XXX assumes short preamble */ + tt += (long_retries+1)*sample_pkt_txtime(rt, length, rix, is_ht40, 0); + + tt += (long_retries+1)*(t_sifs + rt->info[rix].spAckDuration); + + for (x = 0; x <= short_retries + long_retries; x++) { + cw = MIN(WIFI_CW_MAX, (cw + 1) * 2); + tt += (t_slot * cw/2); + } + return tt; +} + +#endif /* _NET80211_IEEE80211_RATECTL_SAMPLE_H_ */ Index: net80211/ieee80211_ratectl_none.h =================================================================== --- net80211/ieee80211_ratectl_none.h (revision 0) +++ net80211/ieee80211_ratectl_none.h (revision 256825) @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _NET80211_IEEE80211_RATECTL_NONE_H_ +#define _NET80211_IEEE80211_RATECTL_NONE_H_ + +#include <net80211/ieee80211_ratectl.h> + +/* + * Rate control settings. + */ + +struct ieee80211_none { + struct ieee80211_rc_stat none_stat; + /* other stuffs */ +}; + +/* + * Rate control state for a given node. + */ +struct ieee80211_none_node { + struct ieee80211_none *non_none; +}; + +#endif /* _NET80211_IEEE80211_RATECTL_NONE_H_ */ Index: net80211/ieee80211_rc_sample_txsched.h =================================================================== --- net80211/ieee80211_rc_sample_txsched.h (revision 0) +++ net80211/ieee80211_rc_sample_txsched.h (revision 256825) @@ -0,0 +1,241 @@ +/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/tx_schedules.h 240384 2012-08-15 07:50:42Z adrian $*/ + +/*- + * Copyright (c) 2005 John Bicket + * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ +#ifndef _NET80211_IEEE80211_RATECTL_SAMPLE_TXSCHEDULES_H_ +#define _NET80211_IEEE80211_RATECTL_SAMPLE_TXSCHEDULES_H_ + +#include <sys/cdefs.h> + +#define A(_r) \ + (((_r) == 6) ? 0 : (((_r) == 9) ? 1 : (((_r) == 12) ? 2 : \ + (((_r) == 18) ? 3 : (((_r) == 24) ? 4 : (((_r) == 36) ? 5 : \ + (((_r) == 48) ? 6 : (((_r) == 54) ? 7 : 0)))))))) +static const struct txschedule series_11a[] = { + { 3,A( 6), 3,A( 6), 0,A( 6), 0,A( 6) }, /* 6Mb/s */ + { 4,A( 9), 3,A( 6), 4,A( 6), 0,A( 6) }, /* 9Mb/s */ + { 4,A(12), 3,A( 6), 4,A( 6), 0,A( 6) }, /* 12Mb/s */ + { 4,A(18), 3,A( 12), 4,A( 6), 2,A( 6) }, /* 18Mb/s */ + { 4,A(24), 3,A( 18), 4,A( 12), 2,A( 6) }, /* 24Mb/s */ + { 4,A(36), 3,A( 24), 4,A( 18), 2,A( 6) }, /* 36Mb/s */ + { 4,A(48), 3,A( 36), 4,A( 24), 2,A(12) }, /* 48Mb/s */ + { 4,A(54), 3,A( 48), 4,A( 36), 2,A(24) } /* 54Mb/s */ +}; + +#define NA1(_r) \ + (((_r) == 6.5) ? 8 : (((_r) == 13) ? 9 : (((_r) == 19.5)? 10 : \ + (((_r) == 26) ? 11 : (((_r) == 39) ? 12 : (((_r) == 52) ? 13 : \ + (((_r) == 58.5)? 14 : (((_r) == 65) ? 15 : 0)))))))) +#define NA2(_r) \ + (((_r) == 13) ? 16 : (((_r) == 26) ? 17 : (((_r) == 39) ? 18 : \ + (((_r) == 52) ? 19 : (((_r) == 78) ? 20 : (((_r) == 104)? 21 : \ + (((_r) == 117)? 22 : (((_r) == 130)? 23 : 0)))))))) +#define NA3(_r) \ + (((_r) == 19.5) ? 24 : (((_r) == 39) ? 25 : (((_r) == 58.5) ? 26 : \ + (((_r) == 78) ? 27 : (((_r) == 117) ? 28 : (((_r) == 156) ? 29 : \ + (((_r) == 175.5) ? 30 : (((_r) == 195)? 31 : 0)))))))) +static const struct txschedule series_11na[] = { + { 3,A( 6), 3,A( 6), 0,A( 6), 0,A( 6) }, /* 6Mb/s */ + { 4,A( 9), 3,A( 6), 4,A( 6), 0,A( 6) }, /* 9Mb/s */ + { 4,A(12), 3,A( 6), 4,A( 6), 0,A( 6) }, /* 12Mb/s */ + { 4,A(18), 3,A( 12), 4,A( 6), 2,A( 6) }, /* 18Mb/s */ + { 4,A(24), 3,A( 18), 4,A( 12), 2,A( 6) }, /* 24Mb/s */ + { 4,A(36), 3,A( 24), 4,A( 18), 2,A( 6) }, /* 36Mb/s */ + { 4,A(48), 3,A( 36), 4,A( 24), 2,A(12) }, /* 48Mb/s */ + { 4,A(54), 3,A( 48), 4,A( 36), 2,A(24) }, /* 54Mb/s */ + + /* 1 stream rates */ + + { 3,NA1( 6.5), 3,NA1( 6.5), 0,NA1( 6.5), 0,NA1(6.5) }, /* 6.5Mb/s */ + { 4,NA1( 13), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) }, /* 13Mb/s */ + { 4,NA1(19.5), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) }, /*19.5Mb/s */ + { 4,NA1( 26), 3,NA1(19.5), 4,NA1( 6.5), 2,NA1(6.5) }, /* 26Mb/s */ + { 4,NA1( 39), 3,NA1( 26), 4,NA1(19.5), 2,NA1(6.5) }, /* 39Mb/s */ + { 4,NA1( 52), 3,NA1( 39), 4,NA1( 26), 2,NA1(6.5) }, /* 52Mb/s */ + { 4,NA1(58.5), 3,NA1( 52), 4,NA1( 39), 2,NA1( 13) }, /*58.5Mb/s */ + { 4,NA1( 65), 3,NA1(58.5), 4,NA1( 52), 2,NA1( 13) }, /* 65Mb/s */ + + /* 2 stream rates */ + + { 3,NA2( 13), 3,NA2( 13), 0,NA2( 13), 0,NA2( 13) }, /* 13Mb/s */ + { 4,NA2( 26), 3,NA2( 13), 4,NA2( 13), 0,NA2( 13) }, /* 26Mb/s */ + { 4,NA2( 39), 3,NA2( 26), 4,NA2( 13), 2,NA2( 13) }, /* 39Mb/s */ + { 4,NA2( 52), 3,NA2( 39), 4,NA2( 26), 2,NA2( 13) }, /* 52Mb/s */ + { 4,NA2( 78), 3,NA2( 52), 4,NA2( 39), 2,NA2( 13) }, /* 78Mb/s */ + { 4,NA2( 104), 3,NA2( 78), 4,NA2( 52), 2,NA2( 13) }, /* 104Mb/s */ + { 4,NA2( 117), 3,NA2( 104), 4,NA2( 78), 2,NA2( 26) }, /* 117Mb/s */ + { 4,NA2( 130), 3,NA2( 117), 4,NA2( 104), 2,NA2( 26) }, /* 130Mb/s */ + + /* 3 stream rates */ + + { 3,NA3(19.5), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) }, /* 19Mb/s */ + { 3,NA3( 39), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) }, /* 39Mb/s */ + { 3,NA3(58.5), 3,NA3( 39), 0,NA3(19.5), 0,NA3(19.5) }, /* 58Mb/s */ + { 3,NA3( 78), 3,NA3(58.5), 0,NA3( 39), 0,NA3(19.5) }, /* 78Mb/s */ + { 3,NA3( 117), 3,NA3( 78), 0,NA3(58.5), 0,NA3(19.5) }, /* 117Mb/s */ + { 3,NA3( 156), 3,NA3( 117), 0,NA3( 78), 0,NA3(19.5) }, /* 156Mb/s */ + { 3,NA3(175.5), 3,NA3( 156), 0,NA3( 117), 0,NA3( 39) }, /* 175Mb/s */ + { 3,NA3( 195), 3,NA3( 195), 0,NA3( 156), 0,NA3(58.5) }, /* 195Mb/s */ +}; +#undef A +#undef NA3 +#undef NA2 +#undef NA1 + +#define G(_r) \ + (((_r) == 1) ? 0 : (((_r) == 2) ? 1 : (((_r) == 5.5) ? 2 : \ + (((_r) == 11) ? 3 : (((_r) == 6) ? 4 : (((_r) == 9) ? 5 : \ + (((_r) == 12) ? 6 : (((_r) == 18) ? 7 : (((_r) == 24) ? 8 : \ + (((_r) == 36) ? 9 : (((_r) == 48) ? 10 : (((_r) == 54) ? 11 : 0)))))))))))) +static const struct txschedule series_11g[] = { + { 3,G( 1), 3,G( 1), 0,G( 1), 0,G( 1) }, /* 1Mb/s */ + { 4,G( 2), 3,G( 1), 4,G( 1), 0,G( 1) }, /* 2Mb/s */ + { 4,G(5.5),3,G( 2), 4,G( 1), 2,G( 1) }, /* 5.5Mb/s */ + { 4,G(11), 3,G(5.5), 4,G( 2), 2,G( 1) }, /* 11Mb/s */ + { 4,G( 6), 3,G(5.5), 4,G( 2), 2,G( 1) }, /* 6Mb/s */ + { 4,G( 9), 3,G( 6), 4,G(5.5), 2,G( 1) }, /* 9Mb/s */ + { 4,G(12), 3,G( 11), 4,G(5.5), 2,G( 1) }, /* 12Mb/s */ + { 4,G(18), 3,G( 12), 4,G( 11), 2,G( 1) }, /* 18Mb/s */ + { 4,G(24), 3,G( 18), 4,G( 12), 2,G( 1) }, /* 24Mb/s */ + { 4,G(36), 3,G( 24), 4,G( 18), 2,G( 1) }, /* 36Mb/s */ + { 4,G(48), 3,G( 36), 4,G( 24), 2,G( 1) }, /* 48Mb/s */ + { 4,G(54), 3,G( 48), 4,G( 36), 2,G( 1) } /* 54Mb/s */ +}; + +#define NG1(_r) \ + (((_r) == 6.5) ? 12 : (((_r) == 13) ? 13 : (((_r) == 19.5)? 14 : \ + (((_r) == 26) ? 15 : (((_r) == 39) ? 16 : (((_r) == 52) ? 17 : \ + (((_r) == 58.5)? 18 : (((_r) == 65) ? 19 : 0)))))))) +#define NG2(_r) \ + (((_r) == 13) ? 20 : (((_r) == 26) ? 21 : (((_r) == 39) ? 22 : \ + (((_r) == 52) ? 23 : (((_r) == 78) ? 24 : (((_r) == 104) ? 25 : \ + (((_r) == 117) ? 26 : (((_r) == 130)? 27 : 0)))))))) +#define NG3(_r) \ + (((_r) == 19.5) ? 28 : (((_r) == 39) ? 29 : (((_r) == 58.5) ? 30 : \ + (((_r) == 78) ? 31 : (((_r) == 117) ? 32 : (((_r) == 156) ? 33 : \ + (((_r) == 175.5) ? 34 : (((_r) == 195)? 35 : 0)))))))) + +static const struct txschedule series_11ng[] = { + { 3,G( 1), 3,G( 1), 0,G( 1), 0,G( 1) }, /* 1Mb/s */ + { 4,G( 2), 3,G( 1), 4,G( 1), 0,G( 1) }, /* 2Mb/s */ + { 4,G(5.5),3,G( 2), 4,G( 1), 2,G( 1) }, /* 5.5Mb/s */ + { 4,G(11), 3,G(5.5), 4,G( 2), 2,G( 1) }, /* 11Mb/s */ + { 4,G( 6), 3,G(5.5), 4,G( 2), 2,G( 1) }, /* 6Mb/s */ + { 4,G( 9), 3,G( 6), 4,G(5.5), 2,G( 1) }, /* 9Mb/s */ + { 4,G(12), 3,G( 11), 4,G(5.5), 2,G( 1) }, /* 12Mb/s */ + { 4,G(18), 3,G( 12), 4,G( 11), 2,G( 1) }, /* 18Mb/s */ + { 4,G(24), 3,G( 18), 4,G( 12), 2,G( 1) }, /* 24Mb/s */ + { 4,G(36), 3,G( 24), 4,G( 18), 2,G( 1) }, /* 36Mb/s */ + { 4,G(48), 3,G( 36), 4,G( 24), 2,G( 1) }, /* 48Mb/s */ + { 4,G(54), 3,G( 48), 4,G( 36), 2,G( 1) }, /* 54Mb/s */ + + /* 1 stream rates */ + + { 3,NG1( 6.5), 3,NG1( 6.5), 0,NG1( 6.5), 0,NG1(6.5) }, /* 6.5Mb/s */ + { 4,NG1( 13), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) }, /* 13Mb/s */ + { 4,NG1(19.5), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) }, /*19.5Mb/s */ + { 4,NG1( 26), 3,NG1(19.5), 4,NG1( 6.5), 2,NG1(6.5) }, /* 26Mb/s */ + { 4,NG1( 39), 3,NG1( 26), 4,NG1(19.5), 2,NG1(6.5) }, /* 39Mb/s */ + { 4,NG1( 52), 3,NG1( 39), 4,NG1( 26), 2,NG1(6.5) }, /* 52Mb/s */ + { 4,NG1(58.5), 3,NG1( 52), 4,NG1( 39), 2,NG1( 13) }, /*58.5Mb/s */ + { 4,NG1( 65), 3,NG1(58.5), 4,NG1( 52), 2,NG1( 13) }, /* 65Mb/s */ + + /* 2 stream rates */ + + { 3,NG2( 13), 3,NG2( 13), 0,NG2( 13), 0,NG2( 13) }, /* 13Mb/s */ + { 4,NG2( 26), 3,NG2( 13), 4,NG2( 13), 0,NG2( 13) }, /* 26Mb/s */ + { 4,NG2( 39), 3,NG2( 26), 4,NG2( 13), 2,NG2( 13) }, /* 39Mb/s */ + { 4,NG2( 52), 3,NG2( 39), 4,NG2( 26), 2,NG2( 13) }, /* 52Mb/s */ + { 4,NG2( 78), 3,NG2( 52), 4,NG2( 39), 2,NG2( 13) }, /* 78Mb/s */ + { 4,NG2( 104), 3,NG2( 78), 4,NG2( 52), 2,NG2( 13) }, /* 104Mb/s */ + { 4,NG2( 117), 3,NG2( 104), 4,NG2( 78), 2,NG2( 26) }, /* 117Mb/s */ + { 4,NG2( 130), 3,NG2( 117), 4,NG2( 104), 2,NG2( 26) }, /* 130Mb/s */ + + /* 3 stream rates */ + + { 3,NG3(19.5), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) }, /* 19Mb/s */ + { 3,NG3( 39), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) }, /* 39Mb/s */ + { 3,NG3(58.5), 3,NG3( 39), 0,NG3(19.5), 0,NG3(19.5) }, /* 58Mb/s */ + { 3,NG3( 78), 3,NG3(58.5), 0,NG3( 39), 0,NG3(19.5) }, /* 78Mb/s */ + { 3,NG3( 117), 3,NG3( 78), 0,NG3(58.5), 0,NG3(19.5) }, /* 117Mb/s */ + { 3,NG3( 156), 3,NG3( 117), 0,NG3( 78), 0,NG3(19.5) }, /* 156Mb/s */ + { 3,NG3(175.5), 3,NG3( 156), 0,NG3( 117), 0,NG3( 39) }, /* 175Mb/s */ + { 3,NG3( 195), 3,NG3( 195), 0,NG3( 156), 0,NG3(58.5) }, /* 195Mb/s */ + +}; +#undef G +#undef NG3 +#undef NG2 +#undef NG1 + +#define H(_r) \ + (((_r) == 3) ? 0 : (((_r) == 4.5) ? 1 : (((_r) == 6) ? 2 : \ + (((_r) == 9) ? 3 : (((_r) == 12) ? 4 : (((_r) == 18) ? 5 : \ + (((_r) == 24) ? 6 : (((_r) == 27) ? 7 : 0)))))))) +static const struct txschedule series_half[] = { + { 3,H( 3), 3,H( 3), 0,H( 3), 0,H( 3) }, /* 3Mb/s */ + { 4,H(4.5),3,H( 3), 4,H( 3), 0,H( 3) }, /* 4.5Mb/s */ + { 4,H( 6), 3,H( 3), 4,H( 3), 0,H( 3) }, /* 6Mb/s */ + { 4,H( 9), 3,H( 6), 4,H( 3), 2,H( 3) }, /* 9Mb/s */ + { 4,H(12), 3,H( 9), 4,H( 6), 2,H( 3) }, /* 12Mb/s */ + { 4,H(18), 3,H( 12), 4,H( 9), 2,H( 3) }, /* 18Mb/s */ + { 4,H(24), 3,H( 18), 4,H( 12), 2,H( 6) }, /* 24Mb/s */ + { 4,H(27), 3,H( 24), 4,H( 18), 2,H(12) } /* 27Mb/s */ +}; +#undef H + +#ifdef Q +#undef Q +#endif +#define Q(_r) \ + (((_r) == 1.5) ? 0 : (((_r) ==2.25) ? 1 : (((_r) == 3) ? 2 : \ + (((_r) == 4.5) ? 3 : (((_r) == 6) ? 4 : (((_r) == 9) ? 5 : \ + (((_r) == 12) ? 6 : (((_r) == 13.5)? 7 : 0)))))))) +static const struct txschedule series_quarter[] = { + { 3,Q( 1.5),3,Q(1.5), 0,Q(1.5), 0,Q(1.5) }, /* 1.5Mb/s */ + { 4,Q(2.25),3,Q(1.5), 4,Q(1.5), 0,Q(1.5) }, /*2.25Mb/s */ + { 4,Q( 3),3,Q(1.5), 4,Q(1.5), 0,Q(1.5) }, /* 3Mb/s */ + { 4,Q( 4.5),3,Q( 3), 4,Q(1.5), 2,Q(1.5) }, /* 4.5Mb/s */ + { 4,Q( 6),3,Q(4.5), 4,Q( 3), 2,Q(1.5) }, /* 6Mb/s */ + { 4,Q( 9),3,Q( 6), 4,Q(4.5), 2,Q(1.5) }, /* 9Mb/s */ + { 4,Q( 12),3,Q( 9), 4,Q( 6), 2,Q( 3) }, /* 12Mb/s */ + { 4,Q(13.5),3,Q( 12), 4,Q( 9), 2,Q( 6) } /*13.5Mb/s */ +}; +#undef Q + +#endif /* _NET80211_IEEE80211_RATECTL_SAMPLE_TXSCHEDULES_H_ */ Index: net80211/ieee80211_rssadapt.h =================================================================== --- net80211/ieee80211_rssadapt.h (revision 254826) +++ net80211/ieee80211_rssadapt.h (working copy) @@ -32,6 +32,8 @@ #ifndef _NET80211_IEEE80211_RSSADAPT_H_ #define _NET80211_IEEE80211_RSSADAPT_H_ +#include <net80211/ieee80211_ratectl.h> + /* Data-rate adaptation loosely based on "Link Adaptation Strategy * for IEEE 802.11 WLAN via Received Signal Strength Measurement" * by Javier del Prado Pavon and Sunghyun Choi. @@ -43,6 +45,7 @@ #define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */ struct ieee80211_rssadapt { + struct ieee80211_rc_stat rssadapt_stat; const struct ieee80211vap *vap; int interval; /* update interval (ticks) */ }; Index: net80211/ieee80211_amrr.h =================================================================== --- net80211/ieee80211_amrr.h (revision 254826) +++ net80211/ieee80211_amrr.h (working copy) @@ -20,6 +20,7 @@ #ifndef _NET80211_IEEE80211_AMRR_H_ #define _NET80211_IEEE80211_AMRR_H_ +#include <net80211/ieee80211_ratectl.h> /*- * Naive implementation of the Adaptive Multi Rate Retry algorithm: * @@ -35,6 +36,7 @@ struct ieee80211vap; struct ieee80211_amrr { + struct ieee80211_rc_stat amrr_stat; u_int amrr_min_success_threshold; u_int amrr_max_success_threshold; int amrr_interval; /* update interval (ticks) */ Index: dev/iwn/if_iwn.c =================================================================== --- dev/iwn/if_iwn.c (revision 254826) +++ dev/iwn/if_iwn.c (working copy) @@ -876,7 +876,8 @@ ivp->iv_newstate = vap->iv_newstate; vap->iv_newstate = iwn_newstate; - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* Complete setup. */ ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status); ic->ic_opmode = opmode; @@ -2499,7 +2500,8 @@ uint64_t bitmap; uint16_t ssn; uint8_t tid; - int ackfailcnt = 0, i, lastidx, qid, *res, shift; + /* int failcnt = 0; */ + int i, lastidx, qid, *res, shift; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); @@ -2563,12 +2565,18 @@ for (i = 0; bitmap; i++) { if ((bitmap & 1) == 0) { ifp->if_oerrors++; +#if 0 ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); +#endif + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */ } else { ifp->if_opackets++; +#if 0 ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); +#endif + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */ } bitmap >>= 1; } @@ -2807,12 +2815,18 @@ */ if (status & IWN_TX_FAIL) { ifp->if_oerrors++; +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ } else { ifp->if_opackets++; +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ } m_freem(m); ieee80211_free_node(ni); Index: dev/bwi/if_bwi.c =================================================================== --- dev/bwi/if_bwi.c (revision 254826) +++ dev/bwi/if_bwi.c (working copy) @@ -617,7 +617,8 @@ #if 0 vap->iv_update_beacon = bwi_beacon_update; #endif - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* complete setup */ ieee80211_vap_attach(vap, bwi_media_change, ieee80211_media_status); @@ -3377,9 +3378,12 @@ * well so to avoid over-aggressive downshifting we * treat any number of retries as "1". */ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, (data_txcnt > 1) ? IEEE80211_RATECTL_TX_SUCCESS : IEEE80211_RATECTL_TX_FAILURE, &acked, NULL); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ } /* Index: dev/bwn/if_bwn.c =================================================================== --- dev/bwn/if_bwn.c (revision 254826) +++ dev/bwn/if_bwn.c (working copy) @@ -2970,7 +2970,8 @@ /* override max aid so sta's cannot assoc when we're out of sta id's */ vap->iv_max_aid = BWN_STAID_MAX; - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, @@ -8991,8 +8992,9 @@ struct bwn_stats *stats = &mac->mac_stats; struct ieee80211_node *ni; struct ieee80211vap *vap; - int retrycnt = 0, slot; - + /*int retrycnt = 0, slot;*/ + int slot; + BWN_ASSERT_LOCKED(mac->mac_sc); if (status->im) @@ -9020,11 +9022,14 @@ if (meta->mt_islast) { ni = meta->mt_ni; vap = ni->ni_vap; +#if 0 ieee80211_ratectl_tx_complete(vap, ni, status->ack ? IEEE80211_RATECTL_TX_SUCCESS : IEEE80211_RATECTL_TX_FAILURE, &retrycnt, 0); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ break; } slot = bwn_dma_nextslot(dr, slot); @@ -9041,11 +9046,14 @@ } ni = tp->tp_ni; vap = ni->ni_vap; +#if 0 ieee80211_ratectl_tx_complete(vap, ni, status->ack ? IEEE80211_RATECTL_TX_SUCCESS : IEEE80211_RATECTL_TX_FAILURE, &retrycnt, 0); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ } bwn_pio_handle_txeof(mac, status); } Index: dev/usb/wlan/if_zyd.c =================================================================== --- dev/usb/wlan/if_zyd.c (revision 254826) +++ dev/usb/wlan/if_zyd.c (working copy) @@ -496,7 +496,8 @@ zvp->newstate = vap->iv_newstate; vap->iv_newstate = zyd_newstate; - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ @@ -664,12 +665,15 @@ */ ni = ieee80211_find_txnode(vap, retry->macaddr); if (ni != NULL) { - int retrycnt = - (int)(le16toh(retry->count) & 0xff); + /*int retrycnt = + (int)(le16toh(retry->count) & 0xff);*/ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ ieee80211_free_node(ni); } if (le16toh(retry->count) & 0x100) Index: dev/usb/wlan/if_rum.c =================================================================== --- dev/usb/wlan/if_rum.c (revision 254826) +++ dev/usb/wlan/if_rum.c (working copy) @@ -613,7 +613,8 @@ usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0); TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp); - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); Index: dev/usb/wlan/if_ural.c =================================================================== --- dev/usb/wlan/if_ural.c (revision 254826) +++ dev/usb/wlan/if_ural.c (working copy) @@ -598,7 +598,8 @@ usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0); TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp); - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ Index: dev/usb/wlan/if_run.c =================================================================== --- dev/usb/wlan/if_run.c (revision 254826) +++ dev/usb/wlan/if_run.c (working copy) @@ -820,7 +820,8 @@ rvp->newstate = vap->iv_newstate; vap->iv_newstate = run_newstate; - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ Index: dev/wpi/if_wpi.c =================================================================== --- dev/wpi/if_wpi.c (revision 254826) +++ dev/wpi/if_wpi.c (working copy) @@ -782,7 +782,8 @@ wvp->newstate = vap->iv_newstate; vap->iv_newstate = wpi_newstate; - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; @@ -1596,8 +1597,11 @@ DPRINTFN(WPI_DEBUG_TX, ("%d retries\n", stat->ntries)); retrycnt = 1; } +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); +#endif + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ /* XXX oerrors should only count errors !maxtries */ if ((le32toh(stat->status) & 0xff) != 1) Index: dev/ral/rt2560.c =================================================================== --- dev/ral/rt2560.c (revision 254826) +++ dev/ral/rt2560.c (working copy) @@ -426,7 +426,8 @@ vap->iv_newstate = rt2560_newstate; vap->iv_update_beacon = rt2560_beacon_update; - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); if (TAILQ_FIRST(&ic->ic_vaps) == vap) @@ -955,9 +956,13 @@ DPRINTFN(sc, 10, "%s\n", "data frame sent successfully"); if (data->rix != IEEE80211_FIXED_RATE_NONE) + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); +#endif + ifp->if_opackets++; break; @@ -967,9 +972,12 @@ DPRINTFN(sc, 9, "data frame sent after %u retries\n", retrycnt); if (data->rix != IEEE80211_FIXED_RATE_NONE) + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); +#endif ifp->if_opackets++; break; @@ -979,9 +987,12 @@ DPRINTFN(sc, 9, "data frame failed after %d retries\n", retrycnt); if (data->rix != IEEE80211_FIXED_RATE_NONE) + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); +#endif ifp->if_oerrors++; break; Index: dev/ral/rt2661.c =================================================================== --- dev/ral/rt2661.c (revision 254826) +++ dev/ral/rt2661.c (working copy) @@ -423,7 +423,8 @@ vap->iv_update_beacon = rt2661_beacon_update; #endif - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); if (TAILQ_FIRST(&ic->ic_vaps) == vap) @@ -905,9 +906,12 @@ DPRINTFN(sc, 10, "data frame sent successfully after " "%d retries\n", retrycnt); if (data->rix != IEEE80211_FIXED_RATE_NONE) + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); +#endif ifp->if_opackets++; break; @@ -917,9 +921,12 @@ DPRINTFN(sc, 9, "%s\n", "sending data frame failed (too much retries)"); if (data->rix != IEEE80211_FIXED_RATE_NONE) + ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); +#endif ifp->if_oerrors++; break; Index: dev/ral/rt2860.c =================================================================== --- dev/ral/rt2860.c (revision 254826) +++ dev/ral/rt2860.c (working copy) @@ -484,7 +484,8 @@ /* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */ vap->iv_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX); - ieee80211_ratectl_init(vap); + /* XXX TODO fill the cap */ + ieee80211_ratectl_init(vap, 0); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); if (TAILQ_FIRST(&ic->ic_vaps) == vap) @@ -1121,11 +1122,17 @@ retrycnt = 1; else retrycnt = 0; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); +#endif } else { + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */ +#if 0 ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); +#endif ifp->if_oerrors++; } } Index: dev/ath/if_ath_sysctl.c =================================================================== --- dev/ath/if_ath_sysctl.c (revision 254826) +++ dev/ath/if_ath_sysctl.c (working copy) @@ -71,6 +71,7 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_ratectl.h> #ifdef IEEE80211_SUPPORT_SUPERG #include <net80211/ieee80211_superg.h> #endif @@ -602,6 +603,28 @@ } #endif /* ATH_DEBUG_ALQ */ +static int +ath_sysctl_ratectl_stats(SYSCTL_HANDLER_ARGS) +{ + struct ath_softc *sc = arg1; + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211vap *vap; + int error, v; + + v = 0; + error = sysctl_handle_int(oidp, &v, 0, req); + if (error || !req->newptr) + return error; + + printf("\n[%s]: per-device net80211 ratectl statistics\n", + ic->ic_ifp->if_xname); + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ieee80211_ratectl_stats(vap); + } + + return 0; +} + void ath_sysctlattach(struct ath_softc *sc) { @@ -792,6 +815,10 @@ #ifdef ATH_DEBUG_ALQ ath_sysctl_alq_attach(sc); #endif + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "ratectl_stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + ath_sysctl_ratectl_stats, "I", "per-device net80211 ratectl statistics"); } static int Index: dev/ath/if_ath_tx_ht.c =================================================================== --- dev/ath/if_ath_tx_ht.c (revision 254826) +++ dev/ath/if_ath_tx_ht.c (working copy) @@ -64,6 +64,8 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> + +#include <net80211/ieee80211_ratectl.h> #ifdef IEEE80211_SUPPORT_SUPERG #include <net80211/ieee80211_superg.h> #endif @@ -221,13 +223,47 @@ void ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf) { - struct ieee80211_node *ni = bf->bf_node; - struct ieee80211com *ic = ni->ni_ic; + // struct ieee80211_node *ni = bf->bf_node; + // struct ieee80211com *ic = ni->ni_ic; const HAL_RATE_TABLE *rt = sc->sc_currates; - struct ath_rc_series *rc = bf->bf_state.bfs_rc; + struct ath_rc_series *ath_rc = bf->bf_state.bfs_rc; + struct ieee80211_rc_info *rc_info = NULL; + struct ieee80211_rc_series *rc = NULL; + struct m_tag *mtag; uint8_t rate; int i; + mtag = m_tag_locate(bf->bf_m, MTAG_ABI_NET80211, + NET80211_TAG_RATECTL, NULL); + if (NULL == mtag) + return; + + rc_info = (struct ieee80211_rc_info*)(mtag + 1); + rc = rc_info->iri_rc; + + for (i = 0; i < IEEE80211_RATECTL_NUM; i++) { + if (rc[i].tries == 0) + continue; + + rate = rt->info[rc[i].rix].rateCode; + if (rc[i].flags & IEEE80211_RATECTL_FLAG_SP) + rate |= rt->info[rc[i].rix].shortPreamble; + + rc[i].ratecode = rate; + + ath_rc[i].rix = rc[i].rix; + ath_rc[i].tries = rc[i].tries; + ath_rc[i].flags = rc[i].flags; + ath_rc[i].ratecode = rc[i].ratecode; + ath_rc[i].tx_power_cap = rc[i].tx_power_cap; + ath_rc[i].max4msframelen = rc[i].max4msframelen; + + DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, + "%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n", + __func__, i, rate, rc[i].flags, rc[i].max4msframelen); + } + +#if 0 for (i = 0; i < ATH_RC_NUM; i++) { rc[i].flags = 0; if (rc[i].tries == 0) @@ -325,6 +361,7 @@ "%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n", __func__, i, rate, rc[i].flags, rc[i].max4msframelen); } +#endif } /* Index: dev/ath/if_ath_tx.c =================================================================== --- dev/ath/if_ath_tx.c (revision 254826) +++ dev/ath/if_ath_tx.c (working copy) @@ -72,6 +72,8 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> + +#include <net80211/ieee80211_ratectl.h> #ifdef IEEE80211_SUPPORT_SUPERG #include <net80211/ieee80211_superg.h> #endif @@ -1387,6 +1389,9 @@ static void ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf) { + struct ieee80211_node *ni = bf->bf_node; + struct ieee80211_rc_info *rc_info = NULL; + struct ieee80211_rc_series *rc = NULL; uint8_t rate, rix; int try0; @@ -1395,7 +1400,7 @@ /* Get rid of any previous state */ bzero(bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); - +#if 0 ATH_NODE_LOCK(ATH_NODE(bf->bf_node)); ath_rate_findrate(sc, ATH_NODE(bf->bf_node), bf->bf_state.bfs_shpream, bf->bf_state.bfs_pktlen, &rix, &try0, &rate); @@ -1409,7 +1414,25 @@ ath_rate_getxtxrates(sc, ATH_NODE(bf->bf_node), rix, bf->bf_state.bfs_rc); ATH_NODE_UNLOCK(ATH_NODE(bf->bf_node)); +#endif + /* net80211 ratectl */ + rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m); + rc = rc_info->iri_rc; + + rc_info->iri_flags = 0; + if (bf->bf_state.bfs_shpream) + rc_info->iri_flags |= IEEE80211_RATECTL_INFO_SP; + if (bf->bf_state.bfs_aggr) + rc_info->iri_flags |= IEEE80211_RATECTL_INFO_AGGR; + rc_info->iri_framelen = bf->bf_state.bfs_pktlen; + + ieee80211_ratectl_rates(ni, rc_info); + + rix = rc[0].rix; + try0 = rc[0].tries; + rate = ni->ni_txrate; + sc->sc_txrix = rix; /* for LED blinking */ sc->sc_lastdatarix = rix; /* for fast frames */ bf->bf_state.bfs_try0 = try0; @@ -1530,7 +1553,8 @@ * frames that must go out - eg management/raw frames. */ bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK; - + + bf->bf_state.bfs_aggr = 0; /* Setup the descriptor before handoff */ ath_tx_do_ratelookup(sc, bf); ath_tx_calc_duration(sc, bf); @@ -4058,6 +4082,7 @@ int tid = bf->bf_state.bfs_tid; struct ath_tid *atid = &an->an_tid[tid]; struct ath_tx_status *ts = &bf->bf_status.ds_txstat; + struct ieee80211_rc_info *rc_info = NULL; /* The TID state is protected behind the TXQ lock */ ATH_TX_LOCK(sc); @@ -4108,9 +4133,23 @@ * during a hw queue drain and the frame wanted an ACK. */ if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) + { +#if 0 ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, ts, bf->bf_state.bfs_pktlen, 1, (ts->ts_status == 0) ? 0 : 1); +#endif + + /* net80211 ratectl */ + rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m); + ieee80211_ratectl_rc_info_set(rc_info, + 1, (ts->ts_status == 0 ? 0 : 1), + bf->bf_state.bfs_pktlen, + ts->ts_shortretry, ts->ts_longretry, + ts->ts_finaltsi, ts->ts_rate); + rc_info->iri_flags &= ~IEEE80211_RATECTL_INFO_AGGR; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info); + } ath_tx_default_comp(sc, bf, fail); } @@ -4475,6 +4514,8 @@ int drops = 0; struct ieee80211_tx_ampdu *tap; ath_bufhead bf_cq; + struct ath_tx_status ts = bf_first->bf_status.ds_txstat; + struct ieee80211_rc_info *rc_info = NULL; TAILQ_INIT(&bf_q); TAILQ_INIT(&bf_cq); @@ -4485,10 +4526,23 @@ * XXX use the length in the first frame in the series; * XXX just so things are consistent for now. */ +#if 0 ath_tx_update_ratectrl(sc, ni, bf_first->bf_state.bfs_rc, &bf_first->bf_status.ds_txstat, bf_first->bf_state.bfs_pktlen, bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes); +#endif + + /* net80211 ratectl */ + rc_info = ieee80211_ratectl_rc_info_get(ni, bf_first->bf_m); + ieee80211_ratectl_rc_info_set(rc_info, + bf_first->bf_state.bfs_nframes, + bf_first->bf_state.bfs_nframes, + bf_first->bf_state.bfs_pktlen, + ts.ts_shortretry, ts.ts_longretry, + ts.ts_finaltsi, ts.ts_rate); + rc_info->iri_flags |= IEEE80211_RATECTL_INFO_AGGR; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info); ATH_TX_LOCK(sc); tap = ath_tx_get_tx_tid(an, tid->tid); @@ -4626,9 +4680,12 @@ int nframes = 0, nbad = 0, nf; int pktlen; /* XXX there's too much on the stack? */ +#if 0 struct ath_rc_series rc[ATH_RC_NUM]; +#endif int txseq; - + struct ieee80211_rc_info *rc_info = NULL; + struct mbuf *m; DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n", __func__, atid->hwq_depth); @@ -4748,7 +4805,7 @@ isaggr = bf_first->bf_state.bfs_aggr; ba[0] = ts.ts_ba_low; ba[1] = ts.ts_ba_high; - +#if 0 /* * Copy the TX completion status and the rate control * series from the first descriptor, as it may be freed @@ -4756,6 +4813,11 @@ * into things. */ memcpy(rc, bf_first->bf_state.bfs_rc, sizeof(rc)); +#endif + /* + * Get the mbuf here, as bf_first will be set to NULL later. + */ + m = bf_first->bf_m; DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: txa_start=%d, tx_ok=%d, status=%.8x, flags=%.8x, " @@ -4873,8 +4935,20 @@ * control code. */ if (fail == 0) + { +#if 0 ath_tx_update_ratectrl(sc, ni, rc, &ts, pktlen, nframes, nbad); +#endif + /* net80211 ratectl */ + rc_info = ieee80211_ratectl_rc_info_get(ni, m); + ieee80211_ratectl_rc_info_set(rc_info, + nframes, nbad, pktlen, + ts.ts_shortretry, ts.ts_longretry, + ts.ts_finaltsi, ts.ts_rate); + rc_info->iri_flags |= IEEE80211_RATECTL_INFO_AGGR; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info); + } /* * send bar if we dropped any frames @@ -4948,6 +5022,7 @@ struct ath_tid *atid = &an->an_tid[tid]; struct ath_tx_status ts; int drops = 0; + struct ieee80211_rc_info *rc_info = NULL; /* * Take a copy of this; filtering/cloning the frame may free the @@ -4962,11 +5037,22 @@ * Do it outside of the TXQ lock. */ if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) + { +#if 0 ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, &bf->bf_status.ds_txstat, bf->bf_state.bfs_pktlen, 1, (ts.ts_status == 0) ? 0 : 1); - +#endif + rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m); + ieee80211_ratectl_rc_info_set(rc_info, + 1, (ts.ts_status == 0 ? 0 : 1), + bf->bf_state.bfs_pktlen, + ts.ts_shortretry, ts.ts_longretry, + ts.ts_finaltsi, ts.ts_rate); + rc_info->iri_flags &= ~IEEE80211_RATECTL_INFO_AGGR; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info); + } /* * This is called early so atid->hwq_depth can be tracked. * This unfortunately means that it's released and regrabbed @@ -5183,6 +5269,15 @@ } /* + * If non-aggregate scenario appears, this will be + * turned off. + * + * This flag is used by ath_tx_do_ratelookup() to + * distinguish aggr/non-aggr scenario. + */ + bf->bf_state.bfs_aggr = 1; + + /* * If the packet doesn't fall within the BAW (eg a NULL * data frame), schedule it directly; continue. */ @@ -5425,6 +5520,7 @@ /* Update CLRDMASK just before this frame is queued */ ath_tx_update_clrdmask(sc, tid, bf); + bf->bf_state.bfs_aggr = 0; /* Program descriptors + rate control */ ath_tx_do_ratelookup(sc, bf); ath_tx_calc_duration(sc, bf); Index: dev/ath/if_ath.c =================================================================== --- dev/ath/if_ath.c (revision 254826) +++ dev/ath/if_ath.c (working copy) @@ -82,6 +82,8 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> + +#include <net80211/ieee80211_ratectl.h> #ifdef IEEE80211_SUPPORT_SUPERG #include <net80211/ieee80211_superg.h> #endif @@ -322,6 +324,7 @@ sc->sc_ah = ah; sc->sc_invalid = 0; /* ready to go, enable interrupt handling */ #ifdef ATH_DEBUG + ath_debug |= ATH_DEBUG_NODE; sc->sc_debug = ath_debug; #endif @@ -1380,6 +1383,16 @@ } ATH_UNLOCK(sc); + uint32_t caps = 0; + if (sc->sc_mrretry) + caps |= IEEE80211_RATECTL_CAP_MRR; + if (sc->sc_mrrprot) + caps |= IEEE80211_RATECTL_CAP_MRRPROT; + if (sc->sc_txchainmask > 1) + caps |= IEEE80211_RATECTL_CAP_MULTXCHAIN; + + ieee80211_ratectl_init(vap, caps); + /* complete setup */ ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status); return vap; @@ -1414,6 +1427,7 @@ ath_stoprecv(sc, 1); /* stop recv side */ } + ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); /* @@ -4042,6 +4056,7 @@ { struct ieee80211_node *ni = bf->bf_node; struct ath_node *an = NULL; + struct ieee80211_rc_info *rc_info = NULL; ATH_TX_UNLOCK_ASSERT(sc); ATH_TXQ_UNLOCK_ASSERT(txq); @@ -4068,10 +4083,21 @@ * XXX assume this isn't an aggregate * frame. */ +#if 0 ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc, ts, bf->bf_state.bfs_pktlen, 1, (ts->ts_status == 0 ? 0 : 1)); +#endif + /* net80211 ratectl */ + rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m); + ieee80211_ratectl_rc_info_set(rc_info, + 1, (ts->ts_status == 0 ? 0 : 1), + bf->bf_state.bfs_pktlen, + ts->ts_shortretry, ts->ts_longretry, + ts->ts_finaltsi, ts->ts_rate); + rc_info->iri_flags &= ~IEEE80211_RATECTL_INFO_AGGR; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info); } ath_tx_default_comp(sc, bf, 0); } else @@ -5574,9 +5600,15 @@ an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); - +#if 0 ath_rate_newassoc(sc, an, isnew); +#endif + DPRINTF(sc, ATH_DEBUG_NODE, "%s: newassoc is new: %d\n", + __func__, isnew); + if (isnew) + ieee80211_ratectl_node_init(ni); + if (isnew && (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFnsE3fnApV_=FNggEuhy1B=idXLxgymqqmJ=9XXrjspLoe%2BWA>
