summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2013-06-03 19:39:37 +0000
committeradrian <adrian@FreeBSD.org>2013-06-03 19:39:37 +0000
commitbcac12846249a47042ba778ea5db26302c3dde87 (patch)
tree194f83893011c2edf3fa91ad8c8c67ef4c8f2a7c /sys/dev
parent2c97b7db0caff3aa17c5f075ac1d9f66e06d98da (diff)
downloadFreeBSD-src-bcac12846249a47042ba778ea5db26302c3dde87.zip
FreeBSD-src-bcac12846249a47042ba778ea5db26302c3dde87.tar.gz
Fix the order of TX shutdown and reset.
* Grab the reset lock first, so any subsequent interrupt, TX, RX work will fail * Then shut down interrupts * Then wait for TX/RX to finish running At this point no further work will be running, so it's safe to do the reset path code. PR: kern/179232
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ath/if_ath.c29
1 files changed, 25 insertions, 4 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index ec96063..ecd5ca1 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -2328,12 +2328,27 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type)
taskqueue_block(sc->sc_tq);
ATH_PCU_LOCK(sc);
- ath_hal_intrset(ah, 0); /* disable interrupts */
- ath_txrx_stop_locked(sc); /* Ensure TX/RX is stopped */
+
+ /*
+ * Grab the reset lock before TX/RX is stopped.
+ *
+ * This is needed to ensure that when the TX/RX actually does finish,
+ * no further TX/RX/reset runs in parallel with this.
+ */
if (ath_reset_grablock(sc, 1) == 0) {
device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n",
__func__);
}
+
+ /* disable interrupts */
+ ath_hal_intrset(ah, 0);
+
+ /*
+ * Now, ensure that any in progress TX/RX completes before we
+ * continue.
+ */
+ ath_txrx_stop_locked(sc);
+
ATH_PCU_UNLOCK(sc);
/*
@@ -4871,12 +4886,18 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
taskqueue_block(sc->sc_tq);
ATH_PCU_LOCK(sc);
- ath_hal_intrset(ah, 0); /* Stop new RX/TX completion */
- ath_txrx_stop_locked(sc); /* Stop pending RX/TX completion */
+
+ /* Stop new RX/TX/interrupt completion */
if (ath_reset_grablock(sc, 1) == 0) {
device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n",
__func__);
}
+
+ ath_hal_intrset(ah, 0);
+
+ /* Stop pending RX/TX completion */
+ ath_txrx_stop_locked(sc);
+
ATH_PCU_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n",
OpenPOWER on IntegriCloud