if_iwm - Update firmware rs table, instead of indexing the table in tx cmds.

* Rather than providing a non-zero index into the firmware RS table,
we should always use index 0 and update the firmware RS table whenever
our chosen tx rate for data-frames changes.

* Send IWM_LQ_CMD updates when the tx rate gets updated by the net80211
rate control (which is after we tell the tx status to the net80211
rate-control in iwm_mvm_rx_tx_cmd_single()).

* Disregard frames transferred with a different tx rate than the currently
selected rate for the rate-control calculations. This way we avoid
counting management frames (which are sent at a slow, and fixed rate),
as well as frames we added to the tx queue just before a new IWM_LQ_CMD
update took effect.

Submitted by:	Augustin Cavalier <waddlesplash@gmail.com> (Haiku)
Obtained from:	DragonFlyBSD (5d6b465e288ac5b52d7115688d4e6516acbbea1c)
This commit is contained in:
Kyle Evans 2019-01-24 03:41:09 +00:00
parent 2de16a737f
commit 95d69da4e0
2 changed files with 79 additions and 94 deletions

View File

@ -353,7 +353,9 @@ static int iwm_release(struct iwm_softc *, struct iwm_node *);
static struct ieee80211_node *
iwm_node_alloc(struct ieee80211vap *,
const uint8_t[IEEE80211_ADDR_LEN]);
static void iwm_setrates(struct iwm_softc *, struct iwm_node *);
static uint8_t iwm_rate_from_ucode_rate(uint32_t);
static int iwm_rate2ridx(struct iwm_softc *, uint8_t);
static void iwm_setrates(struct iwm_softc *, struct iwm_node *, int);
static int iwm_media_change(struct ifnet *);
static int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static void iwm_endscan_cb(void *, int);
@ -3317,7 +3319,11 @@ iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data;
struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs;
struct ieee80211_node *ni = &in->in_ni;
struct ieee80211vap *vap = ni->ni_vap;
int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK;
int new_rate, cur_rate = vap->iv_bss->ni_txrate;
boolean_t rate_matched;
uint8_t tx_resp_rate;
KASSERT(tx_resp->frame_count == 1, ("too many frames"));
@ -3333,6 +3339,17 @@ iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
le32toh(tx_resp->initial_rate),
(int) le16toh(tx_resp->wireless_media_time));
tx_resp_rate = iwm_rate_from_ucode_rate(le32toh(tx_resp->initial_rate));
/* For rate control, ignore frames sent at different initial rate */
rate_matched = (tx_resp_rate != 0 && tx_resp_rate == cur_rate);
if (tx_resp_rate != 0 && cur_rate != 0 && !rate_matched) {
IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
"tx_resp_rate doesn't match ni_txrate (tx_resp_rate=%u "
"ni_txrate=%d)\n", tx_resp_rate, cur_rate);
}
txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY |
IEEE80211_RATECTL_STATUS_LONG_RETRY;
txs->short_retries = tx_resp->failure_rts;
@ -3356,7 +3373,18 @@ iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
} else {
txs->status = IEEE80211_RATECTL_TX_SUCCESS;
}
ieee80211_ratectl_tx_complete(ni, txs);
if (rate_matched) {
ieee80211_ratectl_tx_complete(ni, txs);
int rix = ieee80211_ratectl_rate(vap->iv_bss, NULL, 0);
new_rate = vap->iv_bss->ni_txrate;
if (new_rate != 0 && new_rate != cur_rate) {
struct iwm_node *in = IWM_NODE(vap->iv_bss);
iwm_setrates(sc, in, rix);
iwm_mvm_send_lq_cmd(sc, &in->in_lq, FALSE);
}
}
return (txs->status != IEEE80211_RATECTL_TX_SUCCESS);
}
@ -3482,37 +3510,6 @@ iwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id,
}
#endif
/*
* Take an 802.11 (non-n) rate, find the relevant rate
* table entry. return the index into in_ridx[].
*
* The caller then uses that index back into in_ridx
* to figure out the rate index programmed /into/
* the firmware for this given node.
*/
static int
iwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in,
uint8_t rate)
{
int i;
uint8_t r;
for (i = 0; i < nitems(in->in_ridx); i++) {
r = iwm_rates[in->in_ridx[i]].rate;
if (rate == r)
return (i);
}
IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
"%s: couldn't find an entry for rate=%d\n",
__func__,
rate);
/* XXX Return the first */
/* XXX TODO: have it return the /lowest/ */
return (0);
}
static int
iwm_tx_rateidx_global_lookup(struct iwm_softc *sc, uint8_t rate)
{
@ -3565,22 +3562,15 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in,
IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
"%s: FIXED_RATE (%d)\n", __func__, tp->ucastrate);
} else {
int i;
/* for data frames, use RS table */
IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DATA\n", __func__);
/* XXX pass pktlen */
(void) ieee80211_ratectl_rate(ni, NULL, 0);
i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate);
ridx = in->in_ridx[i];
ridx = iwm_rate2ridx(sc, ni->ni_txrate);
if (ridx == -1)
ridx = 0;
/* This is the index into the programmed table */
tx->initial_rate_index = i;
tx->initial_rate_index = 0;
tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE);
IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
"%s: start with i=%d, txrate %d\n",
__func__, i, iwm_rates[ridx].rate);
}
IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE,
@ -4165,6 +4155,19 @@ iwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
M_NOWAIT | M_ZERO);
}
static uint8_t
iwm_rate_from_ucode_rate(uint32_t rate_n_flags)
{
uint8_t plcp = rate_n_flags & 0xff;
int i;
for (i = 0; i <= IWM_RIDX_MAX; i++) {
if (iwm_rates[i].plcp == plcp)
return iwm_rates[i].rate;
}
return 0;
}
uint8_t
iwm_ridx2rate(struct ieee80211_rateset *rs, int ridx)
{
@ -4180,15 +4183,36 @@ iwm_ridx2rate(struct ieee80211_rateset *rs, int ridx)
return 0;
}
static int
iwm_rate2ridx(struct iwm_softc *sc, uint8_t rate)
{
int i;
for (i = 0; i <= IWM_RIDX_MAX; i++) {
if (iwm_rates[i].rate == rate)
return i;
}
device_printf(sc->sc_dev,
"%s: WARNING: device rate for %u not found!\n",
__func__, rate);
return -1;
}
static void
iwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
iwm_setrates(struct iwm_softc *sc, struct iwm_node *in, int rix)
{
struct ieee80211_node *ni = &in->in_ni;
struct iwm_lq_cmd *lq = &in->in_lq;
int nrates = ni->ni_rates.rs_nrates;
struct ieee80211_rateset *rs = &ni->ni_rates;
int nrates = rs->rs_nrates;
int i, ridx, tab = 0;
// int txant = 0;
KASSERT(rix >= 0 && rix < nrates, ("invalid rix"));
if (nrates > nitems(lq->rs_table)) {
device_printf(sc->sc_dev,
"%s: node supports %d rates, driver handles "
@ -4200,45 +4224,11 @@ iwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
"%s: node supports 0 rates, odd!\n", __func__);
return;
}
nrates = imin(rix + 1, nrates);
/*
* XXX .. and most of iwm_node is not initialised explicitly;
* it's all just 0x0 passed to the firmware.
*/
/* first figure out which rates we should support */
/* XXX TODO: this isn't 11n aware /at all/ */
memset(&in->in_ridx, -1, sizeof(in->in_ridx));
IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
"%s: nrates=%d\n", __func__, nrates);
/*
* Loop over nrates and populate in_ridx from the highest
* rate to the lowest rate. Remember, in_ridx[] has
* IEEE80211_RATE_MAXSIZE entries!
*/
for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) {
int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL;
/* Map 802.11 rate to HW rate index. */
for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++)
if (iwm_rates[ridx].rate == rate)
break;
if (ridx > IWM_RIDX_MAX) {
device_printf(sc->sc_dev,
"%s: WARNING: device rate for %d not found!\n",
__func__, rate);
} else {
IWM_DPRINTF(sc, IWM_DEBUG_TXRATE,
"%s: rate: i: %d, rate=%d, ridx=%d\n",
__func__,
i,
rate,
ridx);
in->in_ridx[i] = ridx;
}
}
/* then construct a lq_cmd based on those */
memset(lq, 0, sizeof(*lq));
lq->sta_id = IWM_STATION_ID;
@ -4262,13 +4252,15 @@ iwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
* Note that we add the rates in the highest rate first
* (opposite of ni_rates).
*/
/*
* XXX TODO: this should be looping over the min of nrates
* and LQ_MAX_RETRY_NUM. Sigh.
*/
for (i = 0; i < nrates; i++) {
int rate = rs->rs_rates[rix - i] & IEEE80211_RATE_VAL;
int nextant;
/* Map 802.11 rate to HW rate index. */
ridx = iwm_rate2ridx(sc, rate);
if (ridx == -1)
continue;
#if 0
if (txant == 0)
txant = iwm_mvm_get_valid_tx_ant(sc);
@ -4277,12 +4269,6 @@ iwm_setrates(struct iwm_softc *sc, struct iwm_node *in)
#else
nextant = iwm_mvm_get_valid_tx_ant(sc);
#endif
/*
* Map the rate id into a rate index into
* our hardware table containing the
* configuration to use for this rate.
*/
ridx = in->in_ridx[i];
tab = iwm_rates[ridx].plcp;
tab |= nextant << IWM_RATE_MCS_ANT_POS;
if (IWM_RIDX_IS_CCK(ridx))
@ -4474,7 +4460,8 @@ iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
iwm_mvm_enable_beacon_filter(sc, ivp);
iwm_mvm_power_update_mac(sc);
iwm_mvm_update_quotas(sc, ivp);
iwm_setrates(sc, in);
int rix = ieee80211_ratectl_rate(&in->in_ni, NULL, 0);
iwm_setrates(sc, in, rix);
if ((error = iwm_mvm_send_lq_cmd(sc, &in->in_lq, TRUE)) != 0) {
device_printf(sc->sc_dev,

View File

@ -404,8 +404,6 @@ struct iwm_node {
int in_assoc;
struct iwm_lq_cmd in_lq;
uint8_t in_ridx[IEEE80211_RATE_MAXSIZE];
};
#define IWM_NODE(_ni) ((struct iwm_node *)(_ni))