From a129099ef7afbe6fe73ffb9d8a8805b559559d19 Mon Sep 17 00:00:00 2001
From: nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Sun, 7 Apr 2013 22:05:45 +0000
Subject: ath9k: fix a tx processing race condition on AR9300+

Signed-off-by: Felix Fietkau <nbd@openwrt.org>

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@36267 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 package/mac80211/patches/300-pending_work.patch | 74 +++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/package/mac80211/patches/300-pending_work.patch b/package/mac80211/patches/300-pending_work.patch
index 7558c3d1a..569bf7f06 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);
-- 
cgit v1.2.3