ath9k: fix a DMA related race condition on reset
When ath_drain_all_txq fails to stop DMA, it issues a hw reset. This reset happens at a very problematic point in time, when the hardware rx path has not been stopped yet. This could lead to memory corruption, hardware hangs or other issues. To fix these issues, simply remove the reset entirely and check the tx DMA stop status to prevent problems with fast channel changes. Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:

committed by
John W. Linville

parent
841051602e
commit
080e1a259a
@@ -1120,7 +1120,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
|
||||
}
|
||||
}
|
||||
|
||||
void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
|
||||
bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
@@ -1128,7 +1128,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
|
||||
int i, npend = 0;
|
||||
|
||||
if (sc->sc_flags & SC_OP_INVALID)
|
||||
return;
|
||||
return true;
|
||||
|
||||
/* Stop beacon queue */
|
||||
ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
|
||||
@@ -1142,25 +1142,15 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
|
||||
}
|
||||
}
|
||||
|
||||
if (npend) {
|
||||
int r;
|
||||
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Failed to stop TX DMA. Resetting hardware!\n");
|
||||
|
||||
spin_lock_bh(&sc->sc_resetlock);
|
||||
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
|
||||
if (r)
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to reset hardware; reset status %d\n",
|
||||
r);
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
}
|
||||
if (npend)
|
||||
ath_print(common, ATH_DBG_FATAL, "Failed to stop TX DMA!\n");
|
||||
|
||||
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
|
||||
if (ATH_TXQ_SETUP(sc, i))
|
||||
ath_draintxq(sc, &sc->tx.txq[i], retry_tx);
|
||||
}
|
||||
|
||||
return !npend;
|
||||
}
|
||||
|
||||
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
|
||||
|
Reference in New Issue
Block a user