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;