From owner-svn-src-head@FreeBSD.ORG Sat Nov 19 21:05:31 2011 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D0C20106564A; Sat, 19 Nov 2011 21:05:31 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id BFACA8FC08; Sat, 19 Nov 2011 21:05:31 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id pAJL5V1f060347; Sat, 19 Nov 2011 21:05:31 GMT (envelope-from adrian@svn.freebsd.org) Received: (from adrian@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id pAJL5V9C060345; Sat, 19 Nov 2011 21:05:31 GMT (envelope-from adrian@svn.freebsd.org) Message-Id: <201111192105.pAJL5V9C060345@svn.freebsd.org> From: Adrian Chadd Date: Sat, 19 Nov 2011 21:05:31 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r227740 - head/sys/dev/ath X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 19 Nov 2011 21:05:31 -0000 Author: adrian Date: Sat Nov 19 21:05:31 2011 New Revision: 227740 URL: http://svn.freebsd.org/changeset/base/227740 Log: 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. Modified: head/sys/dev/ath/if_ath.c Modified: head/sys/dev/ath/if_ath.c ============================================================================== --- head/sys/dev/ath/if_ath.c Sat Nov 19 19:25:57 2011 (r227739) +++ head/sys/dev/ath/if_ath.c Sat Nov 19 21:05:31 2011 (r227740) @@ -188,7 +188,7 @@ static void ath_tx_proc_q0123(void *, in 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_T 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_T * 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_R * 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, struc #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",