Our iwn(4) driver tries to be a bit smart and clears already acknowledged
frames from Tx aggregation queues, even if those frames are still in the
firmware's current block ack window.
Instead, the driver can simply clear frames before the firmware's BA window.
This matches what the Linux driver does and makes our driver code simpler.
The firmware, not our driver/stack, controls the BA window and shifts it
forwards. our driver and stack can simply follow along. The firmware should
be smart enough to avoid needless retransmissions of frames which have
already been acknowledged.
Also, our Tx rate control code relies on sequence numbers falling into
the BA window, so skip rate control for frames before this window.
Please test this patch on iwn devices and check for regressions. Thanks!
iwn's Tx aggregation code will eventually be ported to iwm(4), so testing
this patch will benefit iwm(4) indirectly.
diff c6a6a64b49f2287751632205d64f594eb6c1ee42
00c4273d9def3b8b2e6adb398412310295298e6b
blob - db09809c2a104300eb522b66ffc2c4032b57cb5b
blob + 0991bb89966837cf5b577d886dcafac59accca1c
--- sys/dev/pci/if_iwn.c
+++ sys/dev/pci/if_iwn.c
@@ -2383,6 +2383,9 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_
*/
ssn = le16toh(cba->ssn);
+ if (SEQ_LT(ssn, ba->ba_winstart))
+ return;
+
/* Skip rate control if our Tx rate is fixed. */
if (ic->ic_fixed_mcs == -1)
iwn_ampdu_rate_control(sc, ni, txq, ba->ba_winstart, ssn);
@@ -2392,12 +2395,10 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_
* in firmware's BA window. Firmware is not going to retransmit any
* frames before its BA window so mark them all as done.
*/
- if (SEQ_LT(ba->ba_winstart, ssn)) {
- ieee80211_output_ba_move_window(ic, ni, cba->tid, ssn);
- iwn_ampdu_txq_advance(sc, txq, qid,
- IWN_AGG_SSN_TO_TXQ_IDX(ssn));
- iwn_clear_oactive(sc, txq);
- }
+ ieee80211_output_ba_move_window(ic, ni, cba->tid, ssn);
+ iwn_ampdu_txq_advance(sc, txq, qid,
+ IWN_AGG_SSN_TO_TXQ_IDX(ssn));
+ iwn_clear_oactive(sc, txq);
}
/*
@@ -2607,6 +2608,8 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_
ba = &ni->ni_tx_ba[tid];
if (ba->ba_state != IEEE80211_BA_AGREED)
return;
+ if (SEQ_LT(ssn, ba->ba_winstart))
+ return;
/* This was a final single-frame Tx attempt for frame SSN-1. */
seq = (ssn - 1) & 0xfff;
@@ -2635,29 +2638,14 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, struct iwn_tx_
if (txfail)
ieee80211_tx_compressed_bar(ic, ni, tid, ssn);
- else if (!SEQ_LT(seq, ba->ba_winstart)) {
- /*
- * Move window forward if SEQ lies beyond end of window,
- * otherwise we can't record the ACK for this frame.
- * Non-acked frames which left holes in the bitmap near
- * the beginning of the window must be discarded.
- */
- uint16_t s = seq;
- while (SEQ_LT(ba->ba_winend, s)) {
- ieee80211_output_ba_move_window(ic, ni, tid, s);
- iwn_ampdu_txq_advance(sc, txq, desc->qid,
- IWN_AGG_SSN_TO_TXQ_IDX(s));
- s = (s + 1) % 0xfff;
- }
- /* SEQ should now be within window; set corresponding bit. */
- ieee80211_output_ba_record_ack(ic, ni, tid, seq);
- }
- /* Move window forward up to the first hole in the bitmap. */
- ieee80211_output_ba_move_window_to_first_unacked(ic, ni, tid, ssn);
- iwn_ampdu_txq_advance(sc, txq, desc->qid,
- IWN_AGG_SSN_TO_TXQ_IDX(ba->ba_winstart));
-
+ /*
+ * SSN corresponds to the first (perhaps not yet transmitted) frame
+ * in firmware's BA window. Firmware is not going to retransmit any
+ * frames before its BA window so mark them all as done.
+ */
+ ieee80211_output_ba_move_window(ic, ni, tid, ssn);
+ iwn_ampdu_txq_advance(sc, txq, desc->qid, IWN_AGG_SSN_TO_TXQ_IDX(ssn));
iwn_clear_oactive(sc, txq);
}