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.
This commit is contained in:
Adrian Chadd 2011-11-19 21:05:31 +00:00
parent 6bac4c3e6a
commit 9a842e8b59
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=227740

View File

@ -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",