summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2010-09-19 17:23:15 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2010-09-19 17:23:15 +0000
commitd50c66b88163608e7bb156ad3f19c10106dafdd8 (patch)
tree79537fe8ecf5fe238cbac3e6d952a5b2890be3de
parent8ee00eff72299db5c80de85ba0c3ac454ab4450e (diff)
ath9k: fix various aggregation related race conditions
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@23097 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r--package/mac80211/patches/521-ath9k_aggr_race_fix.patch52
-rw-r--r--package/mac80211/patches/522-ath9k_aggr_flush.patch131
2 files changed, 183 insertions, 0 deletions
diff --git a/package/mac80211/patches/521-ath9k_aggr_race_fix.patch b/package/mac80211/patches/521-ath9k_aggr_race_fix.patch
new file mode 100644
index 0000000000..0da1e6124d
--- /dev/null
+++ b/package/mac80211/patches/521-ath9k_aggr_race_fix.patch
@@ -0,0 +1,52 @@
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -784,17 +784,23 @@ static void ath_tx_sched_aggr(struct ath
+ status != ATH_AGGR_BAW_CLOSED);
+ }
+
+-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+- u16 tid, u16 *ssn)
++int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
++ u16 tid, u16 *ssn)
+ {
+ struct ath_atx_tid *txtid;
+ struct ath_node *an;
+
+ an = (struct ath_node *)sta->drv_priv;
+ txtid = ATH_AN_2_TID(an, tid);
++
++ if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE))
++ return -EAGAIN;
++
+ txtid->state |= AGGR_ADDBA_PROGRESS;
+ txtid->paused = true;
+ *ssn = txtid->seq_start;
++
++ return 0;
+ }
+
+ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -346,8 +346,8 @@ void ath_tx_tasklet(struct ath_softc *sc
+ void ath_tx_edma_tasklet(struct ath_softc *sc);
+ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb);
+ bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno);
+-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+- u16 tid, u16 *ssn);
++int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
++ u16 tid, u16 *ssn);
+ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
+ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
+ void ath9k_enable_ps(struct ath_softc *sc);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -1968,7 +1968,7 @@ static int ath9k_ampdu_action(struct iee
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ ath9k_ps_wakeup(sc);
+- ath_tx_aggr_start(sc, sta, tid, ssn);
++ ret = ath_tx_aggr_start(sc, sta, tid, ssn);
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ ath9k_ps_restore(sc);
+ break;
diff --git a/package/mac80211/patches/522-ath9k_aggr_flush.patch b/package/mac80211/patches/522-ath9k_aggr_flush.patch
new file mode 100644
index 0000000000..6fdd42f8f2
--- /dev/null
+++ b/package/mac80211/patches/522-ath9k_aggr_flush.patch
@@ -0,0 +1,131 @@
+--- 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
+ struct ath_tx_status *ts, int txok);
+ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
+ int nbad, int txok, bool update_rc);
++static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
++ int seqno);
+
+ enum {
+ MCS_HT20,
+@@ -144,18 +146,23 @@ static void ath_tx_flush_tid(struct ath_
+ struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
+ struct ath_buf *bf;
+ struct list_head bf_head;
+- INIT_LIST_HEAD(&bf_head);
++ struct ath_tx_status ts;
+
+- WARN_ON(!tid->paused);
++ INIT_LIST_HEAD(&bf_head);
+
++ memset(&ts, 0, sizeof(ts));
+ spin_lock_bh(&txq->axq_lock);
+- tid->paused = false;
+
+ while (!list_empty(&tid->buf_q)) {
+ bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
+- BUG_ON(bf_isretried(bf));
+ list_move_tail(&bf->list, &bf_head);
+- ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
++
++ if (bf_isretried(bf)) {
++ ath_tx_update_baw(sc, tid, bf->bf_seqno);
++ ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
++ } else {
++ ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
++ }
+ }
+
+ spin_unlock_bh(&txq->axq_lock);
+@@ -430,7 +437,7 @@ static void ath_tx_complete_aggr(struct
+ list_move_tail(&bf->list, &bf_head);
+ }
+
+- if (!txpending) {
++ if (!txpending || (tid->state & AGGR_CLEANUP)) {
+ /*
+ * complete the acked-ones/xretried ones; update
+ * block-ack window
+@@ -451,6 +458,7 @@ static void ath_tx_complete_aggr(struct
+ !txfail, sendbar);
+ } else {
+ /* retry the un-acked ones */
++
+ if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) {
+ if (bf->bf_next == NULL && bf_last->bf_stale) {
+ struct ath_buf *tbf;
+@@ -509,15 +517,12 @@ static void ath_tx_complete_aggr(struct
+ }
+
+ if (tid->state & AGGR_CLEANUP) {
++ ath_tx_flush_tid(sc, tid);
++
+ if (tid->baw_head == tid->baw_tail) {
+ tid->state &= ~AGGR_ADDBA_COMPLETE;
+ tid->state &= ~AGGR_CLEANUP;
+-
+- /* send buffered frames as singles */
+- ath_tx_flush_tid(sc, tid);
+ }
+- rcu_read_unlock();
+- return;
+ }
+
+ rcu_read_unlock();
+@@ -808,12 +813,6 @@ void ath_tx_aggr_stop(struct ath_softc *
+ struct ath_node *an = (struct ath_node *)sta->drv_priv;
+ struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
+ struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum];
+- struct ath_tx_status ts;
+- struct ath_buf *bf;
+- struct list_head bf_head;
+-
+- memset(&ts, 0, sizeof(ts));
+- INIT_LIST_HEAD(&bf_head);
+
+ if (txtid->state & AGGR_CLEANUP)
+ return;
+@@ -823,31 +822,22 @@ void ath_tx_aggr_stop(struct ath_softc *
+ return;
+ }
+
+- /* drop all software retried frames and mark this TID */
+ spin_lock_bh(&txq->axq_lock);
+ txtid->paused = true;
+- while (!list_empty(&txtid->buf_q)) {
+- bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
+- if (!bf_isretried(bf)) {
+- /*
+- * NB: it's based on the assumption that
+- * software retried frame will always stay
+- * at the head of software queue.
+- */
+- break;
+- }
+- list_move_tail(&bf->list, &bf_head);
+- ath_tx_update_baw(sc, txtid, bf->bf_seqno);
+- ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
+- }
+- spin_unlock_bh(&txq->axq_lock);
+
+- if (txtid->baw_head != txtid->baw_tail) {
++ /*
++ * If frames are still being transmitted for this TID, they will be
++ * cleaned up during tx completion. To prevent race conditions, this
++ * TID can only be reused after all in-progress subframes have been
++ * completed.
++ */
++ if (txtid->baw_head != txtid->baw_tail)
+ txtid->state |= AGGR_CLEANUP;
+- } else {
++ else
+ txtid->state &= ~AGGR_ADDBA_COMPLETE;
+- ath_tx_flush_tid(sc, txtid);
+- }
++ spin_unlock_bh(&txq->axq_lock);
++
++ ath_tx_flush_tid(sc, txtid);
+ }
+
+ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)