ath9k: fix queue stopping threshold

ath9k tries to prevent WMM queue tx buffer starvation caused by
traffic on different queues by limiting the number of pending frames
in a tx queue (tracked in the ath_buf structure). This had a leak
issue, because the a skb can be reassigned to a different ath_buf
in the tx path, causing the pending frame counter to become inaccurate.

To fix this, track the number of frames in an array in the softc,
using the mac80211 queue mapping as index.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 9bff6c5..875b8b4 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1760,7 +1760,7 @@
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ath_txq *txq = txctl->txq;
 	struct ath_buf *bf;
-	int r;
+	int q, r;
 
 	bf = ath_tx_get_buffer(sc);
 	if (!bf) {
@@ -1768,14 +1768,6 @@
 		return -1;
 	}
 
-	bf->txq = txctl->txq;
-	spin_lock_bh(&bf->txq->axq_lock);
-	if (++bf->txq->pending_frames > ATH_MAX_QDEPTH && !txq->stopped) {
-		ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb));
-		txq->stopped = 1;
-	}
-	spin_unlock_bh(&bf->txq->axq_lock);
-
 	r = ath_tx_setup_buffer(hw, bf, skb, txctl);
 	if (unlikely(r)) {
 		ath_print(common, ATH_DBG_FATAL, "TX mem alloc failure\n");
@@ -1796,6 +1788,17 @@
 		return r;
 	}
 
+	q = skb_get_queue_mapping(skb);
+	if (q >= 4)
+		q = 0;
+
+	spin_lock_bh(&txq->axq_lock);
+	if (++sc->tx.pending_frames[q] > ATH_MAX_QDEPTH && !txq->stopped) {
+		ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb));
+		txq->stopped = 1;
+	}
+	spin_unlock_bh(&txq->axq_lock);
+
 	ath_tx_start_dma(sc, bf, txctl);
 
 	return 0;
@@ -1865,7 +1868,7 @@
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
-	int padpos, padsize;
+	int q, padpos, padsize;
 
 	ath_print(common, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
 
@@ -1904,8 +1907,16 @@
 
 	if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL))
 		ath9k_tx_status(hw, skb);
-	else
+	else {
+		q = skb_get_queue_mapping(skb);
+		if (q >= 4)
+			q = 0;
+
+		if (--sc->tx.pending_frames[q] < 0)
+			sc->tx.pending_frames[q] = 0;
+
 		ieee80211_tx_status(hw, skb);
+	}
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
@@ -1926,13 +1937,6 @@
 			tx_flags |= ATH_TX_XRETRY;
 	}
 
-	if (bf->txq) {
-		spin_lock_bh(&bf->txq->axq_lock);
-		bf->txq->pending_frames--;
-		spin_unlock_bh(&bf->txq->axq_lock);
-		bf->txq = NULL;
-	}
-
 	dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
 	ath_tx_complete(sc, skb, bf->aphy, tx_flags);
 	ath_debug_stat_tx(sc, txq, bf, ts);
@@ -2020,13 +2024,14 @@
 {
 	int qnum;
 
+	qnum = ath_get_mac80211_qnum(txq->axq_class, sc);
+	if (qnum == -1)
+		return;
+
 	spin_lock_bh(&txq->axq_lock);
-	if (txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) {
-		qnum = ath_get_mac80211_qnum(txq->axq_class, sc);
-		if (qnum != -1) {
-			ath_mac80211_start_queue(sc, qnum);
-			txq->stopped = 0;
-		}
+	if (txq->stopped && sc->tx.pending_frames[qnum] < ATH_MAX_QDEPTH) {
+		ath_mac80211_start_queue(sc, qnum);
+		txq->stopped = 0;
 	}
 	spin_unlock_bh(&txq->axq_lock);
 }