summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2011-11-19 21:05:31 +0000
committeradrian <adrian@FreeBSD.org>2011-11-19 21:05:31 +0000
commit3a882beede34079da891acd627c3a5b1374e9c33 (patch)
tree21d87fae8150b0dd7246fa8adb75d3705d4cbbb5
parentcb931ce6939d6d325cdae98a5edf0c0784771bfd (diff)
downloadFreeBSD-src-3a882beede34079da891acd627c3a5b1374e9c33.zip
FreeBSD-src-3a882beede34079da891acd627c3a5b1374e9c33.tar.gz
Begin breaking apart the receive setup/stop path in preparation for more
"correct" handling of frames in the RX pending queue during interface transitions. * ath_stoprecv() doesn't blank out the descriptor list - that's what ath_startrecv() does. So, change a comment to reflect that. * ath_stoprecv() does include a large (3ms) delay to let pending DMA complete. However, I'm under the impression that the stopdma hal method does check for a bit in the PCU to indicate DMA has stopped. So, to help with fast abort and restart, modify ath_stoprecv() to take a flag which indicates whether this is needed. * Modify the uses of ath_stoprecv() to pass in a flag to support the existing behaviour (ie, do the delay.) * Remove some duplicate PCU teardown code (which wasn't shutting down DMA, so it wasn't entirely correct..) and replace it with a call to ath_stoprecv(sc, 0) - which disables the DELAY call. The upshoot of this is now channel change doesn't simply drop completed frames on the floor, but instead it cleanly handles those frames. It still discards pending TX frames in the software and hardware queues as there's no (current) logic which forcibly recalculates the rate control information (or whether they're appropriate to be on the TX queue after a channel change), that'll come later. This still doesn't stop all the sources of queue stalls but it does tidy up some of the code duplication. To be complete, queue stalls now occur during normal behaviour - they only occur after some kind of broken behaviour causes an interface or node flush, upsetting the TX/RX BAW. Subsequent commits will incrementally fix these and other related issues. Sponsored by: Hobnob, Inc.
-rw-r--r--sys/dev/ath/if_ath.c40
1 files changed, 20 insertions, 20 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 5d4d0ba..f474daa 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -188,7 +188,7 @@ static void ath_tx_proc_q0123(void *, int);
static void ath_tx_proc(void *, int);
static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
static void ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type);
-static void ath_stoprecv(struct ath_softc *);
+static void ath_stoprecv(struct ath_softc *, int);
static int ath_startrecv(struct ath_softc *);
static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
static void ath_scan_start(struct ieee80211com *);
@@ -1153,7 +1153,7 @@ ath_vap_delete(struct ieee80211vap *vap)
ath_hal_intrset(ah, 0); /* disable interrupts */
ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */
/* XXX Do all frames from all vaps/nodes need draining here? */
- ath_stoprecv(sc); /* stop recv side */
+ ath_stoprecv(sc, 1); /* stop recv side */
}
ieee80211_vap_detach(vap);
@@ -1849,7 +1849,7 @@ ath_stop_locked(struct ifnet *ifp)
}
ath_draintxq(sc, ATH_RESET_DEFAULT);
if (!sc->sc_invalid) {
- ath_stoprecv(sc);
+ ath_stoprecv(sc, 1);
ath_hal_phydisable(ah);
} else
sc->sc_rxlink = NULL;
@@ -1943,11 +1943,11 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type)
ATH_PCU_UNLOCK(sc);
/*
- * XXX should now wait for pending TX/RX to complete
- * and block future ones from occuring.
+ * Should now wait for pending TX/RX to complete
+ * and block future ones from occuring. This needs to be
+ * done before the TX queue is drained.
*/
ath_txrx_stop(sc);
-
ath_draintxq(sc, reset_type); /* stop xmit side */
/*
@@ -1955,19 +1955,9 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type)
* not, stop the PCU and handle what's in the RX queue.
* That way frames aren't dropped which shouldn't be.
*/
- ath_hal_stoppcurecv(ah);
- ath_hal_setrxfilter(ah, 0);
+ ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS));
ath_rx_proc(sc, 0);
- /*
- * If we're not doing a noloss reset, now call ath_stoprecv().
- * This fully stops all of the RX machinery and flushes whatever
- * frames are in the RX ring buffer. Hopefully all completed
- * frames have been handled at this point.
- */
- if (reset_type != ATH_RESET_NOLOSS)
- ath_stoprecv(sc); /* stop recv side */
-
ath_settkipmic(sc); /* configure TKIP MIC handling */
/* NB: indicate channel change so we do a full reset */
if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, &status))
@@ -5125,7 +5115,7 @@ ath_draintxq(struct ath_softc *sc, ATH_RESET_TYPE reset_type)
* Disable the receive h/w in preparation for a reset.
*/
static void
-ath_stoprecv(struct ath_softc *sc)
+ath_stoprecv(struct ath_softc *sc, int dodelay)
{
#define PA2DESC(_sc, _pa) \
((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
@@ -5135,7 +5125,8 @@ ath_stoprecv(struct ath_softc *sc)
ath_hal_stoppcurecv(ah); /* disable PCU */
ath_hal_setrxfilter(ah, 0); /* clear recv filter */
ath_hal_stopdmarecv(ah); /* disable DMA engine */
- DELAY(3000); /* 3ms is long enough for 1 frame */
+ if (dodelay)
+ DELAY(3000); /* 3ms is long enough for 1 frame */
#ifdef ATH_DEBUG
if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
struct ath_buf *bf;
@@ -5253,8 +5244,17 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
#if 0
ath_hal_intrset(ah, 0); /* disable interrupts */
#endif
+ ath_stoprecv(sc, 1); /* turn off frame recv */
+ /*
+ * First, handle completed TX/RX frames.
+ */
+ ath_rx_proc(sc, 0);
+ ath_draintxq(sc, ATH_RESET_NOLOSS);
+ /*
+ * Next, flush the non-scheduled frames.
+ */
ath_draintxq(sc, ATH_RESET_FULL); /* clear pending tx frames */
- ath_stoprecv(sc); /* turn off frame recv */
+
if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) {
if_printf(ifp, "%s: unable to reset "
"channel %u (%u MHz, flags 0x%x), hal status %u\n",
OpenPOWER on IntegriCloud