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