Date: Tue, 18 Oct 2011 15:37:59 +0000 (UTC) From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r226515 - user/adrian/if_ath_tx/sys/dev/ath Message-ID: <201110181537.p9IFbxnZ079695@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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 */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201110181537.p9IFbxnZ079695>