svn commit: r226515 - user/adrian/if_ath_tx/sys/dev/ath

Adrian Chadd adrian at FreeBSD.org
Tue Oct 18 15:38:00 UTC 2011


Author: adrian
Date: Tue Oct 18 15:37:59 2011
New Revision: 226515
URL: http://svn.freebsd.org/changeset/base/226515

Log:
  Begin fleshing out the "Don't drop frames during an interface reset" code.
  
  * Add sc->sc_in_reset, which is protected by the ATH_LOCK and is >0 when
    the interface is being reset.
  * Don't call draintxq on a recoverable interface reset - ie, one where
    there's no operational state change. If a channel change is needed,
    or a channel width change occurs, then yes frames are dropped for now.
    But if a stuck beacon occurs, just process the frames in the queue
    and queue subsequent frames being sent to the hardware.
    Instead, just process the queue normally via ath_tx_processq(), but
    don't reschedule the software TXQs.
  * Add a new function - ath_tx_restart_hw() - which restarts all currently
    configured hardware queues. This takes care of clearing the
    ATH_TXQ_PUTPENDING flag, reprograms the TXQ pointer, fixes up the axq_link
    pointer and finally tickles the TXQ enable bit.
  
  I don't quite like this solution - I'd prefer to have some local flag
  which is propagated down from ath_tx_processq() down to the completion
  callbacks, to ensure that nothing enqueues a frame to the hardware.
  
  In the ADDBA case, nothing should be going directly to the hardware -
  any software retried frame is pushed onto the head of the software TXQ
  and then retried when the TXQ is next scheduled.
  
  In the non-ADDBA case, frames aren't retried.
  
  The trouble is that there may be some callbacks invoked via net80211
  which immediately tries scheduling another frame. Since the hardware
  is currently being reset, no new frame should be queued. But since
  ATH_LOCK is being held (and is recursive, sigh), it'll just be grabbed
  again and happily continue along its merry way. This way, frames are
  TX'ed but they won't hit the hardware queue until the reset path
  completes.
  
  Any TX'ing from other contexts will block on the ATH_LOCK.
  
  TODO:
  
  * Check that the node software TXQ's are being rescheduled correctly!
    I bet they're not.
  
  Note:
  
   In order to correctly handle ADDBA session TX, frames that are dropped
   due an interface reset must have a BAR TX'ed for the session. That still
   isn't being done.

Modified:
  user/adrian/if_ath_tx/sys/dev/ath/if_ath.c
  user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c
  user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.h
  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 18 15:25:43 2011	(r226514)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath.c	Tue Oct 18 15:37:59 2011	(r226515)
@@ -1850,8 +1850,10 @@ ath_reset_locked(struct ifnet *ifp, ATH_
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct ath_hal *ah = sc->sc_ah;
 	HAL_STATUS status;
+	int i;
 
 	ATH_LOCK_ASSERT(sc);
+	sc->sc_in_reset++;
 
 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__);
 
@@ -1888,7 +1890,17 @@ ath_reset_locked(struct ifnet *ifp, ATH_
 	}
 	ath_hal_intrset(ah, sc->sc_imask);
 
-	ath_start_locked(ifp);			/* restart xmit */
+	/* Restart TX if needed */
+	if (reset_type == ATH_RESET_NOLOSS)
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+			if (ATH_TXQ_SETUP(sc, i)) {
+				ATH_TXQ_LOCK(&sc->sc_txq[i]);
+				ath_tx_restart_hw(sc, &sc->sc_txq[i]);
+				ATH_TXQ_UNLOCK(&sc->sc_txq[i]);
+			}
+
+	sc->sc_in_reset--;
+	ath_start_locked(ifp);			/* restart netif xmit */
 	return 0;
 }
 
@@ -4939,9 +4951,21 @@ ath_draintxq(struct ath_softc *sc, ATH_R
 	struct ifnet *ifp = sc->sc_ifp;
 	int i;
 
+	ATH_LOCK_ASSERT(sc);
+
 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
-		if (ATH_TXQ_SETUP(sc, i))
-			ath_tx_draintxq(sc, &sc->sc_txq[i]);
+		/*
+		 * If the reset type is ATH_RESET_NOLOSS, don't
+		 * reschedule any further TXQ activity.
+		 *
+		 * XXX TODO: suspend TX operations during reset.
+		 */
+		if (ATH_TXQ_SETUP(sc, i)) {
+			if (reset_type == ATH_RESET_NOLOSS)
+				ath_tx_processq(sc, &sc->sc_txq[i], 0);
+			else
+				ath_tx_draintxq(sc, &sc->sc_txq[i]);
+		}
 #ifdef ATH_DEBUG
 	if (sc->sc_debug & ATH_DEBUG_RESET) {
 		struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf);
@@ -4955,8 +4979,10 @@ ath_draintxq(struct ath_softc *sc, ATH_R
 		}
 	}
 #endif /* ATH_DEBUG */
-	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-	sc->sc_wd_timer = 0;
+	if (reset_type != ATH_RESET_NOLOSS) {
+		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+		sc->sc_wd_timer = 0;
+	}
 }
 
 /*

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c	Tue Oct 18 15:25:43 2011	(r226514)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.c	Tue Oct 18 15:37:59 2011	(r226515)
@@ -477,10 +477,35 @@ ath_tx_handoff_mcast(struct ath_softc *s
 	txq->axq_link = &bf->bf_lastds->ds_link;
 }
 
+/*
+ * Restart TX DMA for the given TXQ.
+ *
+ * This must be called whether the queue is empty or not.
+ */
+void
+ath_tx_restart_hw(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf;
 
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	/* This is always going to be cleared, empty or not */
+	txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
+
+	bf = TAILQ_FIRST(&txq->axq_q);
+	if (bf == NULL)
+		return;
+
+	ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+	txq->axq_link = &bf->bf_lastds->ds_link;
+	ath_hal_txstart(ah, txq->axq_qnum);
+}
 
 /*
  * Hand-off packet to a hardware queue.
+ *
+ * Just queue the frame if the hardware is in a reset state.
  */
 static void
 ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
@@ -501,6 +526,28 @@ ath_tx_handoff_hw(struct ath_softc *sc, 
 	KASSERT(txq->axq_qnum != ATH_TXQ_SWQ,
 	     ("ath_tx_handoff_hw called for mcast queue"));
 
+	ATH_LOCK_ASSERT(sc);
+
+	if (sc->sc_in_reset) {
+		DPRINTF(sc, ATH_DEBUG_RESET,
+		    "%s: called with sc_in_reset != 0\n",
+		    __func__);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: queued: TXDP[%u] = %p (%p) depth %d\n",
+		    __func__, txq->axq_qnum,
+		    (caddr_t)bf->bf_daddr, bf->bf_desc,
+		    txq->axq_depth);
+		ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+		if (bf->bf_state.bfs_aggr)
+			txq->axq_aggr_depth++;
+		/*
+		 * There's no need to update axq_link; the hardware
+		 * is in reset and once the reset is complete, any
+		 * non-empty queues will simply have DMA restarted.
+		 */
+		return;
+	}
+
 	/* For now, so not to generate whitespace diffs */
 	if (1) {
 #ifdef IEEE80211_SUPPORT_TDMA

Modified: user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.h
==============================================================================
--- user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.h	Tue Oct 18 15:25:43 2011	(r226514)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_ath_tx.h	Tue Oct 18 15:37:59 2011	(r226515)
@@ -79,6 +79,7 @@
 #define	BAW_WITHIN(_start, _bawsz, _seqno)	\
 	    ((((_seqno) - (_start)) & 4095) < (_bawsz))
 
+extern void ath_tx_restart_hw(struct ath_softc *sc, struct ath_txq *txq);
 extern void ath_freetx(struct mbuf *m);
 extern void ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an);
 extern void ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq);

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 18 15:25:43 2011	(r226514)
+++ user/adrian/if_ath_tx/sys/dev/ath/if_athvar.h	Tue Oct 18 15:37:59 2011	(r226515)
@@ -435,6 +435,22 @@ struct ath_softc {
 	uint32_t		sc_txq_active;	/* bitmap of active TXQs */
 	uint32_t		sc_kickpcu;	/* whether to kick the PCU */
 
+	/*
+	 * These are also currently protected by the ATH_LOCK and
+	 * aren't atomics.
+	 *
+	 * These indicate whether any of the below tasks are currently
+	 * running. If, for example, we are in the middle of a reset
+	 * call then TX/RX must be suspended until it completes.
+	 */
+	uint32_t		sc_in_reset;	/* whether in a reset */
+#ifdef	notyet
+	uint32_t		sc_in_intr;	/* whether in ath_intr */
+	uint32_t		sc_in_rxtask;	/* whether in rxtask */
+	uint32_t		sc_in_xmit;	/* whether in ath_start / ath_raw_xmit */
+	uint32_t		sc_in_txtask;	/* whether in ath_tx_proc* */
+#endif
+
 	u_int			sc_keymax;	/* size of key cache */
 	u_int8_t		sc_keymap[ATH_KEYBYTES];/* key use bit map */
 


More information about the svn-src-user mailing list