diff options
author | Bruno Randolf <br1@einfach.org> | 2010-09-16 22:36:56 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-09-21 11:05:12 -0400 |
commit | 4edd761f4075b03be5932682a2f7b9368dc9e536 (patch) | |
tree | 2920eb3213e722a68e560eed472c24542812ac77 /drivers/net | |
parent | 1440401e7051d4cf66084a7c36125834901bb90d (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.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 51 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.h | 3 |
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 | ||
2177 | static void | ||
2178 | ath5k_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) | |||
2261 | done: | 2305 | done: |
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) \ |