From c18d6e16ea2334b2d379f7a1e1778427b17666e6 Mon Sep 17 00:00:00 2001
From: nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Tue, 1 Jun 2010 17:38:01 +0000
Subject: [PATCH] ath9k: fix queue stopping/starting logic, should slightly
 reduce RAM usage under load and make throughput more smooth

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@21650 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 .../patches/530-ath9k_queue_fill.patch        | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100644 package/mac80211/patches/530-ath9k_queue_fill.patch

diff --git a/package/mac80211/patches/530-ath9k_queue_fill.patch b/package/mac80211/patches/530-ath9k_queue_fill.patch
new file mode 100644
index 000000000..172fba400
--- /dev/null
+++ b/package/mac80211/patches/530-ath9k_queue_fill.patch
@@ -0,0 +1,144 @@
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -137,6 +137,8 @@ void ath_descdma_cleanup(struct ath_soft
+ #define ATH_MAX_ANTENNA         3
+ #define ATH_RXBUF               512
+ #define ATH_TXBUF               512
++#define ATH_TXBUF_RESERVE       5
++#define ATH_MAX_QDEPTH          (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE)
+ #define ATH_TXMAXTRY            13
+ #define ATH_MGT_TXMAXTRY        4
+ 
+@@ -205,6 +207,7 @@ struct ath_txq {
+ 	struct list_head txq_fifo_pending;
+ 	u8 txq_headidx;
+ 	u8 txq_tailidx;
++	int pending_frames;
+ };
+ 
+ struct ath_atx_ac {
+@@ -242,6 +245,7 @@ struct ath_buf {
+ 	struct ath_buf_state bf_state;
+ 	dma_addr_t bf_dmacontext;
+ 	struct ath_wiphy *aphy;
++	struct ath_txq *txq;
+ };
+ 
+ struct ath_atx_tid {
+@@ -331,7 +335,6 @@ void ath_tx_node_cleanup(struct ath_soft
+ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
+ int ath_tx_init(struct ath_softc *sc, int nbufs);
+ void ath_tx_cleanup(struct ath_softc *sc);
+-struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb);
+ int ath_txq_update(struct ath_softc *sc, int qnum,
+ 		   struct ath9k_tx_queue_info *q);
+ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -986,32 +986,6 @@ int ath_tx_get_qnum(struct ath_softc *sc
+ 	return qnum;
+ }
+ 
+-struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb)
+-{
+-	struct ath_txq *txq = NULL;
+-	u16 skb_queue = skb_get_queue_mapping(skb);
+-	int qnum;
+-
+-	qnum = ath_get_hal_qnum(skb_queue, sc);
+-	txq = &sc->tx.txq[qnum];
+-
+-	spin_lock_bh(&txq->axq_lock);
+-
+-	if (txq->axq_depth >= (ATH_TXBUF - 20)) {
+-		ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_XMIT,
+-			  "TX queue: %d is full, depth: %d\n",
+-			  qnum, txq->axq_depth);
+-		ath_mac80211_stop_queue(sc, skb_queue);
+-		txq->stopped = 1;
+-		spin_unlock_bh(&txq->axq_lock);
+-		return NULL;
+-	}
+-
+-	spin_unlock_bh(&txq->axq_lock);
+-
+-	return txq;
+-}
+-
+ int ath_txq_update(struct ath_softc *sc, int qnum,
+ 		   struct ath9k_tx_queue_info *qinfo)
+ {
+@@ -1811,6 +1785,7 @@ int ath_tx_start(struct ieee80211_hw *hw
+ 	struct ath_wiphy *aphy = hw->priv;
+ 	struct ath_softc *sc = aphy->sc;
+ 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++	struct ath_txq *txq = txctl->txq;
+ 	struct ath_buf *bf;
+ 	int r;
+ 
+@@ -1820,10 +1795,16 @@ int ath_tx_start(struct ieee80211_hw *hw
+ 		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)) {
+-		struct ath_txq *txq = txctl->txq;
+-
+ 		ath_print(common, ATH_DBG_FATAL, "TX mem alloc failure\n");
+ 
+ 		/* upon ath_tx_processq() this TX queue will be resumed, we
+@@ -1831,7 +1812,7 @@ int ath_tx_start(struct ieee80211_hw *hw
+ 		 * we will at least have to run TX completionon one buffer
+ 		 * on the queue */
+ 		spin_lock_bh(&txq->axq_lock);
+-		if (sc->tx.txq[txq->axq_qnum].axq_depth > 1) {
++		if (!txq->stopped && txq->axq_depth > 1) {
+ 			ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb));
+ 			txq->stopped = 1;
+ 		}
+@@ -1972,6 +1953,13 @@ static void ath_tx_complete_buf(struct a
+ 			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);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -1026,6 +1026,7 @@ static int ath9k_tx(struct ieee80211_hw 
+ 	struct ath_tx_control txctl;
+ 	int padpos, padsize;
+ 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
++	int qnum;
+ 
+ 	if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
+ 		ath_print(common, ATH_DBG_XMIT,
+@@ -1098,11 +1099,8 @@ static int ath9k_tx(struct ieee80211_hw 
+ 		memmove(skb->data, skb->data + padsize, padpos);
+ 	}
+ 
+-	/* Check if a tx queue is available */
+-
+-	txctl.txq = ath_test_get_txq(sc, skb);
+-	if (!txctl.txq)
+-		goto exit;
++	qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc);
++	txctl.txq = &sc->tx.txq[qnum];
+ 
+ 	ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
+ 
-- 
2.20.1