Index: sys/dev/ath/if_ath_misc.h =================================================================== --- sys/dev/ath/if_ath_misc.h (revision 248091) +++ sys/dev/ath/if_ath_misc.h (working copy) @@ -126,10 +126,16 @@ static inline void ath_tx_kick(struct ath_softc *sc) { + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + IEEE80211_TX_UNLOCK_ASSERT(ic); + ATH_TX_UNLOCK_ASSERT(sc); + + IEEE80211_TX_LOCK(ic); ATH_TX_LOCK(sc); ath_start(sc->sc_ifp); ATH_TX_UNLOCK(sc); + IEEE80211_TX_UNLOCK(ic); } /* Index: sys/dev/ath/if_athvar.h =================================================================== --- sys/dev/ath/if_athvar.h (revision 248091) +++ sys/dev/ath/if_athvar.h (working copy) @@ -329,6 +329,7 @@ u_int axq_intrcnt; /* interrupt count */ u_int32_t *axq_link; /* link ptr in last TX desc */ TAILQ_HEAD(axq_q_s, ath_buf) axq_q; /* transmit queue */ + struct ath_buf *axq_holdingbf; /* holding TX buffer */ char axq_name[12]; /* e.g. "ath0_txq4" */ /* Per-TID traffic queue for software -> hardware TX */ Index: sys/dev/ath/if_ath_tx.c =================================================================== --- sys/dev/ath/if_ath_tx.c (revision 248091) +++ sys/dev/ath/if_ath_tx.c (working copy) @@ -1784,6 +1784,7 @@ uint8_t type, subtype; ATH_TX_LOCK_ASSERT(sc); + IEEE80211_TX_LOCK_ASSERT(vap->iv_ic); /* * Determine the target hardware queue. @@ -1971,6 +1972,7 @@ int do_override; ATH_TX_LOCK_ASSERT(sc); + IEEE80211_TX_LOCK_ASSERT(ic); wh = mtod(m0, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); @@ -5007,8 +5009,17 @@ ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq) { struct ath_tid *tid, *next, *last; +#ifdef INVARIANTS + struct ieee80211com *ic = sc->sc_ifp->if_l2com; +#endif ATH_TX_LOCK_ASSERT(sc); + /* + * This shouldn't be directly called from the TX queue + * code path; it should be called from the TX completion + * or TX kick task. + */ + IEEE80211_TX_UNLOCK_ASSERT(ic); /* * Don't schedule if the hardware queue is busy. Index: sys/dev/ath/if_ath.c =================================================================== --- sys/dev/ath/if_ath.c (revision 248091) +++ sys/dev/ath/if_ath.c (working copy) @@ -2577,9 +2577,14 @@ ath_start_queue(struct ifnet *ifp) { struct ath_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + IEEE80211_TX_LOCK_ASSERT(ic); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_queue: start"); - ath_tx_kick(sc); + ATH_TX_LOCK(sc); + ath_start(ifp); + ATH_TX_UNLOCK(sc); ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_queue: finished"); } @@ -2587,6 +2592,7 @@ ath_start_task(void *arg, int npending) { struct ath_softc *sc = (struct ath_softc *) arg; + struct ieee80211com *ic = sc->sc_ifp->if_l2com; struct ifnet *ifp = sc->sc_ifp; ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: start"); @@ -2607,9 +2613,11 @@ sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + IEEE80211_TX_LOCK(ic); ATH_TX_LOCK(sc); ath_start(sc->sc_ifp); ATH_TX_UNLOCK(sc); + IEEE80211_TX_UNLOCK(ic); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; @@ -2621,6 +2629,9 @@ ath_start(struct ifnet *ifp) { struct ath_softc *sc = ifp->if_softc; +#ifdef INVARIANTS + struct ieee80211com *ic = ifp->if_l2com; +#endif struct ieee80211_node *ni; struct ath_buf *bf; struct mbuf *m, *next; @@ -2630,6 +2641,7 @@ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) return; + IEEE80211_TX_LOCK_ASSERT(ic); ATH_TX_LOCK_ASSERT(sc); ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start: called"); @@ -3750,39 +3762,6 @@ } /* - * Update the busy status of the last frame on the free list. - * When doing TDMA, the busy flag tracks whether the hardware - * currently points to this buffer or not, and thus gated DMA - * may restart by re-reading the last descriptor in this - * buffer. - * - * This should be called in the completion function once one - * of the buffers has been used. - */ -static void -ath_tx_update_busy(struct ath_softc *sc) -{ - struct ath_buf *last; - - /* - * Since the last frame may still be marked - * as ATH_BUF_BUSY, unmark it here before - * finishing the frame processing. - * Since we've completed a frame (aggregate - * or otherwise), the hardware has moved on - * and is no longer referencing the previous - * descriptor. - */ - ATH_TXBUF_LOCK_ASSERT(sc); - last = TAILQ_LAST(&sc->sc_txbuf_mgmt, ath_bufhead_s); - if (last != NULL) - last->bf_flags &= ~ATH_BUF_BUSY; - last = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s); - if (last != NULL) - last->bf_flags &= ~ATH_BUF_BUSY; -} - -/* * Process the completion of the given buffer. * * This calls the rate control update and then the buffer completion. @@ -3901,7 +3880,6 @@ break; } ATH_TXQ_REMOVE(txq, bf, bf_list); -#ifdef IEEE80211_SUPPORT_TDMA if (txq->axq_depth > 0) { /* * More frames follow. Mark the buffer busy @@ -3914,9 +3892,6 @@ */ bf->bf_last->bf_flags |= ATH_BUF_BUSY; } else -#else - if (txq->axq_depth == 0) -#endif txq->axq_link = NULL; if (bf->bf_state.bfs_aggr) txq->axq_aggr_depth--; @@ -4188,6 +4163,51 @@ } /* + * Free the holding buffer if it exists + */ +static void +ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq) +{ + + if (txq->axq_holdingbf == NULL) + return; + + txq->axq_holdingbf->bf_flags &= ~ATH_BUF_BUSY; + ATH_TXBUF_LOCK(sc); + ath_returnbuf_tail(sc, txq->axq_holdingbf); + ATH_TXBUF_UNLOCK(sc); + txq->axq_holdingbf = NULL; +} + +/* + * Add this buffer to the holding queue, freeing the previous + * one if it exists. + */ +static void +ath_txq_addholdingbuf(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_txq *txq; + + /* XXX assert ATH_BUF_BUSY is set */ + + /* XXX assert the tx queue is under the max number */ + if (bf->bf_state.bfs_tx_queue > HAL_NUM_TX_QUEUES) { + device_printf(sc->sc_dev, "%s: bf=%p: invalid tx queue (%d)\n", + __func__, + bf, + bf->bf_state.bfs_tx_queue); + bf->bf_flags &= ~ATH_BUF_BUSY; + ath_returnbuf_tail(sc, bf); + return; + } + + txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue]; + ath_txq_freeholdingbuf(sc, txq); + + txq->axq_holdingbf = bf; +} + +/* * Return a buffer to the pool and update the 'busy' flag on the * previous 'tail' entry. * @@ -4207,8 +4227,18 @@ KASSERT((bf->bf_node == NULL), ("%s: bf->bf_node != NULL\n", __func__)); KASSERT((bf->bf_m == NULL), ("%s: bf->bf_m != NULL\n", __func__)); + /* + * If this buffer is busy, push it onto the holding queue + */ + if (bf->bf_flags & ATH_BUF_BUSY) { + ath_txq_addholdingbuf(sc, bf); + return; + } + + /* + * Not a busy buffer, so free normally + */ ATH_TXBUF_LOCK(sc); - ath_tx_update_busy(sc); ath_returnbuf_tail(sc, bf); ATH_TXBUF_UNLOCK(sc); } @@ -4261,15 +4291,6 @@ * NB: this assumes output has been stopped and * we do not need to block ath_tx_proc */ - ATH_TXBUF_LOCK(sc); - bf = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s); - if (bf != NULL) - bf->bf_flags &= ~ATH_BUF_BUSY; - bf = TAILQ_LAST(&sc->sc_txbuf_mgmt, ath_bufhead_s); - if (bf != NULL) - bf->bf_flags &= ~ATH_BUF_BUSY; - ATH_TXBUF_UNLOCK(sc); - for (ix = 0;; ix++) { ATH_TX_LOCK(sc); bf = TAILQ_FIRST(&txq->axq_q); @@ -4331,6 +4352,11 @@ } /* + * Free the holding buffer if it exists + */ + ath_txq_freeholdingbuf(sc, txq); + + /* * Drain software queued frames which are on * active TIDs. */