aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2010-09-20 07:45:38 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-09-21 11:05:33 -0400
commit90fa539ca3f07323da5a90f5c8f4e5cd952875e7 (patch)
tree8d8bb5a53f2316cd36f8b4c709e7db8eafe3db89 /drivers/net/wireless/ath/ath9k
parent231c3a1f0630c07a584905507a1cb7b705a56ab7 (diff)
ath9k: clean up / fix aggregation session flush
The tid aggregation cleanup is a bit fragile, as it discards failed subframes in some places, and retransmits them in others. This could block the cleanup of an existing aggregation session, if a retransmission for a tid is issued, yet the tid is never scheduled again because of the cleanup state. Fix this by getting rid of as many subframes as possible, as early as possible, and immediately transmitting pending subframes as regular HT frames instead of waiting for the cleanup to complete. Drop all pending subframes while keeping track of the Block ACK window during aggregate tx completion to prevent sending out stale subframes, which could confuse the receiver side. Signed-off-by: Felix Fietkau <nbd@openwrt.org> Cc: stable@kernel.org Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k')
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c63
1 files changed, 26 insertions, 37 deletions
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index d629bfbdfab4..e53433e3e4cc 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -61,6 +61,8 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
61 struct ath_tx_status *ts, int txok); 61 struct ath_tx_status *ts, int txok);
62static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts, 62static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
63 int nbad, int txok, bool update_rc); 63 int nbad, int txok, bool update_rc);
64static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
65 int seqno);
64 66
65enum { 67enum {
66 MCS_HT20, 68 MCS_HT20,
@@ -143,18 +145,23 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
143 struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum]; 145 struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
144 struct ath_buf *bf; 146 struct ath_buf *bf;
145 struct list_head bf_head; 147 struct list_head bf_head;
146 INIT_LIST_HEAD(&bf_head); 148 struct ath_tx_status ts;
147 149
148 WARN_ON(!tid->paused); 150 INIT_LIST_HEAD(&bf_head);
149 151
152 memset(&ts, 0, sizeof(ts));
150 spin_lock_bh(&txq->axq_lock); 153 spin_lock_bh(&txq->axq_lock);
151 tid->paused = false;
152 154
153 while (!list_empty(&tid->buf_q)) { 155 while (!list_empty(&tid->buf_q)) {
154 bf = list_first_entry(&tid->buf_q, struct ath_buf, list); 156 bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
155 BUG_ON(bf_isretried(bf));
156 list_move_tail(&bf->list, &bf_head); 157 list_move_tail(&bf->list, &bf_head);
157 ath_tx_send_ht_normal(sc, txq, tid, &bf_head); 158
159 if (bf_isretried(bf)) {
160 ath_tx_update_baw(sc, tid, bf->bf_seqno);
161 ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
162 } else {
163 ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
164 }
158 } 165 }
159 166
160 spin_unlock_bh(&txq->axq_lock); 167 spin_unlock_bh(&txq->axq_lock);
@@ -429,7 +436,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
429 list_move_tail(&bf->list, &bf_head); 436 list_move_tail(&bf->list, &bf_head);
430 } 437 }
431 438
432 if (!txpending) { 439 if (!txpending || (tid->state & AGGR_CLEANUP)) {
433 /* 440 /*
434 * complete the acked-ones/xretried ones; update 441 * complete the acked-ones/xretried ones; update
435 * block-ack window 442 * block-ack window
@@ -508,15 +515,12 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
508 } 515 }
509 516
510 if (tid->state & AGGR_CLEANUP) { 517 if (tid->state & AGGR_CLEANUP) {
518 ath_tx_flush_tid(sc, tid);
519
511 if (tid->baw_head == tid->baw_tail) { 520 if (tid->baw_head == tid->baw_tail) {
512 tid->state &= ~AGGR_ADDBA_COMPLETE; 521 tid->state &= ~AGGR_ADDBA_COMPLETE;
513 tid->state &= ~AGGR_CLEANUP; 522 tid->state &= ~AGGR_CLEANUP;
514
515 /* send buffered frames as singles */
516 ath_tx_flush_tid(sc, tid);
517 } 523 }
518 rcu_read_unlock();
519 return;
520 } 524 }
521 525
522 rcu_read_unlock(); 526 rcu_read_unlock();
@@ -807,12 +811,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
807 struct ath_node *an = (struct ath_node *)sta->drv_priv; 811 struct ath_node *an = (struct ath_node *)sta->drv_priv;
808 struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); 812 struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
809 struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum]; 813 struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum];
810 struct ath_tx_status ts;
811 struct ath_buf *bf;
812 struct list_head bf_head;
813
814 memset(&ts, 0, sizeof(ts));
815 INIT_LIST_HEAD(&bf_head);
816 814
817 if (txtid->state & AGGR_CLEANUP) 815 if (txtid->state & AGGR_CLEANUP)
818 return; 816 return;
@@ -822,31 +820,22 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
822 return; 820 return;
823 } 821 }
824 822
825 /* drop all software retried frames and mark this TID */
826 spin_lock_bh(&txq->axq_lock); 823 spin_lock_bh(&txq->axq_lock);
827 txtid->paused = true; 824 txtid->paused = true;
828 while (!list_empty(&txtid->buf_q)) {
829 bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
830 if (!bf_isretried(bf)) {
831 /*
832 * NB: it's based on the assumption that
833 * software retried frame will always stay
834 * at the head of software queue.
835 */
836 break;
837 }
838 list_move_tail(&bf->list, &bf_head);
839 ath_tx_update_baw(sc, txtid, bf->bf_seqno);
840 ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
841 }
842 spin_unlock_bh(&txq->axq_lock);
843 825
844 if (txtid->baw_head != txtid->baw_tail) { 826 /*
827 * If frames are still being transmitted for this TID, they will be
828 * cleaned up during tx completion. To prevent race conditions, this
829 * TID can only be reused after all in-progress subframes have been
830 * completed.
831 */
832 if (txtid->baw_head != txtid->baw_tail)
845 txtid->state |= AGGR_CLEANUP; 833 txtid->state |= AGGR_CLEANUP;
846 } else { 834 else
847 txtid->state &= ~AGGR_ADDBA_COMPLETE; 835 txtid->state &= ~AGGR_ADDBA_COMPLETE;
848 ath_tx_flush_tid(sc, txtid); 836 spin_unlock_bh(&txq->axq_lock);
849 } 837
838 ath_tx_flush_tid(sc, txtid);
850} 839}
851 840
852void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) 841void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)