From owner-svn-src-user@FreeBSD.ORG Tue Oct 4 06:46:13 2011 Return-Path: Delivered-To: svn-src-user@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 40F41106566C; Tue, 4 Oct 2011 06:46:13 +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 3050B8FC15; Tue, 4 Oct 2011 06:46:13 +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 p946kDTU046209; Tue, 4 Oct 2011 06:46:13 GMT (envelope-from adrian@svn.freebsd.org) Received: (from adrian@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id p946kCCh046204; Tue, 4 Oct 2011 06:46:12 GMT (envelope-from adrian@svn.freebsd.org) Message-Id: <201110040646.p946kCCh046204@svn.freebsd.org> From: Adrian Chadd Date: Tue, 4 Oct 2011 06:46:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-user@freebsd.org X-SVN-Group: user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r225960 - user/adrian/if_ath_tx/sys/dev/ath X-BeenThere: svn-src-user@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the experimental " user" src tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 04 Oct 2011 06:46:13 -0000 Author: adrian Date: Tue Oct 4 06:46:12 2011 New Revision: 225960 URL: http://svn.freebsd.org/changeset/base/225960 Log: Begin fleshing out queue handling during reset conditions. Right now, all TX/RX queues are flushed during a reset. For non-11n and 11n in non-addba, this just shows up as packet loss. But for 11n + addba, this introduces holes in the TX/RX BAW tracking algorithm and can result in traffic actually hanging. The eventual aim here is to select whether to flush the TX/RX queue during a reset, or handle the RX frames and reschedule the incomplete TX frames. There are complications however! * If the flush is due to a stuck beacon or non-opmode config change (eg interference mitigation) then it's pretty clear, we can reschedule TX frames. * If the flush is due to a frequency change, then yes, we have to flush everything and (I -think-) re-establish association and TX/RX addba state. I'll have to go over the spec (and implementations) to see what to do. * If the flush is due to a channel width change, then we could in theory just handle all RX frames and resubmit all the TX frames for retransmission after correctly adjusting the rate control for the new width/GI. However we don't currently have a separate call for that - it's just done as a channel change. * On a reset, we need to stop the RX DMA (so the MAC doesn't ACK any further frames) before we handle what's in the queue - and we need to ensure that the PCU and PCU RX DMA isn't re-enabled. * If _any_ frames are dropped in an active aggregation session (and it's not a full reset due to a channel change) then we're going to have to TX a BAR frame. This is likely going to crop up if we're doing things like channel width management as I bet the general implementations out there expect to be able to do this without dropping the association and addba state. In addition, we can't just call ath_rx_proc() from ath_reset() as things stand because it's quite possible that ath_reset() will be called from a non- taskqueue context (ie, an ioctl via net80211, or a net80211 task/callout.) So before we can properly handle RX (and maybe TX, I haven't yet fully audited all of that) we'll need to introduce the PCU locking to ensure things don't race. Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath.c user/adrian/if_ath_tx/sys/dev/ath/if_ath_misc.h user/adrian/if_ath_tx/sys/dev/ath/if_ath_sysctl.c user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath.c ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/if_ath.c Tue Oct 4 04:13:34 2011 (r225959) +++ user/adrian/if_ath_tx/sys/dev/ath/if_ath.c Tue Oct 4 06:46:12 2011 (r225960) @@ -181,7 +181,7 @@ static void ath_tx_proc_q0(void *, int); 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 *); +static void ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type); static void ath_stoprecv(struct ath_softc *); static int ath_startrecv(struct ath_softc *); static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); @@ -1136,7 +1136,7 @@ ath_vap_delete(struct ieee80211vap *vap) * the vap state by any frames pending on the tx queues. */ ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* stop hw xmit side */ + 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 */ } @@ -1160,7 +1160,7 @@ ath_vap_delete(struct ieee80211vap *vap) * call!) */ - ath_draintxq(sc); + ath_draintxq(sc, ATH_RESET_DEFAULT); ATH_LOCK(sc); /* @@ -1563,7 +1563,7 @@ ath_fatal_proc(void *arg, int pending) state[0], state[1] , state[2], state[3], state[4], state[5]); } - ath_reset(ifp); + ath_reset(ifp, ATH_RESET_NOLOSS); } static void @@ -1623,7 +1623,7 @@ ath_bmiss_proc(void *arg, int pending) if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs); - ath_reset(ifp); + ath_reset(ifp, ATH_RESET_NOLOSS); } else ieee80211_beacon_miss(ifp->if_l2com); } @@ -1803,7 +1803,7 @@ ath_stop_locked(struct ifnet *ifp) } ath_hal_intrset(ah, 0); } - ath_draintxq(sc); + ath_draintxq(sc, ATH_RESET_DEFAULT); if (!sc->sc_invalid) { ath_stoprecv(sc); ath_hal_phydisable(ah); @@ -1831,7 +1831,7 @@ ath_stop(struct ifnet *ifp) * to reset or reload hardware state. */ int -ath_reset(struct ifnet *ifp) +ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) { struct ath_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; @@ -1841,7 +1841,12 @@ ath_reset(struct ifnet *ifp) DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* stop xmit side */ + ath_draintxq(sc, reset_type); /* stop xmit side */ + /* + * XXX Don't flush if ATH_RESET_NOLOSS;but we have to first + * XXX need to ensure this doesn't race with an outstanding + * XXX taskqueue call. + */ ath_stoprecv(sc); /* stop recv side */ ath_settkipmic(sc); /* configure TKIP MIC handling */ /* NB: indicate channel change so we do a full reset */ @@ -1894,7 +1899,8 @@ ath_reset_vap(struct ieee80211vap *vap, ath_hal_settxpowlimit(ah, ic->ic_txpowlimit); return 0; } - return ath_reset(ifp); + /* XXX? Full or NOLOSS? */ + return ath_reset(ifp, ATH_RESET_FULL); } struct ath_buf * @@ -2874,7 +2880,11 @@ ath_bstuck_proc(void *arg, int pending) sc->sc_bmisscount); sc->sc_stats.ast_bstuck++; - ath_reset(ifp); + /* + * This assumes that there's no simultaneous channel mode change + * occuring. + */ + ath_reset(ifp, ATH_RESET_NOLOSS); } /* @@ -4881,7 +4891,7 @@ ath_tx_stopdma(struct ath_softc *sc, str * Drain the transmit queues and reclaim resources. */ static void -ath_draintxq(struct ath_softc *sc) +ath_draintxq(struct ath_softc *sc, ATH_RESET_TYPE reset_type) { struct ath_hal *ah = sc->sc_ah; struct ifnet *ifp = sc->sc_ifp; @@ -5033,7 +5043,7 @@ ath_chan_set(struct ath_softc *sc, struc * the relevant bits of the h/w. */ ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc); /* clear pending tx 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 " @@ -5123,7 +5133,7 @@ ath_calibrate(void *arg) DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: rfgain change\n", __func__); sc->sc_stats.ast_per_rfgain++; - ath_reset(ifp); + ath_reset(ifp, ATH_RESET_NOLOSS); } /* * If this long cal is after an idle period, then @@ -5789,7 +5799,7 @@ ath_watchdog(void *arg) hangs & 0xff ? "bb" : "mac", hangs); } else if_printf(ifp, "device timeout\n"); - ath_reset(ifp); + ath_reset(ifp, ATH_RESET_NOLOSS); ifp->if_oerrors++; sc->sc_stats.ast_watchdog++; } Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_misc.h ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/if_ath_misc.h Tue Oct 4 04:13:34 2011 (r225959) +++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_misc.h Tue Oct 4 06:46:12 2011 (r225960) @@ -56,7 +56,7 @@ extern struct ath_buf * ath_buf_clone(st const struct ath_buf *bf); extern void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf); -extern int ath_reset(struct ifnet *); +extern int ath_reset(struct ifnet *, ATH_RESET_TYPE); extern void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq); extern void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail); Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_sysctl.c ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/if_ath_sysctl.c Tue Oct 4 04:13:34 2011 (r225959) +++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_sysctl.c Tue Oct 4 06:46:12 2011 (r225960) @@ -263,7 +263,8 @@ ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS) if (error || !req->newptr) return error; return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : - (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; + (ifp->if_drv_flags & IFF_DRV_RUNNING) ? + ath_reset(ifp, ATH_RESET_NOLOSS) : 0; } static int @@ -295,7 +296,8 @@ ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS) return 0; if (!ath_hal_setrfkill(ah, rfkill)) return EINVAL; - return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0; + return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? + ath_reset(ifp, ATH_RESET_FULL) : 0; } static int @@ -428,7 +430,7 @@ ath_sysctl_intmit(SYSCTL_HANDLER_ARGS) * things in an inconsistent state. */ if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) - ath_reset(sc->sc_ifp); + ath_reset(sc->sc_ifp, ATH_RESET_NOLOSS); return 0; } Modified: user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h ============================================================================== --- user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h Tue Oct 4 04:13:34 2011 (r225959) +++ user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h Tue Oct 4 06:46:12 2011 (r225960) @@ -332,6 +332,16 @@ struct ath_vap { struct taskqueue; struct ath_tx99; +/* + * Whether to reset the TX/RX queue with or without + * a queue flush. + */ +typedef enum { + ATH_RESET_DEFAULT = 0, + ATH_RESET_NOLOSS = 1, + ATH_RESET_FULL = 2, +} ATH_RESET_TYPE; + struct ath_softc { struct ifnet *sc_ifp; /* interface common */ struct ath_stats sc_stats; /* interface statistics */