diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/if_iwn.c | 309 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnvar.h | 11 |
2 files changed, 129 insertions, 191 deletions
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c index a7b7249d46e..afeb963ef62 100644 --- a/sys/dev/pci/if_iwn.c +++ b/sys/dev/pci/if_iwn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwn.c,v 1.244 2020/12/12 11:48:53 jan Exp $ */ +/* $OpenBSD: if_iwn.c,v 1.245 2021/03/12 16:27:27 stsp Exp $ */ /*- * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> @@ -55,7 +55,7 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_amrr.h> -#include <net80211/ieee80211_mira.h> +#include <net80211/ieee80211_ra.h> #include <net80211/ieee80211_radiotap.h> #include <net80211/ieee80211_priv.h> /* for SEQ_LT */ #undef DPRINTF /* defined in ieee80211_priv.h */ @@ -157,9 +157,11 @@ void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *, struct mbuf_list *); -void iwn_mira_choose(struct iwn_softc *, struct ieee80211_node *); +void iwn_ra_choose(struct iwn_softc *, struct ieee80211_node *); void iwn_ampdu_rate_control(struct iwn_softc *, struct ieee80211_node *, - struct iwn_tx_ring *, int, uint16_t, uint16_t); + struct iwn_tx_ring *, uint16_t, uint16_t); +void iwn_ht_single_rate_control(struct iwn_softc *, + struct ieee80211_node *, uint8_t, uint8_t, uint8_t, int); void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); void iwn5000_rx_calib_results(struct iwn_softc *, @@ -179,7 +181,7 @@ void iwn_tx_done_free_txdata(struct iwn_softc *, struct iwn_tx_data *); void iwn_clear_oactive(struct iwn_softc *, struct iwn_tx_ring *); void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, - uint8_t, uint8_t, int, int, uint16_t); + uint8_t, uint8_t, uint8_t, int, int, uint16_t); void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); void iwn_notif_intr(struct iwn_softc *); void iwn_wakeup_intr(struct iwn_softc *); @@ -1771,7 +1773,6 @@ iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) struct ifnet *ifp = &ic->ic_if; struct iwn_softc *sc = ifp->if_softc; struct ieee80211_node *ni = ic->ic_bss; - struct iwn_node *wn = (void *)ni; int error; if (ic->ic_state == IEEE80211_S_RUN) { @@ -1783,7 +1784,6 @@ iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) ieee80211_stop_ampdu_tx(ic, ni, -1); ieee80211_ba_del(ni); } - ieee80211_mira_cancel_timeouts(&wn->mn); timeout_del(&sc->calib_to); sc->calib.state = IWN_CALIB_STATE_INIT; if (sc->sc_flags & IWN_FLAG_BGSCAN) @@ -2229,104 +2229,98 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, } void -iwn_mira_choose(struct iwn_softc *sc, struct ieee80211_node *ni) +iwn_ra_choose(struct iwn_softc *sc, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct iwn_node *wn = (void *)ni; - int best_mcs = ieee80211_mira_get_best_mcs(&wn->mn); + int old_txmcs = ni->ni_txmcs; - ieee80211_mira_choose(&wn->mn, ic, ni); + ieee80211_ra_choose(&wn->rn, ic, ni); - /* - * Update firmware's LQ retry table if MiRA has chosen a new MCS. - * - * We only need to do this if the best MCS has changed because - * we ask firmware to use a fixed MCS while MiRA is probing a - * candidate MCS. - * While not probing we ask firmware to retry at lower rates in case - * Tx at the newly chosen best MCS ends up failing, and then report - * any resulting Tx retries to MiRA in order to trigger probing. - */ - if (best_mcs != ieee80211_mira_get_best_mcs(&wn->mn)) + /* Update firmware's LQ retry table if RA has chosen a new MCS. */ + if (ni->ni_txmcs != old_txmcs) iwn_set_link_quality(sc, ni); } void iwn_ampdu_rate_control(struct iwn_softc *sc, struct ieee80211_node *ni, - struct iwn_tx_ring *txq, int tid, uint16_t seq, uint16_t ssn) + struct iwn_tx_ring *txq, uint16_t seq, uint16_t ssn) { struct ieee80211com *ic = &sc->sc_ic; struct iwn_node *wn = (void *)ni; - struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; - int min_ampdu_id, max_ampdu_id, id; int idx, end_idx; - /* Determine the min/max IDs we assigned to AMPDUs in this range. */ + /* + * Update Tx rate statistics for A-MPDUs before firmware's BA window. + */ idx = IWN_AGG_SSN_TO_TXQ_IDX(seq); end_idx = IWN_AGG_SSN_TO_TXQ_IDX(ssn); - min_ampdu_id = txq->data[idx].ampdu_id; - max_ampdu_id = min_ampdu_id; while (idx != end_idx) { struct iwn_tx_data *txdata = &txq->data[idx]; - - if (txdata->m != NULL) { - if (min_ampdu_id > txdata->ampdu_id) - min_ampdu_id = txdata->ampdu_id; - if (max_ampdu_id < txdata->ampdu_id) - max_ampdu_id = txdata->ampdu_id; - } - - idx = (idx + 1) % IWN_TX_RING_COUNT; - } - - /* - * Update Tx rate statistics for A-MPDUs before firmware's BA window. - */ - for (id = min_ampdu_id; id <= max_ampdu_id; id++) { - int have_ack = 0, bit = 0; - idx = IWN_AGG_SSN_TO_TXQ_IDX(seq); - end_idx = IWN_AGG_SSN_TO_TXQ_IDX(ssn); - wn->mn.agglen = 0; - wn->mn.ampdu_size = 0; - while (idx != end_idx) { - struct iwn_tx_data *txdata = &txq->data[idx]; - uint16_t s = (seq + bit) & 0xfff; + if (txdata->m != NULL && txdata->ampdu_nframes > 1) { /* * We can assume that this subframe has been ACKed * because ACK failures come as single frames and * before failing an A-MPDU subframe the firmware * sends it as a single frame at least once. - * - * However, when this A-MPDU was transmitted we - * learned how many subframes it contained. - * So if firmware isn't reporting all subframes now - * we can deduce an ACK failure for missing frames. */ - if (txdata->m != NULL && txdata->ampdu_id == id && - txdata->ampdu_txmcs == ni->ni_txmcs && - txdata->ampdu_nframes > 0 && - (SEQ_LT(ba->ba_winend, s) || - (ba->ba_bitmap & (1 << bit)) == 0)) { - have_ack++; - wn->mn.frames = txdata->ampdu_nframes; - wn->mn.agglen = txdata->ampdu_nframes; - wn->mn.ampdu_size = txdata->ampdu_size; - if (txdata->retries > 1) - wn->mn.retries++; - if (!SEQ_LT(ba->ba_winend, s)) - ieee80211_output_ba_record_ack(ic, ni, - tid, s); - } + ieee80211_ra_add_stats_ht(&wn->rn, ic, ni, + txdata->ampdu_txmcs, 1, 0); - idx = (idx + 1) % IWN_TX_RING_COUNT; - bit++; + /* Report this frame only once. */ + txdata->ampdu_nframes = 0; } - if (have_ack > 0) { - wn->mn.txfail = wn->mn.frames - have_ack; - iwn_mira_choose(sc, ni); + idx = (idx + 1) % IWN_TX_RING_COUNT; + } + + iwn_ra_choose(sc, ni); +} + +void +iwn_ht_single_rate_control(struct iwn_softc *sc, struct ieee80211_node *ni, + uint8_t rate, uint8_t rflags, uint8_t ackfailcnt, int txfail) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct iwn_node *wn = (void *)ni; + int mcs = rate; + const struct ieee80211_ht_rateset *rs = + ieee80211_ra_get_ht_rateset(rate, + ieee80211_node_supports_ht_sgi20(ni)); + unsigned int retries = 0, i; + + /* + * Ignore Tx reports which don't match our last LQ command. + */ + if (rate != ni->ni_txmcs) { + if (++wn->lq_rate_mismatch > 15) { + /* Try to sync firmware with driver. */ + iwn_set_link_quality(sc, ni); + wn->lq_rate_mismatch = 0; } + return; + } + + wn->lq_rate_mismatch = 0; + + /* + * Firmware has attempted rates in this rate set in sequence. + * Retries at a basic rate are counted against the minimum MCS. + */ + for (i = 0; i < ackfailcnt; i++) { + if (mcs > rs->min_mcs) { + ieee80211_ra_add_stats_ht(&wn->rn, ic, ni, mcs, 1, 1); + mcs--; + } else + retries++; } + + if (txfail && ackfailcnt == 0) + ieee80211_ra_add_stats_ht(&wn->rn, ic, ni, mcs, 1, 1); + else + ieee80211_ra_add_stats_ht(&wn->rn, ic, ni, mcs, retries + 1, retries); + + iwn_ra_choose(sc, ni); } /* @@ -2391,8 +2385,7 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, /* Skip rate control if our Tx rate is fixed. */ if (ic->ic_fixed_mcs == -1) - iwn_ampdu_rate_control(sc, ni, txq, cba->tid, ba->ba_winstart, - ssn); + iwn_ampdu_rate_control(sc, ni, txq, ba->ba_winstart, ssn); /* * SSN corresponds to the first (perhaps not yet transmitted) frame @@ -2555,9 +2548,9 @@ iwn_ampdu_txq_advance(struct iwn_softc *sc, struct iwn_tx_ring *txq, int qid, /* * Handle A-MPDU Tx queue status report. * Tx failures come as single frames (perhaps out of order), and before failing - * an A-MPDU subframe the firmware transmits it as a single frame at least once - * and reports Tx success/failure here. Frames successfully transmitted in an - * A-MPDU are completed when a compressed block ack notification is received. + * an A-MPDU subframe the firmware transmits it as a single frame at least once. + * Frames successfully transmitted in an A-MPDU are completed when a compressed + * block ack notification is received. */ void iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_ring *txq, @@ -2569,7 +2562,6 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_ring *txq, int tid = desc->qid - sc->first_agg_txq; struct iwn_tx_data *txdata = &txq->data[desc->idx]; struct ieee80211_node *ni = txdata->ni; - struct iwn_node *wn = (void *)ni; int txfail = (status != IWN_TX_STATUS_SUCCESS && status != IWN_TX_STATUS_DIRECT_DONE); struct ieee80211_tx_ba *ba; @@ -2581,35 +2573,19 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_ring *txq, return; if (nframes > 1) { - int ampdu_id, have_ampdu_id = 0, ampdu_size = 0; int i; - /* Compute the size of this A-MPDU. */ - for (i = 0; i < nframes; i++) { - uint8_t qid = agg_status[i].qid; - uint8_t idx = agg_status[i].idx; - - if (qid != desc->qid) - continue; - - txdata = &txq->data[idx]; - if (txdata->ni == NULL) - continue; - - ampdu_size += txdata->totlen + IEEE80211_CRC_LEN; - } - - /* - * For each subframe collect Tx status, retries, and Tx rate. - * (The Tx rate is the same for all subframes in this batch.) + /* + * Collect information about this A-MPDU. */ for (i = 0; i < nframes; i++) { uint8_t qid = agg_status[i].qid; uint8_t idx = agg_status[i].idx; uint16_t txstatus = (le16toh(agg_status[i].status) & IWN_AGG_TX_STATUS_MASK); - uint16_t trycnt = (le16toh(agg_status[i].status) & - IWN_AGG_TX_TRY) >> IWN_AGG_TX_TRY_SHIFT; + + if (txstatus != IWN_AGG_TX_STATE_TRANSMITTED) + continue; if (qid != desc->qid) continue; @@ -2618,36 +2594,9 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_ring *txq, if (txdata->ni == NULL) continue; - if (rflags & IWN_RFLAG_MCS) - txdata->ampdu_txmcs = rate; - if (txstatus != IWN_AGG_TX_STATE_TRANSMITTED) - txdata->txfail++; - if (trycnt > 1) - txdata->retries++; - - /* - * Assign a common ID to all subframes of this A-MPDU. - * This ID will be used during Tx rate control to - * infer the ACK status of individual subframes. - */ - if (!have_ampdu_id) { - wn = (void *)txdata->ni; - ampdu_id = wn->next_ampdu_id++; - have_ampdu_id = 1; - } - txdata->ampdu_id = ampdu_id; - - /* - * We will also need to know the total number of - * subframes and the size of this A-MPDU. We store - * this redundantly on each subframe because firmware - * only reports acknowledged subframes via compressed - * block-ack notification. This way we will know what - * the total number of subframes and size were even if - * just one of these subframes gets acknowledged. - */ + /* The Tx rate was the same for all subframes. */ + txdata->ampdu_txmcs = rate; txdata->ampdu_nframes = nframes; - txdata->ampdu_size = ampdu_size; } return; } @@ -2664,18 +2613,24 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_ring *txq, /* * Skip rate control if our Tx rate is fixed. - * Don't report frames to MiRA which were sent at a different - * Tx rate than ni->ni_txmcs. */ - if (ic->ic_fixed_mcs == -1 && txdata->txmcs == ni->ni_txmcs) { - wn->mn.frames++; - wn->mn.agglen = 1; - wn->mn.ampdu_size = txdata->totlen + IEEE80211_CRC_LEN; - if (ackfailcnt > 0) - wn->mn.retries++; - if (txfail) - wn->mn.txfail++; - iwn_mira_choose(sc, ni); + if (ic->ic_fixed_mcs == -1) { + if (txdata->ampdu_nframes > 1) { + struct iwn_node *wn = (void *)ni; + /* + * This frame was once part of an A-MPDU. + * Report one failed A-MPDU Tx attempt. + * The firmware might have made several such + * attempts but we don't keep track of this. + */ + ieee80211_ra_add_stats_ht(&wn->rn, ic, ni, + txdata->ampdu_txmcs, 1, 1); + } + + /* Report the final single-frame Tx attempt. */ + if (rflags & IWN_RFLAG_MCS) + iwn_ht_single_rate_control(sc, ni, rate, rflags, + ackfailcnt, txfail); } if (txfail) @@ -2746,8 +2701,8 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int txfail = (status != IWN_TX_STATUS_SUCCESS && status != IWN_TX_STATUS_DIRECT_DONE); - iwn_tx_done(sc, desc, stat->ackfailcnt, stat->rate, txfail, - desc->qid, framelen); + iwn_tx_done(sc, desc, stat->ackfailcnt, stat->rate, + stat->rflags, txfail, desc->qid, framelen); } else { memcpy(&ssn, &stat->stat.status + stat->nframes, sizeof(ssn)); ssn = le32toh(ssn) & 0xfff; @@ -2794,8 +2749,8 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, /* Reset TX scheduler slot. */ iwn5000_reset_sched(sc, desc->qid, desc->idx); - iwn_tx_done(sc, desc, stat->ackfailcnt, stat->rate, txfail, - desc->qid, letoh16(stat->len)); + iwn_tx_done(sc, desc, stat->ackfailcnt, stat->rate, + stat->rflags, txfail, desc->qid, letoh16(stat->len)); } else { memcpy(&ssn, &stat->stat.status + stat->nframes, sizeof(ssn)); ssn = le32toh(ssn) & 0xfff; @@ -2818,11 +2773,8 @@ iwn_tx_done_free_txdata(struct iwn_softc *sc, struct iwn_tx_data *data) ieee80211_release_node(ic, data->ni); data->ni = NULL; data->totlen = 0; - data->retries = 0; - data->txfail = 0; - data->txmcs = 0; + data->ampdu_nframes = 0; data->ampdu_txmcs = 0; - data->txrate = 0; } void @@ -2846,7 +2798,8 @@ iwn_clear_oactive(struct iwn_softc *sc, struct iwn_tx_ring *ring) */ void iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, - uint8_t ackfailcnt, uint8_t rate, int txfail, int qid, uint16_t len) + uint8_t ackfailcnt, uint8_t rate, uint8_t rflags, int txfail, + int qid, uint16_t len) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; @@ -2859,23 +2812,26 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, if (data->ni->ni_flags & IEEE80211_NODE_HT) { if (ic->ic_state == IEEE80211_S_RUN && - ic->ic_fixed_mcs == -1 && - data->txmcs == data->ni->ni_txmcs) { - wn->mn.frames++; - wn->mn.ampdu_size = len; - wn->mn.agglen = 1; + ic->ic_fixed_mcs == -1 && (rflags & IWN_RFLAG_MCS)) { + iwn_ht_single_rate_control(sc, data->ni, rate, rflags, + ackfailcnt, txfail); + } + } else { + if (rate != data->ni->ni_txrate) { + if (++wn->lq_rate_mismatch > 15) { + /* Try to sync firmware with driver. */ + iwn_set_link_quality(sc, data->ni); + wn->lq_rate_mismatch = 0; + } + } else { + wn->lq_rate_mismatch = 0; + + wn->amn.amn_txcnt++; if (ackfailcnt > 0) - wn->mn.retries++; + wn->amn.amn_retrycnt++; if (txfail) - wn->mn.txfail++; - iwn_mira_choose(sc, data->ni); + wn->amn.amn_retrycnt++; } - } else if (data->txrate == data->ni->ni_txrate) { - wn->amn.amn_txcnt++; - if (ackfailcnt > 0) - wn->amn.amn_retrycnt++; - if (txfail) - wn->amn.amn_retrycnt++; } if (txfail) ifp->if_oerrors++; @@ -3528,13 +3484,8 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { - int rtsthres = ic->ic_rtsthreshold; - if (ni->ni_flags & IEEE80211_NODE_HT) - rtsthres = ieee80211_mira_get_rts_threshold(&wn->mn, - ic, ni, totlen + IEEE80211_CRC_LEN); - /* NB: Group frames are sent using CCK in 802.11b/g/n (2GHz). */ - if (totlen + IEEE80211_CRC_LEN > rtsthres) { + if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { flags |= IWN_TX_NEED_RTS; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ridx >= IWN_RIDX_OFDM6) { @@ -3601,15 +3552,9 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) } else tx->rflags = rinfo->flags; - /* - * Keep the Tx rate constant while mira is probing, or if this is - * an aggregation queue in which case a fixed Tx rate works around - * FIFO_UNDERRUN Tx errors. - */ - if (tx->id == sc->broadcast_id || ieee80211_mira_is_probing(&wn->mn) || - qid >= sc->first_agg_txq || - ic->ic_fixed_mcs != -1 || ic->ic_fixed_rate != -1) { - /* Group or management frame, or probing, or fixed Tx rate. */ + if (tx->id == sc->broadcast_id || ic->ic_fixed_mcs != -1 || + ic->ic_fixed_rate != -1) { + /* Group or management frame, or fixed Tx rate. */ tx->linkq = 0; /* XXX Alternate between antenna A and B? */ txant = IWN_LSB(sc->txchainmask); @@ -3680,8 +3625,6 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) data->m = m; data->ni = ni; - data->txmcs = ni->ni_txmcs; - data->txrate = ni->ni_txrate; data->ampdu_txmcs = ni->ni_txmcs; /* updated upon Tx interrupt */ DPRINTFN(4, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", @@ -5649,7 +5592,7 @@ iwn_run(struct iwn_softc *sc) sc->calib_cnt = 0; timeout_add_msec(&sc->calib_to, 500); - ieee80211_mira_node_init(&wn->mn); + ieee80211_ra_node_init(&wn->rn); /* Link LED always on while associated. */ iwn_set_led(sc, IWN_LED_LINK, 0, 1); diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h index 3af35055a6e..e1c3e196d28 100644 --- a/sys/dev/pci/if_iwnvar.h +++ b/sys/dev/pci/if_iwnvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwnvar.h,v 1.39 2020/10/11 07:05:28 mpi Exp $ */ +/* $OpenBSD: if_iwnvar.h,v 1.40 2021/03/12 16:27:28 stsp Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -65,16 +65,10 @@ struct iwn_tx_data { struct mbuf *m; struct ieee80211_node *ni; int totlen; - int retries; - int txfail; - int txmcs; - int txrate; /* A-MPDU subframes */ - int ampdu_id; int ampdu_txmcs; int ampdu_nframes; - int ampdu_size; }; struct iwn_tx_ring { @@ -108,11 +102,12 @@ struct iwn_rx_ring { struct iwn_node { struct ieee80211_node ni; /* must be the first */ struct ieee80211_amrr_node amn; - struct ieee80211_mira_node mn; + struct ieee80211_ra_node rn; uint16_t disable_tid; uint8_t id; uint8_t ridx[IEEE80211_RATE_MAXSIZE]; uint32_t next_ampdu_id; + int lq_rate_mismatch; }; struct iwn_calib_state { |