Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Oct 2011 06:46:12 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r225960 - user/adrian/if_ath_tx/sys/dev/ath
Message-ID:  <201110040646.p946kCCh046204@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201110040646.p946kCCh046204>