diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2019-02-24 09:37:19 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2019-02-24 09:37:19 +0000 |
commit | 2ad65a5778f70e4236631e556d380acc7eb18cb5 (patch) | |
tree | d5577e10b8ffb025829c2780e7e215b3389c5580 | |
parent | b49ab31fedf8cfc2777c810196c3bb8ddc069564 (diff) |
Rework iwm(4) Tx rate selection.
Ask firmware to retry at a constant Tx rate instead of successively lower
rates. This provides better feedback to Tx rate scaling algorithms in our
kernel and thus increases Tx throughput, especially on clean channels.
Make the driver fall back to CCK rates on 2 GHz channels if 11n MCS
don't work. Previously, we were relying on firmware to do this for us.
Tested on 7260, 7265, 8260, 8265 by me, jcs@, tb@, solene@, Matthias Schmidt.
-rw-r--r-- | sys/dev/pci/if_iwm.c | 257 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmvar.h | 8 |
2 files changed, 82 insertions, 183 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index 45e4446d79d..7d7f0697a2b 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.234 2019/01/23 10:08:48 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.235 2019/02/24 09:37:18 stsp Exp $ */ /* * Copyright (c) 2014, 2016 genua gmbh <info@genua.de> @@ -217,6 +217,7 @@ const struct iwm_rate { #define IWM_RIDX_MAX (nitems(iwm_rates)-1) #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) +#define IWM_RVAL_IS_OFDM(_i_) ((_i_) >= 12 && (_i_) != 22) /* Convert an MCS index into an iwm_rates[] index. */ const int iwm_mcs2ridx[] = { @@ -368,6 +369,7 @@ void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *, int iwm_get_noise(const struct iwm_statistics_rx_non_phy *); void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); +void iwm_enable_ht_cck_fallback(struct iwm_softc *, struct iwm_node *); void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); void iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, @@ -447,7 +449,6 @@ int iwm_run(struct iwm_softc *); int iwm_run_stop(struct iwm_softc *); struct ieee80211_node *iwm_node_alloc(struct ieee80211com *); void iwm_calib_timeout(void *); -void iwm_setrates(struct iwm_node *); int iwm_media_change(struct ifnet *); void iwm_newstate_task(void *); int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); @@ -3574,6 +3575,37 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, } void +iwm_enable_ht_cck_fallback(struct iwm_softc *sc, struct iwm_node *in) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = &in->in_ni; + struct ieee80211_rateset *rs = &ni->ni_rates; + uint8_t rval = (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL); + uint8_t min_rval = ieee80211_min_basic_rate(ic); + int i; + + /* Are CCK frames forbidden in our BSS? */ + if (IWM_RVAL_IS_OFDM(min_rval)) + return; + + in->ht_force_cck = 1; + + ieee80211_mira_cancel_timeouts(&in->in_mn); + ieee80211_mira_node_init(&in->in_mn); + ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn); + + /* Choose initial CCK Tx rate. */ + ni->ni_txrate = 0; + for (i = 0; i < rs->rs_nrates; i++) { + rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL); + if (rval == min_rval) { + ni->ni_txrate = i; + break; + } + } +} + +void iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_node *in) { @@ -3590,12 +3622,18 @@ iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, status != IWM_TX_STATUS_DIRECT_DONE); /* Update rate control statistics. */ - if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) { + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 || in->ht_force_cck) { in->in_amn.amn_txcnt++; - if (tx_resp->failure_frame > 0) + if (in->ht_force_cck) { + /* + * We want to move back to OFDM quickly if possible. + * Only show actual Tx failures to AMRR, not retries. + */ + if (txfail) + in->in_amn.amn_retrycnt++; + } else if (tx_resp->failure_frame > 0) in->in_amn.amn_retrycnt++; } else if (ic->ic_fixed_mcs == -1) { - int omcs = ni->ni_txmcs; in->in_mn.frames += tx_resp->frame_count; in->in_mn.ampdu_size = le16toh(tx_resp->byte_cnt); in->in_mn.agglen = tx_resp->frame_count; @@ -3603,14 +3641,16 @@ iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, in->in_mn.retries += tx_resp->failure_frame; if (txfail) in->in_mn.txfail += tx_resp->frame_count; - if (ic->ic_state == IEEE80211_S_RUN) + if (ic->ic_state == IEEE80211_S_RUN && !in->ht_force_cck) { + int otxmcs = ni->ni_txmcs; + ieee80211_mira_choose(&in->in_mn, ic, &in->in_ni); - /* - * If MiRA has chosen a new TX rate we must update - * the firwmare's LQ rate table from process context. - */ - if (omcs != ni->ni_txmcs) - iwm_add_task(sc, systq, &sc->setrates_task); + + /* Fall back to CCK rates if MCS 0 is failing. */ + if (txfail && IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) && + otxmcs == 0 && ni->ni_txmcs == 0) + iwm_enable_ht_cck_fallback(sc, in); + } } if (txfail) @@ -4112,41 +4152,32 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni = &in->in_ni; + struct ieee80211_rateset *rs = &ni->ni_rates; const struct iwm_rate *rinfo; int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - int ridx, rate_flags, i; - int nrates = ni->ni_rates.rs_nrates; + int min_ridx = iwm_rval2ridx(ieee80211_min_basic_rate(ic)); + int ridx, rate_flags; tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; - tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; + tx->data_retry_limit = IWM_LOW_RETRY_LIMIT; if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA) { /* for non-data, use the lowest supported rate */ - ridx = iwm_rval2ridx(ieee80211_min_basic_rate(ic)); + ridx = min_ridx; tx->data_retry_limit = IWM_MGMT_DFAULT_RETRY_LIMIT; } else if (ic->ic_fixed_mcs != -1) { ridx = sc->sc_fixed_ridx; } else if (ic->ic_fixed_rate != -1) { ridx = sc->sc_fixed_ridx; + } else if ((ni->ni_flags & IEEE80211_NODE_HT) && !in->ht_force_cck) { + ridx = iwm_mcs2ridx[ni->ni_txmcs]; } else { - /* for data frames, use RS table */ - tx->initial_rate_index = 0; - tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); - if (ni->ni_flags & IEEE80211_NODE_HT) { - ridx = iwm_mcs2ridx[ni->ni_txmcs]; - return &iwm_rates[ridx]; - } - ridx = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ? - IWM_RIDX_OFDM : IWM_RIDX_CCK; - for (i = 0; i < nrates; i++) { - if (iwm_rates[i].rate == (ni->ni_txrate & - IEEE80211_RATE_VAL)) { - ridx = i; - break; - } - } - return &iwm_rates[ridx]; + uint8_t rval; + rval = (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL); + ridx = iwm_rval2ridx(rval); + if (ridx < min_ridx) + ridx = min_ridx; } rinfo = &iwm_rates[ridx]; @@ -4228,7 +4259,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) if ((ni->ni_flags & IEEE80211_NODE_HT) && !IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA && - rinfo->plcp == IWM_RATE_INVM_PLCP) { + rinfo->ht_plcp != IWM_RATE_HT_SISO_MCS_INV_PLCP) { tap->wt_rate = (0x80 | rinfo->ht_plcp); } else tap->wt_rate = rinfo->rate; @@ -5172,6 +5203,8 @@ iwm_rval2ridx(int rval) int ridx; for (ridx = 0; ridx < nitems(iwm_rates); ridx++) { + if (iwm_rates[ridx].plcp == IWM_RATE_INVM_PLCP) + continue; if (rval == iwm_rates[ridx].rate) break; } @@ -5837,7 +5870,6 @@ iwm_run(struct iwm_softc *sc) /* Start at lowest available bit-rate, AMRR will raise. */ in->in_ni.ni_txrate = 0; in->in_ni.ni_txmcs = 0; - iwm_setrates(in); timeout_add_msec(&sc->sc_calib_to, 500); iwm_led_enable(sc); @@ -5900,157 +5932,25 @@ iwm_calib_timeout(void *arg) struct ieee80211com *ic = &sc->sc_ic; struct iwm_node *in = (void *)ic->ic_bss; struct ieee80211_node *ni = &in->in_ni; - int s, otxrate; + int s; s = splnet(); if ((ic->ic_fixed_rate == -1 || ic->ic_fixed_mcs == -1) && - ((ni->ni_flags & IEEE80211_NODE_HT) == 0) && + ((ni->ni_flags & IEEE80211_NODE_HT) == 0 || in->ht_force_cck) && ic->ic_opmode == IEEE80211_M_STA && ic->ic_bss) { - otxrate = ni->ni_txrate; ieee80211_amrr_choose(&sc->sc_amrr, &in->in_ni, &in->in_amn); - /* - * If AMRR has chosen a new TX rate we must update - * the firwmare's LQ rate table from process context. - */ - if (otxrate != ni->ni_txrate) - iwm_add_task(sc, systq, &sc->setrates_task); - } - splx(s); - - timeout_add_msec(&sc->sc_calib_to, 500); -} - -void -iwm_setrates_task(void *arg) -{ - struct iwm_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - struct iwm_node *in = (struct iwm_node *)ic->ic_bss; - int s = splnet(); - - if (sc->sc_flags & IWM_FLAG_SHUTDOWN) { - refcnt_rele_wake(&sc->task_refs); - splx(s); - return; - } - - /* Update rates table based on new TX rate determined by AMRR. */ - iwm_setrates(in); - refcnt_rele_wake(&sc->task_refs); - splx(s); -} - -void -iwm_setrates(struct iwm_node *in) -{ - struct ieee80211_node *ni = &in->in_ni; - struct ieee80211com *ic = ni->ni_ic; - struct iwm_softc *sc = IC2IFP(ic)->if_softc; - struct iwm_lq_cmd *lq = &in->in_lq; - struct ieee80211_rateset *rs = &ni->ni_rates; - int i, ridx, ridx_min, ridx_max, j, sgi_ok = 0, mimo, tab = 0; - struct iwm_host_cmd cmd = { - .id = IWM_LQ_CMD, - .len = { sizeof(in->in_lq), }, - }; - - memset(lq, 0, sizeof(*lq)); - lq->sta_id = IWM_STATION_ID; - - if (ic->ic_flags & IEEE80211_F_USEPROT) - lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; - - if ((ni->ni_flags & IEEE80211_NODE_HT) && - ieee80211_node_supports_ht_sgi20(ni)) { - ni->ni_flags |= IEEE80211_NODE_HT_SGI20; - sgi_ok = 1; - } - - /* - * Fill the LQ rate selection table with legacy and/or HT rates - * in descending order, i.e. with the node's current TX rate first. - * In cases where throughput of an HT rate corresponds to a legacy - * rate it makes no sense to add both. We rely on the fact that - * iwm_rates is laid out such that equivalent HT/legacy rates share - * the same IWM_RATE_*_INDEX value. Also, rates not applicable to - * legacy/HT are assumed to be marked with an 'invalid' PLCP value. - */ - j = 0; - ridx_min = iwm_rval2ridx(ieee80211_min_basic_rate(ic)); - mimo = iwm_is_mimo_mcs(ni->ni_txmcs); - ridx_max = (mimo ? IWM_RIDX_MAX : IWM_LAST_HT_SISO_RATE); - for (ridx = ridx_max; ridx >= ridx_min; ridx--) { - uint8_t plcp = iwm_rates[ridx].plcp; - uint8_t ht_plcp = iwm_rates[ridx].ht_plcp; - - if (j >= nitems(lq->rs_table)) - break; - tab = 0; - if (ni->ni_flags & IEEE80211_NODE_HT) { - if (ht_plcp == IWM_RATE_HT_SISO_MCS_INV_PLCP) - continue; - /* Do not mix SISO and MIMO HT rates. */ - if ((mimo && !iwm_is_mimo_ht_plcp(ht_plcp)) || - (!mimo && iwm_is_mimo_ht_plcp(ht_plcp))) - continue; - for (i = ni->ni_txmcs; i >= 0; i--) { - if (isclr(ni->ni_rxmcs, i)) - continue; - if (ridx == iwm_mcs2ridx[i]) { - tab = ht_plcp; - tab |= IWM_RATE_MCS_HT_MSK; - if (sgi_ok) - tab |= IWM_RATE_MCS_SGI_MSK; - break; - } - } - } else if (plcp != IWM_RATE_INVM_PLCP) { - for (i = ni->ni_txrate; i >= 0; i--) { - if (iwm_rates[ridx].rate == (rs->rs_rates[i] & - IEEE80211_RATE_VAL)) { - tab = plcp; - break; - } - } + if (in->ht_force_cck) { + struct ieee80211_rateset *rs = &ni->ni_rates; + uint8_t rv; + rv = (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL); + if (IWM_RVAL_IS_OFDM(rv)) + in->ht_force_cck = 0; } - - if (tab == 0) - continue; - - if (iwm_is_mimo_ht_plcp(ht_plcp)) - tab |= IWM_RATE_MCS_ANT_AB_MSK; - else - tab |= IWM_RATE_MCS_ANT_A_MSK; - - if (IWM_RIDX_IS_CCK(ridx)) - tab |= IWM_RATE_MCS_CCK_MSK; - lq->rs_table[j++] = htole32(tab); - } - - lq->mimo_delim = (mimo ? j : 0); - - /* Fill the rest with the lowest possible rate */ - while (j < nitems(lq->rs_table)) { - tab = iwm_rates[ridx_min].plcp; - if (IWM_RIDX_IS_CCK(ridx_min)) - tab |= IWM_RATE_MCS_CCK_MSK; - tab |= IWM_RATE_MCS_ANT_A_MSK; - lq->rs_table[j++] = htole32(tab); } - lq->single_stream_ant_msk = IWM_ANT_A; - lq->dual_stream_ant_msk = IWM_ANT_AB; - - lq->agg_time_limit = htole16(4000); /* 4ms */ - lq->agg_disable_start_th = 3; -#ifdef notyet - lq->agg_frame_cnt_limit = 0x3f; -#else - lq->agg_frame_cnt_limit = 1; /* tx agg disabled */ -#endif + splx(s); - cmd.data[0] = &in->in_lq; - iwm_send_cmd(sc, &cmd); + timeout_add_msec(&sc->sc_calib_to, 500); } int @@ -6196,7 +6096,6 @@ iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) if (ic->ic_state == IEEE80211_S_RUN) { timeout_del(&sc->sc_calib_to); ieee80211_mira_cancel_timeouts(&in->in_mn); - iwm_del_task(sc, systq, &sc->setrates_task); iwm_del_task(sc, systq, &sc->ba_task); iwm_del_task(sc, systq, &sc->htprot_task); } @@ -6705,7 +6604,6 @@ iwm_stop(struct ifnet *ifp) /* Cancel scheduled tasks and let any stale tasks finish up. */ task_del(systq, &sc->init_task); iwm_del_task(sc, sc->sc_nswq, &sc->newstate_task); - iwm_del_task(sc, systq, &sc->setrates_task); iwm_del_task(sc, systq, &sc->ba_task); iwm_del_task(sc, systq, &sc->htprot_task); KASSERT(sc->task_refs.refs >= 1); @@ -7895,7 +7793,6 @@ iwm_attach(struct device *parent, struct device *self, void *aux) timeout_set(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc); task_set(&sc->init_task, iwm_init_task, sc); task_set(&sc->newstate_task, iwm_newstate_task, sc); - task_set(&sc->setrates_task, iwm_setrates_task, sc); task_set(&sc->ba_task, iwm_ba_task, sc); task_set(&sc->htprot_task, iwm_htprot_task, sc); diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h index 7e35820c7ce..e6593fbc840 100644 --- a/sys/dev/pci/if_iwmvar.h +++ b/sys/dev/pci/if_iwmvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmvar.h,v 1.37 2017/12/08 21:16:01 stsp Exp $ */ +/* $OpenBSD: if_iwmvar.h,v 1.38 2019/02/24 09:37:18 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -360,7 +360,6 @@ struct iwm_softc { struct task init_task; /* NB: not reference-counted */ struct refcnt task_refs; struct task newstate_task; - struct task setrates_task; enum ieee80211_state ns_nstate; int ns_arg; @@ -509,9 +508,12 @@ struct iwm_node { uint16_t in_id; uint16_t in_color; - struct iwm_lq_cmd in_lq; struct ieee80211_amrr_node in_amn; struct ieee80211_mira_node in_mn; + + /* Set in 11n mode if we don't receive ACKs for OFDM frames. */ + int ht_force_cck; + }; #define IWM_STATION_ID 0 #define IWM_AUX_STA_ID 1 |