summaryrefslogtreecommitdiff
path: root/sys/dev/pci/if_iwm.c
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2021-10-05 10:34:37 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2021-10-05 10:34:37 +0000
commitec82f06800d4087c7724085ba63f8f3b8483a5c4 (patch)
tree0c1282311d4c513f7f4ad37a7ddacaade388a0c8 /sys/dev/pci/if_iwm.c
parent2b7dc9ed5fea8e782131c7efed90fa9e28008f5f (diff)
Fix iwm(4) performance drop after roaming between APs in 11n mode.
Stop BA sessions directly in iwm_run_stop() and disable Tx agg queues when leaving RUN state. Otherwise Tx agg queues do not work properly after switching APs and Tx performance drops to about 2 Mbit/s with excessive retries being reported to RA. Tested: 7260: florian 8260: bket 8265: stsp 9260: florian 9560: stsp
Diffstat (limited to 'sys/dev/pci/if_iwm.c')
-rw-r--r--sys/dev/pci/if_iwm.c82
1 files changed, 70 insertions, 12 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 8bf1224f792..31e60bbfe4f 100644
--- a/sys/dev/pci/if_iwm.c
+++ b/sys/dev/pci/if_iwm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwm.c,v 1.370 2021/10/02 07:47:54 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.371 2021/10/05 10:34:36 stsp Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -307,6 +307,7 @@ int iwm_nic_init(struct iwm_softc *);
int iwm_enable_ac_txq(struct iwm_softc *, int, int);
int iwm_enable_txq(struct iwm_softc *, int, int, int, int, uint8_t,
uint16_t);
+int iwm_disable_txq(struct iwm_softc *, int, int, uint8_t);
int iwm_post_alive(struct iwm_softc *);
struct iwm_phy_db_entry *iwm_phy_db_get_section(struct iwm_softc *, uint16_t,
uint16_t);
@@ -2462,6 +2463,26 @@ iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo,
}
int
+iwm_disable_txq(struct iwm_softc *sc, int sta_id, int qid, uint8_t tid)
+{
+ struct iwm_scd_txq_cfg_cmd cmd;
+ int err;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.tid = tid;
+ cmd.scd_queue = qid;
+ cmd.enable = 0;
+ cmd.sta_id = sta_id;
+
+ err = iwm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd);
+ if (err)
+ return err;
+
+ sc->qenablemsk &= ~(1 << qid);
+ return 0;
+}
+
+int
iwm_post_alive(struct iwm_softc *sc)
{
int nwords;
@@ -3397,7 +3418,6 @@ iwm_sta_tx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t tid,
struct iwm_node *in = (void *)ni;
int qid = IWM_FIRST_AGG_TX_QUEUE + tid;
struct iwm_tx_ring *ring;
- struct ieee80211_tx_ba *ba;
enum ieee80211_edca_ac ac;
int fifo;
uint32_t status;
@@ -3417,7 +3437,6 @@ iwm_sta_tx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t tid,
}
ring = &sc->txq[qid];
- ba = &ni->ni_tx_ba[tid];
ac = iwm_tid_to_ac[tid];
fifo = iwm_ac_to_tx_fifo[ac];
@@ -3434,7 +3453,10 @@ iwm_sta_tx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t tid,
in->tfd_queue_msk |= (1 << qid);
} else {
in->tid_disable_ampdu |= (1 << tid);
- /* Queue remains enabled in the TFD queue mask. */
+ /*
+ * Queue remains enabled in the TFD queue mask
+ * until we leave RUN state.
+ */
err = iwm_flush_sta(sc, in);
if (err)
return err;
@@ -3503,8 +3525,7 @@ iwm_sta_tx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t tid,
* Clear pending frames but keep the queue enabled.
* Firmware panics if we disable the queue here.
*/
- iwm_txq_advance(sc, ring,
- IWM_AGG_SSN_TO_TXQ_IDX(ba->ba_winend));
+ iwm_txq_advance(sc, ring, ring->cur);
iwm_clear_oactive(sc, ring);
}
@@ -3520,6 +3541,13 @@ iwm_ba_task(void *arg)
int s = splnet();
int tid, err = 0;
+ if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) ||
+ ic->ic_state != IEEE80211_S_RUN) {
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
+
for (tid = 0; tid < IWM_MAX_TID_COUNT && !err; tid++) {
if (sc->sc_flags & IWM_FLAG_SHUTDOWN)
break;
@@ -8502,10 +8530,45 @@ iwm_run_stop(struct iwm_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct iwm_node *in = (void *)ic->ic_bss;
- int err;
+ struct ieee80211_node *ni = &in->in_ni;
+ int err, i, tid;
splassert(IPL_NET);
+ /*
+ * Stop Tx/Rx BA sessions now. We cannot rely on the BA task
+ * for this when moving out of RUN state since it runs in a
+ * separate thread.
+ * Note that in->in_ni (struct ieee80211_node) already represents
+ * our new access point in case we are roaming between APs.
+ * This means we cannot rely on struct ieee802111_node to tell
+ * us which BA sessions exist.
+ */
+ for (i = 0; i < nitems(sc->sc_rxba_data); i++) {
+ struct iwm_rxba_data *rxba = &sc->sc_rxba_data[i];
+ if (rxba->baid == IWM_RX_REORDER_DATA_INVALID_BAID)
+ continue;
+ err = iwm_sta_rx_agg(sc, ni, rxba->tid, 0, 0, 0, 0);
+ if (err)
+ return err;
+ iwm_clear_reorder_buffer(sc, rxba);
+ if (sc->sc_rx_ba_sessions > 0)
+ sc->sc_rx_ba_sessions--;
+ }
+ for (tid = 0; tid < IWM_MAX_TID_COUNT; tid++) {
+ int qid = IWM_FIRST_AGG_TX_QUEUE + tid;
+ if ((sc->tx_ba_queue_mask & (1 << qid)) == 0)
+ continue;
+ err = iwm_sta_tx_agg(sc, ni, tid, 0, 0, 0);
+ if (err)
+ return err;
+ err = iwm_disable_txq(sc, IWM_STATION_ID, qid, tid);
+ if (err)
+ return err;
+ in->tfd_queue_msk &= ~(1 << qid);
+ }
+ ieee80211_ba_del(ni);
+
if (ic->ic_opmode == IEEE80211_M_MONITOR)
iwm_led_blink_stop(sc);
@@ -8940,7 +9003,6 @@ iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
struct ifnet *ifp = IC2IFP(ic);
struct iwm_softc *sc = ifp->if_softc;
- int i;
/*
* Prevent attemps to transition towards the same state, unless
@@ -8954,10 +9016,6 @@ iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
timeout_del(&sc->sc_calib_to);
iwm_del_task(sc, systq, &sc->ba_task);
iwm_del_task(sc, systq, &sc->mac_ctxt_task);
- for (i = 0; i < nitems(sc->sc_rxba_data); i++) {
- struct iwm_rxba_data *rxba = &sc->sc_rxba_data[i];
- iwm_clear_reorder_buffer(sc, rxba);
- }
}
sc->ns_nstate = nstate;