aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSenthil Balasubramanian <senthilkumar@atheros.com>2009-07-14 20:17:09 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-07-24 15:05:18 -0400
commit164ace38536849966ffa377b1b1132993a5a375d (patch)
tree91819800dcfaab603a7d44c148ccf32c13640fd8
parentdd8b15b027d96f7097ae9dbaebd822a114a03c34 (diff)
ath9k: Fix TX hang issue with Atheros chipsets
The hardware doesn't generate interrupts in some cases and so work around this by monitoring the TX status periodically and reset the chip if required. This behavior of the hardware not generating the TX interrupts can be noticed through ath9k debugfs interrupt statistics when heavy traffic is being sent from STA to AP. One can easily see this behavior when the STA is transmitting at a higher rates. The interrupt statistics in the debugfs interface clearly shows that only RX interrupts alone being generated and TX being stuck. TX should be monitored through a timer and reset the chip only when frames are queued to the hardware but TX interrupts are not generated for the same even after one second. Also, we shouldn't remove holding descriptor from AC queue if it happens to be the only descriptor and schedule TX aggregation regarless of queue depth as it improves scheduling of AMPDUs from software to hardware queue. Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com> Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c57
3 files changed, 50 insertions, 14 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 544599b826c1..20bf4a7f896d 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -225,6 +225,8 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
225#define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA) 225#define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA)
226#define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)]) 226#define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)])
227 227
228#define ATH_TX_COMPLETE_POLL_INT 1000
229
228enum ATH_AGGR_STATUS { 230enum ATH_AGGR_STATUS {
229 ATH_AGGR_DONE, 231 ATH_AGGR_DONE,
230 ATH_AGGR_BAW_CLOSED, 232 ATH_AGGR_BAW_CLOSED,
@@ -240,6 +242,7 @@ struct ath_txq {
240 u8 axq_aggr_depth; 242 u8 axq_aggr_depth;
241 u32 axq_totalqueued; 243 u32 axq_totalqueued;
242 bool stopped; 244 bool stopped;
245 bool axq_tx_inprogress;
243 struct ath_buf *axq_linkbuf; 246 struct ath_buf *axq_linkbuf;
244 247
245 /* first desc of the last descriptor that contains CTS */ 248 /* first desc of the last descriptor that contains CTS */
@@ -605,6 +608,7 @@ struct ath_softc {
605#endif 608#endif
606 struct ath_bus_ops *bus_ops; 609 struct ath_bus_ops *bus_ops;
607 struct ath_beacon_config cur_beacon_conf; 610 struct ath_beacon_config cur_beacon_conf;
611 struct delayed_work tx_complete_work;
608}; 612};
609 613
610struct ath_wiphy { 614struct ath_wiphy {
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 2ad718489c0d..46f4a692c8d1 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1259,6 +1259,7 @@ void ath_detach(struct ath_softc *sc)
1259 ath_deinit_leds(sc); 1259 ath_deinit_leds(sc);
1260 cancel_work_sync(&sc->chan_work); 1260 cancel_work_sync(&sc->chan_work);
1261 cancel_delayed_work_sync(&sc->wiphy_work); 1261 cancel_delayed_work_sync(&sc->wiphy_work);
1262 cancel_delayed_work_sync(&sc->tx_complete_work);
1262 1263
1263 for (i = 0; i < sc->num_sec_wiphy; i++) { 1264 for (i = 0; i < sc->num_sec_wiphy; i++) {
1264 struct ath_wiphy *aphy = sc->sec_wiphy[i]; 1265 struct ath_wiphy *aphy = sc->sec_wiphy[i];
@@ -1979,6 +1980,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
1979 1980
1980 ieee80211_wake_queues(hw); 1981 ieee80211_wake_queues(hw);
1981 1982
1983 queue_delayed_work(sc->hw->workqueue, &sc->tx_complete_work, 0);
1984
1982mutex_unlock: 1985mutex_unlock:
1983 mutex_unlock(&sc->mutex); 1986 mutex_unlock(&sc->mutex);
1984 1987
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 5de9878d2c12..a3bc4310a67c 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -857,6 +857,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
857 txq->axq_aggr_depth = 0; 857 txq->axq_aggr_depth = 0;
858 txq->axq_totalqueued = 0; 858 txq->axq_totalqueued = 0;
859 txq->axq_linkbuf = NULL; 859 txq->axq_linkbuf = NULL;
860 txq->axq_tx_inprogress = false;
860 sc->tx.txqsetup |= 1<<qnum; 861 sc->tx.txqsetup |= 1<<qnum;
861 } 862 }
862 return &sc->tx.txq[qnum]; 863 return &sc->tx.txq[qnum];
@@ -1023,6 +1024,10 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
1023 ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); 1024 ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
1024 } 1025 }
1025 1026
1027 spin_lock_bh(&txq->axq_lock);
1028 txq->axq_tx_inprogress = false;
1029 spin_unlock_bh(&txq->axq_lock);
1030
1026 /* flush any pending frames if aggregation is enabled */ 1031 /* flush any pending frames if aggregation is enabled */
1027 if (sc->sc_flags & SC_OP_TXAGGR) { 1032 if (sc->sc_flags & SC_OP_TXAGGR) {
1028 if (!retry_tx) { 1033 if (!retry_tx) {
@@ -1103,8 +1108,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
1103 if (tid->paused) 1108 if (tid->paused)
1104 continue; 1109 continue;
1105 1110
1106 if ((txq->axq_depth % 2) == 0) 1111 ath_tx_sched_aggr(sc, txq, tid);
1107 ath_tx_sched_aggr(sc, txq, tid);
1108 1112
1109 /* 1113 /*
1110 * add tid to round-robin queue if more frames 1114 * add tid to round-robin queue if more frames
@@ -1947,19 +1951,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
1947 if (bf->bf_stale) { 1951 if (bf->bf_stale) {
1948 bf_held = bf; 1952 bf_held = bf;
1949 if (list_is_last(&bf_held->list, &txq->axq_q)) { 1953 if (list_is_last(&bf_held->list, &txq->axq_q)) {
1950 txq->axq_link = NULL;
1951 txq->axq_linkbuf = NULL;
1952 spin_unlock_bh(&txq->axq_lock); 1954 spin_unlock_bh(&txq->axq_lock);
1953
1954 /*
1955 * The holding descriptor is the last
1956 * descriptor in queue. It's safe to remove
1957 * the last holding descriptor in BH context.
1958 */
1959 spin_lock_bh(&sc->tx.txbuflock);
1960 list_move_tail(&bf_held->list, &sc->tx.txbuf);
1961 spin_unlock_bh(&sc->tx.txbuflock);
1962
1963 break; 1955 break;
1964 } else { 1956 } else {
1965 bf = list_entry(bf_held->list.next, 1957 bf = list_entry(bf_held->list.next,
@@ -1996,6 +1988,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
1996 txq->axq_aggr_depth--; 1988 txq->axq_aggr_depth--;
1997 1989
1998 txok = (ds->ds_txstat.ts_status == 0); 1990 txok = (ds->ds_txstat.ts_status == 0);
1991 txq->axq_tx_inprogress = false;
1999 spin_unlock_bh(&txq->axq_lock); 1992 spin_unlock_bh(&txq->axq_lock);
2000 1993
2001 if (bf_held) { 1994 if (bf_held) {
@@ -2029,6 +2022,40 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
2029 } 2022 }
2030} 2023}
2031 2024
2025void ath_tx_complete_poll_work(struct work_struct *work)
2026{
2027 struct ath_softc *sc = container_of(work, struct ath_softc,
2028 tx_complete_work.work);
2029 struct ath_txq *txq;
2030 int i;
2031 bool needreset = false;
2032
2033 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
2034 if (ATH_TXQ_SETUP(sc, i)) {
2035 txq = &sc->tx.txq[i];
2036 spin_lock_bh(&txq->axq_lock);
2037 if (txq->axq_depth) {
2038 if (txq->axq_tx_inprogress) {
2039 needreset = true;
2040 spin_unlock_bh(&txq->axq_lock);
2041 break;
2042 } else {
2043 txq->axq_tx_inprogress = true;
2044 }
2045 }
2046 spin_unlock_bh(&txq->axq_lock);
2047 }
2048
2049 if (needreset) {
2050 DPRINTF(sc, ATH_DBG_RESET, "tx hung, resetting the chip\n");
2051 ath_reset(sc, false);
2052 }
2053
2054 queue_delayed_work(sc->hw->workqueue, &sc->tx_complete_work,
2055 msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
2056}
2057
2058
2032 2059
2033void ath_tx_tasklet(struct ath_softc *sc) 2060void ath_tx_tasklet(struct ath_softc *sc)
2034{ 2061{
@@ -2069,6 +2096,8 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
2069 goto err; 2096 goto err;
2070 } 2097 }
2071 2098
2099 INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
2100
2072err: 2101err:
2073 if (error != 0) 2102 if (error != 0)
2074 ath_tx_cleanup(sc); 2103 ath_tx_cleanup(sc);