diff options
author | Felix Fietkau <nbd@openwrt.org> | 2010-06-24 19:26:16 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-06-28 15:16:19 -0400 |
commit | 78c4653a2274479547e259e1f416d2b3d04c42a8 (patch) | |
tree | 84249c1c0d2cc299c6422347653651406c6a0d01 /drivers | |
parent | 6665b54e79d52c813914481783d82398ca2451f6 (diff) |
ath9k: fix retry count for A-MPDU rate control status reports
The 'bf_retries' field of the ath_buf structure was used for both
software retries (AMPDU subframes) and hardware retries (legacy
frames). This led to a wrong retry count being reported for the A-MPDU
rate control stats.
This patch changes the code to no longer use bf_retries for reporting
retry counts, but instead always using the real on-chip retry count
from the ath_tx_status.
Additionally, if the first subframe of an A-MPDU was not acked, the tx
status report is submitted along with the first acked subframe, which
may not contain the correct rates in the tx info.
This is easily corrected by saving the tx rate info before looping over
subframes, and then copying it back once the A-MPDU status report is
submitted.
In my tests this change improves throughput visibly.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Reported-by: Björn Smedman <bjorn.smedman@venatech.se>
Cc: stable@kernel.org
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 11 |
1 files changed, 8 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index c41185b28c0a..c3681a1dc941 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c | |||
@@ -328,6 +328,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
328 | u32 ba[WME_BA_BMP_SIZE >> 5]; | 328 | u32 ba[WME_BA_BMP_SIZE >> 5]; |
329 | int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; | 329 | int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; |
330 | bool rc_update = true; | 330 | bool rc_update = true; |
331 | struct ieee80211_tx_rate rates[4]; | ||
331 | 332 | ||
332 | skb = bf->bf_mpdu; | 333 | skb = bf->bf_mpdu; |
333 | hdr = (struct ieee80211_hdr *)skb->data; | 334 | hdr = (struct ieee80211_hdr *)skb->data; |
@@ -335,6 +336,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
335 | tx_info = IEEE80211_SKB_CB(skb); | 336 | tx_info = IEEE80211_SKB_CB(skb); |
336 | hw = bf->aphy->hw; | 337 | hw = bf->aphy->hw; |
337 | 338 | ||
339 | memcpy(rates, tx_info->control.rates, sizeof(rates)); | ||
340 | |||
338 | rcu_read_lock(); | 341 | rcu_read_lock(); |
339 | 342 | ||
340 | /* XXX: use ieee80211_find_sta! */ | 343 | /* XXX: use ieee80211_find_sta! */ |
@@ -375,6 +378,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
375 | txfail = txpending = 0; | 378 | txfail = txpending = 0; |
376 | bf_next = bf->bf_next; | 379 | bf_next = bf->bf_next; |
377 | 380 | ||
381 | skb = bf->bf_mpdu; | ||
382 | tx_info = IEEE80211_SKB_CB(skb); | ||
383 | |||
378 | if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) { | 384 | if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) { |
379 | /* transmit completion, subframe is | 385 | /* transmit completion, subframe is |
380 | * acked by block ack */ | 386 | * acked by block ack */ |
@@ -428,6 +434,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
428 | spin_unlock_bh(&txq->axq_lock); | 434 | spin_unlock_bh(&txq->axq_lock); |
429 | 435 | ||
430 | if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { | 436 | if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) { |
437 | memcpy(tx_info->control.rates, rates, sizeof(rates)); | ||
431 | ath_tx_rc_status(bf, ts, nbad, txok, true); | 438 | ath_tx_rc_status(bf, ts, nbad, txok, true); |
432 | rc_update = false; | 439 | rc_update = false; |
433 | } else { | 440 | } else { |
@@ -2033,7 +2040,7 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, | |||
2033 | tx_info->status.rates[i].idx = -1; | 2040 | tx_info->status.rates[i].idx = -1; |
2034 | } | 2041 | } |
2035 | 2042 | ||
2036 | tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1; | 2043 | tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; |
2037 | } | 2044 | } |
2038 | 2045 | ||
2039 | static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) | 2046 | static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq) |
@@ -2144,7 +2151,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) | |||
2144 | * This frame is sent out as a single frame. | 2151 | * This frame is sent out as a single frame. |
2145 | * Use hardware retry status for this frame. | 2152 | * Use hardware retry status for this frame. |
2146 | */ | 2153 | */ |
2147 | bf->bf_retries = ts.ts_longretry; | ||
2148 | if (ts.ts_status & ATH9K_TXERR_XRETRY) | 2154 | if (ts.ts_status & ATH9K_TXERR_XRETRY) |
2149 | bf->bf_state.bf_type |= BUF_XRETRY; | 2155 | bf->bf_state.bf_type |= BUF_XRETRY; |
2150 | ath_tx_rc_status(bf, &ts, 0, txok, true); | 2156 | ath_tx_rc_status(bf, &ts, 0, txok, true); |
@@ -2274,7 +2280,6 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) | |||
2274 | } | 2280 | } |
2275 | 2281 | ||
2276 | if (!bf_isampdu(bf)) { | 2282 | if (!bf_isampdu(bf)) { |
2277 | bf->bf_retries = txs.ts_longretry; | ||
2278 | if (txs.ts_status & ATH9K_TXERR_XRETRY) | 2283 | if (txs.ts_status & ATH9K_TXERR_XRETRY) |
2279 | bf->bf_state.bf_type |= BUF_XRETRY; | 2284 | bf->bf_state.bf_type |= BUF_XRETRY; |
2280 | ath_tx_rc_status(bf, &txs, 0, txok, true); | 2285 | ath_tx_rc_status(bf, &txs, 0, txok, true); |