diff options
author | Bruno Randolf <br1@einfach.org> | 2010-09-17 04:36:56 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-09-21 17:05:12 +0200 |
commit | 4edd761f4075b03be5932682a2f7b9368dc9e536 (patch) | |
tree | 2920eb3213e722a68e560eed472c24542812ac77 /drivers/net/wireless/ath/ath5k/base.c | |
parent | ath5k: Move tx frame completion into separate function (diff) | |
download | linux-4edd761f4075b03be5932682a2f7b9368dc9e536.tar.xz linux-4edd761f4075b03be5932682a2f7b9368dc9e536.zip |
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/wireless/ath/ath5k/base.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 51 |
1 files changed, 51 insertions, 0 deletions
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, spin_lock_init(&txq->lock); txq->setup = true; txq->txq_len = 0; + txq->txq_poll_mark = false; } return &sc->txqs[qnum]; } @@ -989,6 +990,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) spin_unlock_bh(&sc->txbuflock); } txq->link = NULL; + txq->txq_poll_mark = false; spin_unlock_bh(&txq->lock); } @@ -1616,6 +1618,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) sc->txbuf_len++; txq->txq_len--; spin_unlock(&sc->txbuflock); + + txq->txq_poll_mark = false; } if (likely(list_empty(&txq->q))) txq->link = NULL; @@ -2170,6 +2174,46 @@ ath5k_tasklet_ani(unsigned long data) } +static void +ath5k_tx_complete_poll_work(struct work_struct *work) +{ + struct ath5k_softc *sc = container_of(work, struct ath5k_softc, + tx_complete_work.work); + struct ath5k_txq *txq; + int i; + bool needreset = false; + + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) { + if (sc->txqs[i].setup) { + txq = &sc->txqs[i]; + spin_lock_bh(&txq->lock); + if (txq->txq_len > 0) { + if (txq->txq_poll_mark) { + ATH5K_DBG(sc, ATH5K_DEBUG_XMIT, + "TX queue stuck %d\n", + txq->qnum); + needreset = true; + spin_unlock_bh(&txq->lock); + break; + } else { + txq->txq_poll_mark = true; + } + } + spin_unlock_bh(&txq->lock); + } + } + + if (needreset) { + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, + "TX queues stuck, resetting\n"); + ath5k_reset(sc, sc->curchan); + } + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, + msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); +} + + /*************************\ * Initialization routines * \*************************/ @@ -2261,6 +2305,10 @@ ath5k_init(struct ath5k_softc *sc) done: mmiowb(); mutex_unlock(&sc->lock); + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, + msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT)); + return ret; } @@ -2319,6 +2367,8 @@ ath5k_stop_hw(struct ath5k_softc *sc) stop_tasklets(sc); + cancel_delayed_work_sync(&sc->tx_complete_work); + ath5k_rfkill_hw_stop(sc->ah); return ret; @@ -2505,6 +2555,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc); INIT_WORK(&sc->reset_work, ath5k_reset_work); + INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work); ret = ath5k_eeprom_read_mac(ah, mac); if (ret) { |