Commit 4edd761f authored by Bruno Randolf's avatar Bruno Randolf Committed by John W. Linville

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 164ace38.
Signed-off-by: default avatarBruno Randolf <br1@einfach.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 1440401e
...@@ -206,6 +206,8 @@ ...@@ -206,6 +206,8 @@
#define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */
#define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */
#define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */
#define AR5K_INIT_CARR_SENSE_EN 1 #define AR5K_INIT_CARR_SENSE_EN 1
/*Swap RX/TX Descriptor for big endian archs*/ /*Swap RX/TX Descriptor for big endian archs*/
......
...@@ -891,6 +891,7 @@ ath5k_txq_setup(struct ath5k_softc *sc, ...@@ -891,6 +891,7 @@ ath5k_txq_setup(struct ath5k_softc *sc,
spin_lock_init(&txq->lock); spin_lock_init(&txq->lock);
txq->setup = true; txq->setup = true;
txq->txq_len = 0; txq->txq_len = 0;
txq->txq_poll_mark = false;
} }
return &sc->txqs[qnum]; return &sc->txqs[qnum];
} }
...@@ -989,6 +990,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) ...@@ -989,6 +990,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
spin_unlock_bh(&sc->txbuflock); spin_unlock_bh(&sc->txbuflock);
} }
txq->link = NULL; txq->link = NULL;
txq->txq_poll_mark = false;
spin_unlock_bh(&txq->lock); spin_unlock_bh(&txq->lock);
} }
...@@ -1616,6 +1618,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) ...@@ -1616,6 +1618,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
sc->txbuf_len++; sc->txbuf_len++;
txq->txq_len--; txq->txq_len--;
spin_unlock(&sc->txbuflock); spin_unlock(&sc->txbuflock);
txq->txq_poll_mark = false;
} }
if (likely(list_empty(&txq->q))) if (likely(list_empty(&txq->q)))
txq->link = NULL; txq->link = NULL;
...@@ -2170,6 +2174,46 @@ ath5k_tasklet_ani(unsigned long data) ...@@ -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 * * Initialization routines *
\*************************/ \*************************/
...@@ -2261,6 +2305,10 @@ ath5k_init(struct ath5k_softc *sc) ...@@ -2261,6 +2305,10 @@ ath5k_init(struct ath5k_softc *sc)
done: done:
mmiowb(); mmiowb();
mutex_unlock(&sc->lock); mutex_unlock(&sc->lock);
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
msecs_to_jiffies(ATH5K_TX_COMPLETE_POLL_INT));
return ret; return ret;
} }
...@@ -2319,6 +2367,8 @@ ath5k_stop_hw(struct ath5k_softc *sc) ...@@ -2319,6 +2367,8 @@ ath5k_stop_hw(struct ath5k_softc *sc)
stop_tasklets(sc); stop_tasklets(sc);
cancel_delayed_work_sync(&sc->tx_complete_work);
ath5k_rfkill_hw_stop(sc->ah); ath5k_rfkill_hw_stop(sc->ah);
return ret; return ret;
...@@ -2505,6 +2555,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) ...@@ -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); tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
INIT_WORK(&sc->reset_work, ath5k_reset_work); 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); ret = ath5k_eeprom_read_mac(ah, mac);
if (ret) { if (ret) {
......
...@@ -87,6 +87,7 @@ struct ath5k_txq { ...@@ -87,6 +87,7 @@ struct ath5k_txq {
spinlock_t lock; /* lock on q and link */ spinlock_t lock; /* lock on q and link */
bool setup; bool setup;
int txq_len; /* number of queued buffers */ int txq_len; /* number of queued buffers */
bool txq_poll_mark;
}; };
#define ATH5K_LED_MAX_NAME_LEN 31 #define ATH5K_LED_MAX_NAME_LEN 31
...@@ -233,6 +234,8 @@ struct ath5k_softc { ...@@ -233,6 +234,8 @@ struct ath5k_softc {
struct ath5k_ani_state ani_state; struct ath5k_ani_state ani_state;
struct tasklet_struct ani_tasklet; /* ANI calibration */ struct tasklet_struct ani_tasklet; /* ANI calibration */
struct delayed_work tx_complete_work;
}; };
#define ath5k_hw_hasbssidmask(_ah) \ #define ath5k_hw_hasbssidmask(_ah) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment