I have been using an rt2860 hostap for a few years and I have discovered
that AMRR does not work properly for this driver. The symptom is that
some stations get stuck at 1 Mbps and do not progress up to faster rates.

Unlike many drivers, rt2860 does not keep the ieee80211_amrr_node on its
rt2860_node. Instead it maintains an array of ieee80211_amrr_nodes on
rt2860_softc indexed by WCID.

This approach runs into trouble when a non-active node (e.g. when in
IEEE80211_STA_COLLECT) exists with the same WCID as an active node.
If during rt2860_updatestats() the non-active node is encountered prior
to the active node, ieee80211_amrr_choose() performs the rate adjustment
against the non-active node. The counters in the ieee80211_amrr_node
structure get reset and then when the active node does show up in the
iteration it doesn't get its rate adjusted.

The approach that the diff below takes is to move the ieee80211_amrr_node
onto the rt2860_node and maintain a WCID to rt2860_node mapping on
rt2860_softc. This improves consistency with other drivers such as athn(4),
bwi(4), iwn(4), rt2560 and others, which maintain the ieee80211_amrr_node
on the device node respectively.

The diff below also introduces dedicated timers for AMRR and for scan
instead of using the RT2860 GP interrupt, which also improves consistency
with the way other drivers manage AMRR.

Nathanael

Index: rt2860.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/rt2860.c,v
retrieving revision 1.90
diff -u -p -r1.90 rt2860.c
--- rt2860.c    13 Apr 2016 10:49:26 -0000      1.90
+++ rt2860.c    24 Apr 2016 04:02:37 -0000
@@ -84,8 +84,9 @@ void          rt2860_free_rx_ring(struct rt2860_
                    struct rt2860_rx_ring *);
 struct         ieee80211_node *rt2860_node_alloc(struct ieee80211com *);
 int            rt2860_media_change(struct ifnet *);
+void           rt2860_next_scan(void *);
 void           rt2860_iter_func(void *, struct ieee80211_node *);
-void           rt2860_updatestats(struct rt2860_softc *);
+void           rt2860_amrr_timeout(void *);
 void           rt2860_newassoc(struct ieee80211com *, struct ieee80211_node *,
                    int);
 void           rt2860_node_leave(struct ieee80211com *,
@@ -103,7 +104,6 @@ void                rt2860_drain_stats_fifo(struct rt2
 void           rt2860_tx_intr(struct rt2860_softc *, int);
 void           rt2860_rx_intr(struct rt2860_softc *);
 void           rt2860_tbtt_intr(struct rt2860_softc *);
-void           rt2860_gp_intr(struct rt2860_softc *);
 int            rt2860_tx(struct rt2860_softc *, struct mbuf *,
                    struct ieee80211_node *);
 void           rt2860_start(struct ifnet *);
@@ -127,7 +127,6 @@ int         rt3090_filter_calib(struct rt2860_s
                    uint8_t *);
 void           rt3090_rf_setup(struct rt2860_softc *);
 void           rt2860_set_leds(struct rt2860_softc *, uint16_t);
-void           rt2860_set_gp_timer(struct rt2860_softc *, int);
 void           rt2860_set_bssid(struct rt2860_softc *, const uint8_t *);
 void           rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *);
 void           rt2860_updateslot(struct ieee80211com *);
@@ -202,6 +201,8 @@ rt2860_attach(void *xsc, int id)
 
        sc->amrr.amrr_min_success_threshold =  1;
        sc->amrr.amrr_max_success_threshold = 15;
+       timeout_set(&sc->amrr_to, rt2860_amrr_timeout, sc);
+       timeout_set(&sc->scan_to, rt2860_next_scan, sc);
 
        /* wait for NIC to initialize */
        for (ntries = 0; ntries < 100; ntries++) {
@@ -376,6 +377,9 @@ rt2860_detach(void *xsc)
        struct ifnet *ifp = &sc->sc_ic.ic_if;
        int qid;
 
+       timeout_del(&sc->scan_to);
+       timeout_del(&sc->amrr_to);
+
        ieee80211_ifdetach(ifp);        /* free all nodes */
        if_detach(ifp);
 
@@ -768,20 +772,40 @@ rt2860_media_change(struct ifnet *ifp)
        return 0;
 }
 
+/*
+ * This function is called periodically during scanning to
+ * switch from one channel to another.
+ */
+void
+rt2860_next_scan(void *arg)
+{
+       struct rt2860_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       int s;
+
+       s = splnet();
+       if (ic->ic_state == IEEE80211_S_SCAN)
+               ieee80211_next_scan(&ic->ic_if);
+       splx(s);
+}
+
 void
 rt2860_iter_func(void *arg, struct ieee80211_node *ni)
 {
        struct rt2860_softc *sc = arg;
-       uint8_t wcid = ((struct rt2860_node *)ni)->wcid;
+       struct rt2860_node *rn = (struct rt2860_node *)ni;
 
-       ieee80211_amrr_choose(&sc->amrr, ni, &sc->amn[wcid]);
+       ieee80211_amrr_choose(&sc->amrr, ni, &rn->amn);
 }
 
 void
-rt2860_updatestats(struct rt2860_softc *sc)
+rt2860_amrr_timeout(void *arg)
 {
+       struct rt2860_softc *sc = arg;
        struct ieee80211com *ic = &sc->sc_ic;
+       int s;
 
+       s = splnet();
 #ifndef IEEE80211_STA_ONLY
        /*
         * In IBSS or HostAP modes (when the hardware sends beacons), the
@@ -809,6 +833,9 @@ rt2860_updatestats(struct rt2860_softc *
        else
                ieee80211_iterate_nodes(ic, rt2860_iter_func, sc);
 #endif
+       splx(s);
+
+       timeout_add_msec(&sc->amrr_to, 500);
 }
 
 void
@@ -824,6 +851,8 @@ rt2860_newassoc(struct ieee80211com *ic,
                /* only interested in true associations */
                wcid = rn->wcid = IEEE80211_AID(ni->ni_associd);
 
+               sc->node[wcid] = rn;
+
                /* init WCID table entry */
                RAL_WRITE_REGION_1(sc, RT2860_WCID_ENTRY(wcid),
                    ni->ni_macaddr, IEEE80211_ADDR_LEN);
@@ -831,7 +860,7 @@ rt2860_newassoc(struct ieee80211com *ic,
        DPRINTF(("new assoc isnew=%d addr=%s WCID=%d\n",
            isnew, ether_sprintf(ni->ni_macaddr), wcid));
 
-       ieee80211_amrr_node_init(&sc->amrr, &sc->amn[wcid]);
+       ieee80211_amrr_node_init(&sc->amrr, &rn->amn);
        /* start at lowest available bit-rate, AMRR will raise */
        ni->ni_txrate = 0;
 
@@ -869,6 +898,8 @@ rt2860_node_leave(struct ieee80211com *i
 
        /* clear Rx WCID search table entry */
        RAL_SET_REGION_4(sc, RT2860_WCID_ENTRY(wcid), 0, 2);
+
+       sc->node[wcid] = NULL;
 }
 #endif
 
@@ -909,6 +940,8 @@ rt2860_newstate(struct ieee80211com *ic,
        uint32_t tmp;
 
        ostate = ic->ic_state;
+       timeout_del(&sc->scan_to);
+       timeout_del(&sc->amrr_to);
 
        if (ostate == IEEE80211_S_RUN) {
                /* turn link LED off */
@@ -924,23 +957,19 @@ rt2860_newstate(struct ieee80211com *ic,
                            tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
                            RT2860_TBTT_TIMER_EN));
                }
-               rt2860_set_gp_timer(sc, 0);
                break;
 
        case IEEE80211_S_SCAN:
                rt2860_switch_chan(sc, ic->ic_bss->ni_chan);
-               if (ostate != IEEE80211_S_SCAN)
-                       rt2860_set_gp_timer(sc, 150);
+               timeout_add_msec(&sc->scan_to, 150);
                break;
 
        case IEEE80211_S_AUTH:
        case IEEE80211_S_ASSOC:
-               rt2860_set_gp_timer(sc, 0);
                rt2860_switch_chan(sc, ic->ic_bss->ni_chan);
                break;
 
        case IEEE80211_S_RUN:
-               rt2860_set_gp_timer(sc, 0);
                rt2860_switch_chan(sc, ic->ic_bss->ni_chan);
 
                if (ic->ic_opmode != IEEE80211_M_MONITOR) {
@@ -964,7 +993,8 @@ rt2860_newstate(struct ieee80211com *ic,
 
                if (ic->ic_opmode != IEEE80211_M_MONITOR) {
                        rt2860_enable_tsf_sync(sc);
-                       rt2860_set_gp_timer(sc, 500);
+                       if (ic->ic_fixed_rate == -1)
+                               timeout_add_msec(&sc->amrr_to, 500);
                }
 
                /* turn link LED on */
@@ -1102,6 +1132,7 @@ void
 rt2860_drain_stats_fifo(struct rt2860_softc *sc)
 {
        struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct rt2860_node *rn;
        struct ieee80211_amrr_node *amn;
        uint32_t stat;
        uint8_t wcid, mcs, pid;
@@ -1116,8 +1147,12 @@ rt2860_drain_stats_fifo(struct rt2860_so
                if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff)
                        continue;
 
+               rn = sc->node[wcid];
+               if (rn == NULL)
+                       continue;
+
                /* update per-STA AMRR stats */
-               amn = &sc->amn[wcid];
+               amn = &rn->amn;
                amn->amn_txcnt++;
                if (stat & RT2860_TXQ_OK) {
                        /*
@@ -1403,19 +1438,6 @@ rt2860_tbtt_intr(struct rt2860_softc *sc
        }
 }
 
-void
-rt2860_gp_intr(struct rt2860_softc *sc)
-{
-       struct ieee80211com *ic = &sc->sc_ic;
-
-       DPRINTFN(2, ("GP timeout state=%d\n", ic->ic_state));
-
-       if (ic->ic_state == IEEE80211_S_SCAN)
-               ieee80211_next_scan(&ic->ic_if);
-       else if (ic->ic_state == IEEE80211_S_RUN)
-               rt2860_updatestats(sc);
-}
-
 int
 rt2860_intr(void *arg)
 {
@@ -1464,9 +1486,6 @@ rt2860_intr(void *arg)
        if (r & RT2860_MAC_INT_3)       /* Auto wakeup */
                /* TBD wakeup */;
 
-       if (r & RT2860_MAC_INT_4)       /* GP timer */
-               rt2860_gp_intr(sc);
-
        return 1;
 }
 
@@ -2576,32 +2603,6 @@ rt2860_set_leds(struct rt2860_softc *sc,
            which | (sc->leds & 0x7f), 0);
 }
 
-/*
- * Hardware has a general-purpose programmable timer interrupt that can
- * periodically raise MAC_INT_4.
- */
-void
-rt2860_set_gp_timer(struct rt2860_softc *sc, int ms)
-{
-       uint32_t tmp;
-
-       /* disable GP timer before reprogramming it */
-       tmp = RAL_READ(sc, RT2860_INT_TIMER_EN);
-       RAL_WRITE(sc, RT2860_INT_TIMER_EN, tmp & ~RT2860_GP_TIMER_EN);
-
-       if (ms == 0)
-               return;
-
-       tmp = RAL_READ(sc, RT2860_INT_TIMER_CFG);
-       ms *= 16;       /* Unit: 64us */
-       tmp = (tmp & 0xffff) | ms << RT2860_GP_TIMER_SHIFT;
-       RAL_WRITE(sc, RT2860_INT_TIMER_CFG, tmp);
-
-       /* enable GP timer */
-       tmp = RAL_READ(sc, RT2860_INT_TIMER_EN);
-       RAL_WRITE(sc, RT2860_INT_TIMER_EN, tmp | RT2860_GP_TIMER_EN);
-}
-
 void
 rt2860_set_bssid(struct rt2860_softc *sc, const uint8_t *bssid)
 {
@@ -3581,9 +3582,6 @@ rt2860_stop(struct ifnet *ifp, int disab
 
        /* disable interrupts */
        RAL_WRITE(sc, RT2860_INT_MASK, 0);
-
-       /* disable GP timer */
-       rt2860_set_gp_timer(sc, 0);
 
        /* disable Rx */
        tmp = RAL_READ(sc, RT2860_MAC_SYS_CTRL);
Index: rt2860var.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/rt2860var.h,v
retrieving revision 1.23
diff -u -p -r1.23 rt2860var.h
--- rt2860var.h 21 Mar 2016 21:16:30 -0000      1.23
+++ rt2860var.h 24 Apr 2016 04:02:37 -0000
@@ -103,6 +103,7 @@ struct rt2860_node {
        uint8_t                 wcid;
        uint8_t                 ridx[IEEE80211_RATE_MAXSIZE];
        uint8_t                 ctl_ridx[IEEE80211_RATE_MAXSIZE];
+       struct ieee80211_amrr_node      amn;
 };
 
 struct rt2860_softc {
@@ -120,6 +121,9 @@ struct rt2860_softc {
        bus_space_tag_t                 sc_st;
        bus_space_handle_t              sc_sh;
 
+       struct timeout                  scan_to;
+       struct timeout                  amrr_to;
+
        uint16_t                        (*sc_srom_read)(struct rt2860_softc *,
                                            uint16_t);
 
@@ -183,7 +187,7 @@ struct rt2860_softc {
        uint32_t                        txpow40mhz_2ghz[5];
        uint32_t                        txpow40mhz_5ghz[5];
 
-       struct ieee80211_amrr_node      amn[RT2860_WCID_MAX + 1];
+       struct rt2860_node*             node[RT2860_WCID_MAX + 1];
 
 #if NBPFILTER > 0
        caddr_t                         sc_drvbpf;

Reply via email to