aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2013-04-07 22:05:45 +0000
committerFelix Fietkau <nbd@openwrt.org>2013-04-07 22:05:45 +0000
commitc42c4837b9be2ca7437c6bb8d524c841dc349fbe (patch)
treef0b386d42eba3917592739ca59d1d8eb1d1af2bb
parent3a0e6c06e9a8dc461d9791448d784d9bf962e57d (diff)
downloadupstream-c42c4837b9be2ca7437c6bb8d524c841dc349fbe.tar.gz
upstream-c42c4837b9be2ca7437c6bb8d524c841dc349fbe.tar.bz2
upstream-c42c4837b9be2ca7437c6bb8d524c841dc349fbe.zip
ath9k: fix a tx processing race condition on AR9300+
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 36267
-rw-r--r--package/mac80211/patches/300-pending_work.patch74
1 files changed, 74 insertions, 0 deletions
diff --git a/package/mac80211/patches/300-pending_work.patch b/package/mac80211/patches/300-pending_work.patch
index 7558c3d1a2..569bf7f06a 100644
--- a/package/mac80211/patches/300-pending_work.patch
+++ b/package/mac80211/patches/300-pending_work.patch
@@ -1036,3 +1036,77 @@
dev_err(priv->dev, "ath9k_htc: Please upgrade to FW version %d.%d\n",
MAJOR_VERSION_REQ, MINOR_VERSION_REQ);
return -EINVAL;
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -516,8 +516,7 @@ static void ath_tx_complete_aggr(struct
+ * not a holding desc.
+ */
+ INIT_LIST_HEAD(&bf_head);
+- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ||
+- bf_next != NULL || !bf_last->bf_stale)
++ if (bf_next != NULL || !bf_last->bf_stale)
+ list_move_tail(&bf->list, &bf_head);
+
+ if (!txpending || (tid->state & AGGR_CLEANUP)) {
+@@ -537,8 +536,7 @@ static void ath_tx_complete_aggr(struct
+ !txfail);
+ } else {
+ /* retry the un-acked ones */
+- if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
+- bf->bf_next == NULL && bf_last->bf_stale) {
++ if (bf->bf_next == NULL && bf_last->bf_stale) {
+ struct ath_buf *tbf;
+
+ tbf = ath_clone_txbuf(sc, bf_last);
+@@ -2264,6 +2262,7 @@ void ath_tx_edma_tasklet(struct ath_soft
+ struct ath_txq *txq;
+ struct ath_buf *bf, *lastbf;
+ struct list_head bf_head;
++ struct list_head *fifo_list;
+ int status;
+
+ for (;;) {
+@@ -2291,20 +2290,24 @@ void ath_tx_edma_tasklet(struct ath_soft
+
+ TX_STAT_INC(txq->axq_qnum, txprocdesc);
+
+- if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
++ fifo_list = &txq->txq_fifo[txq->txq_tailidx];
++ if (list_empty(fifo_list)) {
+ ath_txq_unlock(sc, txq);
+ return;
+ }
+
+- bf = list_first_entry(&txq->txq_fifo[txq->txq_tailidx],
+- struct ath_buf, list);
++ bf = list_first_entry(fifo_list, struct ath_buf, list);
++ if (bf->bf_stale) {
++ list_del(&bf->list);
++ ath_tx_return_buffer(sc, bf);
++ bf = list_first_entry(fifo_list, struct ath_buf, list);
++ }
++
+ lastbf = bf->bf_lastbf;
+
+ INIT_LIST_HEAD(&bf_head);
+- list_cut_position(&bf_head, &txq->txq_fifo[txq->txq_tailidx],
+- &lastbf->list);
+-
+- if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) {
++ if (list_is_last(&lastbf->list, fifo_list)) {
++ list_splice_tail_init(fifo_list, &bf_head);
+ INCR(txq->txq_tailidx, ATH_TXFIFO_DEPTH);
+
+ if (!list_empty(&txq->axq_q)) {
+@@ -2315,6 +2318,11 @@ void ath_tx_edma_tasklet(struct ath_soft
+ list_splice_tail_init(&txq->axq_q, &bf_q);
+ ath_tx_txqaddbuf(sc, txq, &bf_q, true);
+ }
++ } else {
++ lastbf->bf_stale = true;
++ if (bf != lastbf)
++ list_cut_position(&bf_head, fifo_list,
++ lastbf->list.prev);
+ }
+
+ ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);