aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorBruno Randolf <br1@einfach.org>2010-09-16 22:36:56 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-09-21 11:05:12 -0400
commit4edd761f4075b03be5932682a2f7b9368dc9e536 (patch)
tree2920eb3213e722a68e560eed472c24542812ac77 /drivers/net
parent1440401e7051d4cf66084a7c36125834901bb90d (diff)
ath5k: Add watchdog for stuck TX queues
Since we do not know any better solution to the problem that TX queues can get stuck, this adds a timer-based watchdog, which will check for stuck queues and reset the hardware if necessary. Ported from ath9k commit 164ace38536849966ffa377b1b1132993a5a375d. Signed-off-by: Bruno Randolf <br1@einfach.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h2
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c51
-rw-r--r--drivers/net/wireless/ath/ath5k/base.h3
3 files changed, 56 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 50209aed0ed2..9475b2157f2d 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -206,6 +206,8 @@
206#define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */ 206#define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */
207#define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */ 207#define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */
208 208
209#define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */
210
209#define AR5K_INIT_CARR_SENSE_EN 1 211#define AR5K_INIT_CARR_SENSE_EN 1
210 212
211/*Swap RX/TX Descriptor for big endian archs*/ 213/*Swap RX/TX Descriptor for big endian archs*/
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 81f4b567c6f2..afedfeba13dd 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -891,6 +891,7 @@ ath5k_txq_setup(struct ath5k_softc *sc,
891 spin_lock_init(&txq->lock); 891 spin_lock_init(&txq->lock);
892 txq->setup = true; 892 txq->setup = true;
893 txq->txq_len = 0; 893 txq->txq_len = 0;
894 txq->txq_poll_mark = false;
894 } 895 }
895 return &sc->txqs[qnum]; 896 return &sc->txqs[qnum];
896} 897}
@@ -989,6 +990,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
989 spin_unlock_bh(&sc->txbuflock); 990 spin_unlock_bh(&sc->txbuflock);
990 } 991 }
991 txq->link = NULL; 992 txq->link = NULL;
993 txq->txq_poll_mark = false;
992 spin_unlock_bh(&txq->lock); 994 spin_unlock_bh(&txq->lock);
993} 995}
994 996
@@ -1616,6 +1618,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
1616 sc->txbuf_len++; 1618 sc->txbuf_len++;
1617 txq->txq_len--; 1619 txq->txq_len--;
1618 spin_unlock(&sc->txbuflock); 1620 spin_unlock(&sc->txbuflock);
1621
1622 txq->txq_poll_mark = false;
1619 } 1623 }
1620 if (likely(list_empty(&txq->q))) 1624 if (likely(list_empty(&txq->q)))
1621 txq->link = NULL; 1625 txq->link = NULL;
@@ -2170,6 +2174,46 @@ ath5k_tasklet_ani(unsigned long data)
2170} 2174}
2171 2175
2172 2176
2177static void
2178ath5k_tx_complete_poll_work(struct work_struct *work)
2179{
2180 struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
2181 tx_complete_work.work);
2182 struct ath5k_txq *txq;
2183 int i;
2184 bool needreset = false;
2185
2186 for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
2187 if (sc->txqs[i].setup) {
2188 txq = &sc->txqs[i];
2189 spin_lock_bh(&txq->lock);
2190 if (txq->txq_len > 0) {
2191 if (txq->txq_poll_mark) {
2192 ATH5K_DBG(sc, ATH5K_DEBUG_XMIT,
2193 "TX queue stuck %d\n",
2194 txq->qnum);
2195 needreset = true;
2196 spin_unlock_bh(&txq->lock);
2197 break;
2198 } else {
2199 txq->txq_poll_mark = true;
2200 }
2201 }
2202 spin_unlock_bh(&txq->lock);
2203 }
2204 }
2205
2206 if (needreset) {
2207 ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
2208 "TX queues stuck, resetting\n");
2209 ath5k_reset(sc, sc->curchan);
2210 }
2211
2212 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
2213 msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
2214}
2215
2216
2173/*************************\ 2217/*************************\
2174* Initialization routines * 2218* Initialization routines *
2175\*************************/ 2219\*************************/
@@ -2261,6 +2305,10 @@ ath5k_init(struct ath5k_softc *sc)
2261done: 2305done:
2262 mmiowb(); 2306 mmiowb();
2263 mutex_unlock(&sc->lock); 2307 mutex_unlock(&sc->lock);
2308
2309 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
2310 msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
2311
2264 return ret; 2312 return ret;
2265} 2313}
2266 2314
@@ -2319,6 +2367,8 @@ ath5k_stop_hw(struct ath5k_softc *sc)
2319 2367
2320 stop_tasklets(sc); 2368 stop_tasklets(sc);
2321 2369
2370 cancel_delayed_work_sync(&sc->tx_complete_work);
2371
2322 ath5k_rfkill_hw_stop(sc->ah); 2372 ath5k_rfkill_hw_stop(sc->ah);
2323 2373
2324 return ret; 2374 return ret;
@@ -2505,6 +2555,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
2505 tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc); 2555 tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
2506 2556
2507 INIT_WORK(&sc->reset_work, ath5k_reset_work); 2557 INIT_WORK(&sc->reset_work, ath5k_reset_work);
2558 INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work);
2508 2559
2509 ret = ath5k_eeprom_read_mac(ah, mac); 2560 ret = ath5k_eeprom_read_mac(ah, mac);
2510 if (ret) { 2561 if (ret) {
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index 5e2366d3db09..d8e2674aec71 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -87,6 +87,7 @@ struct ath5k_txq {
87 spinlock_t lock; /* lock on q and link */ 87 spinlock_t lock; /* lock on q and link */
88 bool setup; 88 bool setup;
89 int txq_len; /* number of queued buffers */ 89 int txq_len; /* number of queued buffers */
90 bool txq_poll_mark;
90}; 91};
91 92
92#define ATH5K_LED_MAX_NAME_LEN 31 93#define ATH5K_LED_MAX_NAME_LEN 31
@@ -233,6 +234,8 @@ struct ath5k_softc {
233 234
234 struct ath5k_ani_state ani_state; 235 struct ath5k_ani_state ani_state;
235 struct tasklet_struct ani_tasklet; /* ANI calibration */ 236 struct tasklet_struct ani_tasklet; /* ANI calibration */
237
238 struct delayed_work tx_complete_work;
236}; 239};
237 240
238#define ath5k_hw_hasbssidmask(_ah) \ 241#define ath5k_hw_hasbssidmask(_ah) \