Index: sys/dev/ath/if_ath_tx_edma.c =================================================================== --- sys/dev/ath/if_ath_tx_edma.c (revision 248564) +++ sys/dev/ath/if_ath_tx_edma.c (working copy) @@ -142,7 +142,7 @@ struct ath_buf *bf; int i = 0; - ATH_TX_LOCK_ASSERT(sc); + ATH_TXQ_LOCK_ASSERT(txq); DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called\n", __func__); @@ -181,9 +181,8 @@ txq, txq->axq_qnum); - ATH_TX_LOCK_ASSERT(sc); + ATH_TXQ_LOCK_ASSERT(txq); ath_edma_tx_fifo_fill(sc, txq); - } /* @@ -204,7 +203,7 @@ { struct ath_hal *ah = sc->sc_ah; - ATH_TX_LOCK_ASSERT(sc); + ATH_TXQ_LOCK_ASSERT(txq); KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, ("%s: busy status 0x%x", __func__, bf->bf_flags)); @@ -249,7 +248,7 @@ struct ath_buf *bf) { - ATH_TX_LOCK_ASSERT(sc); + ATH_TXQ_LOCK_ASSERT(txq); KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, ("%s: busy status 0x%x", __func__, bf->bf_flags)); @@ -303,8 +302,6 @@ struct ath_buf *bf) { - ATH_TX_LOCK_ASSERT(sc); - DPRINTF(sc, ATH_DEBUG_XMIT_DESC, "%s: called; bf=%p, txq=%p, qnum=%d\n", __func__, @@ -526,7 +523,7 @@ txq = &sc->sc_txq[ts.ts_queue_id]; - ATH_TX_LOCK(sc); + ATH_TXQ_LOCK(txq); bf = TAILQ_FIRST(&txq->axq_q); DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: qcuid=%d, bf=%p\n", @@ -554,7 +551,7 @@ txq->axq_aggr_depth--; txq->axq_fifo_depth --; /* XXX assert FIFO depth >= 0 */ - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); /* * First we need to make sure ts_rate is valid. @@ -636,11 +633,11 @@ * to begin validating that things are somewhat * working. */ - ATH_TX_LOCK(sc); + ATH_TXQ_LOCK(txq); if (dosched && txq->axq_fifo_depth == 0) { ath_edma_tx_fifo_fill(sc, txq); } - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); } sc->sc_wd_timer = 0; Index: sys/dev/ath/if_ath_beacon.c =================================================================== --- sys/dev/ath/if_ath_beacon.c (revision 248564) +++ sys/dev/ath/if_ath_beacon.c (working copy) @@ -478,6 +478,12 @@ sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; } + /* Program the CABQ with the contents of the CABQ txq and start it */ + ATH_TXQ_LOCK(sc->sc_cabq); + ath_beacon_cabq_start(sc); + ATH_TXQ_UNLOCK(sc->sc_cabq); + + /* Program the new beacon frame if we have one for this interval */ if (bfaddr != 0) { /* * Stop any current dma and put the new frame on the queue. @@ -500,6 +506,33 @@ } } +/* + * Start CABQ transmission - this assumes that all frames are prepped + * and ready in the CABQ. + * + * XXX TODO: methodize this; for the EDMA case it should only push + * into the hardware if the FIFO isn't full _AND_ then it should + * tag the final buffer in the queue as ATH_BUF_FIFOEND so the FIFO + * depth is correctly accounted for. + */ +void +ath_beacon_cabq_start(struct ath_softc *sc) +{ + struct ath_buf *bf; + struct ath_txq *cabq = sc->sc_cabq; + + ATH_TXQ_LOCK_ASSERT(cabq); + if (TAILQ_EMPTY(&cabq->axq_q)) + return; + bf = TAILQ_FIRST(&cabq->axq_q); + + /* Push the first entry into the hardware */ + ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); + + /* NB: gated by beacon so safe to start here */ + ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); +} + struct ath_buf * ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) { @@ -561,38 +594,43 @@ * insure cab frames are triggered by this beacon. */ if (avp->av_boff.bo_tim[4] & 1) { - struct ath_hal *ah = sc->sc_ah; /* NB: only at DTIM */ - ATH_TX_LOCK(sc); + ATH_TXQ_LOCK(&avp->av_mcastq); if (nmcastq) { struct ath_buf *bfm; /* * Move frames from the s/w mcast q to the h/w cab q. - * XXX TODO: walk the list, update MORE_DATA bit - * XXX TODO: or maybe, set the MORE data bit in the - * TX descriptor(s) here? * - * XXX TODO: we're still pushing a CABQ frame list to - * AR9380 hosts; but we don't (yet) populate - * the ATH_BUF_BUSY flag in the EDMA - * completion task (for CABQ, though!) + * XXX TODO: This should be methodized - the EDMA + * CABQ setup code may look different! + * + * XXX TODO: if we chain together multiple VAPs + * worth of CABQ traffic, should we keep the + * MORE data bit set on the last frame of each + * intermediary VAP (ie, only clear the MORE + * bit of the last frame on the last vap?) + * + * XXX TODO: once we append this, what happens + * to cabq->axq_link? It'll point at the avp + * mcastq link pointer, so things should be OK. + * Just double-check this is what actually happens. */ bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q); - if (cabq->axq_link != NULL) { + ATH_TXQ_LOCK(cabq); + if (cabq->axq_link != NULL) *cabq->axq_link = bfm->bf_daddr; - } else - ath_hal_puttxbuf(ah, cabq->axq_qnum, - bfm->bf_daddr); ath_txqmove(cabq, &avp->av_mcastq); - + ATH_TXQ_UNLOCK(cabq); + /* + * XXX not entirely accurate, in case a mcast + * queue frame arrived before we grabbed the TX + * lock. + */ sc->sc_stats.ast_cabq_xmit += nmcastq; } - /* NB: gated by beacon so safe to start here */ - if (! TAILQ_EMPTY(&(cabq->axq_q))) - ath_hal_txstart(ah, cabq->axq_qnum); - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(&avp->av_mcastq); } return bf; } Index: sys/dev/ath/if_ath_beacon.h =================================================================== --- sys/dev/ath/if_ath_beacon.h (revision 248564) +++ sys/dev/ath/if_ath_beacon.h (working copy) @@ -39,6 +39,7 @@ struct ieee80211vap *vap); extern struct ath_buf * ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap); +extern void ath_beacon_cabq_start(struct ath_softc *sc); extern int ath_wme_update(struct ieee80211com *ic); extern void ath_beacon_update(struct ieee80211vap *vap, int item); extern void ath_beacon_start_adhoc(struct ath_softc *sc, Index: sys/dev/ath/if_ath_tdma.c =================================================================== --- sys/dev/ath/if_ath_tdma.c (revision 248564) +++ sys/dev/ath/if_ath_tdma.c (working copy) @@ -612,13 +612,19 @@ } bf = ath_beacon_generate(sc, vap); + /* XXX We don't do cabq traffic, but just for completeness .. */ + ATH_TXQ_LOCK(sc->sc_cabq); + ath_beacon_cabq_start(sc); + ATH_TXQ_UNLOCK(sc->sc_cabq); + if (bf != NULL) { /* * Stop any current dma and put the new frame on the queue. * This should never fail since we check above that no frames * are still pending on the queue. */ - if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { + if ((! sc->sc_isedma) && + (! ath_hal_stoptxdma(ah, sc->sc_bhalq))) { DPRINTF(sc, ATH_DEBUG_ANY, "%s: beacon queue %u did not stop?\n", __func__, sc->sc_bhalq); Index: sys/dev/ath/if_athvar.h =================================================================== --- sys/dev/ath/if_athvar.h (revision 248564) +++ sys/dev/ath/if_athvar.h (working copy) @@ -329,9 +329,11 @@ 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 mtx axq_lock; /* lock on q and link */ + /* * XXX the holdingbf field is protected by the TXBUF lock - * for now, NOT the TX lock. + * for now, NOT the TXQ lock. * * Architecturally, it would likely be better to move * the holdingbf field to a separate array in ath_softc @@ -342,9 +344,24 @@ char axq_name[12]; /* e.g. "ath0_txq4" */ /* Per-TID traffic queue for software -> hardware TX */ + /* + * This is protected by the general TX path lock, not (for now) + * by the TXQ lock. + */ TAILQ_HEAD(axq_t_s,ath_tid) axq_tidq; }; +#define ATH_TXQ_LOCK_INIT(_sc, _tq) do { \ + snprintf((_tq)->axq_name, sizeof((_tq)->axq_name), "%s_txq%u", \ + device_get_nameunit((_sc)->sc_dev), (_tq)->axq_qnum); \ + mtx_init(&(_tq)->axq_lock, (_tq)->axq_name, NULL, MTX_DEF); \ + } while (0) +#define ATH_TXQ_LOCK_DESTROY(_tq) mtx_destroy(&(_tq)->axq_lock) +#define ATH_TXQ_LOCK(_tq) mtx_lock(&(_tq)->axq_lock) +#define ATH_TXQ_UNLOCK(_tq) mtx_unlock(&(_tq)->axq_lock) +#define ATH_TXQ_LOCK_ASSERT(_tq) mtx_assert(&(_tq)->axq_lock, MA_OWNED) + + #define ATH_NODE_LOCK(_an) mtx_lock(&(_an)->an_mtx) #define ATH_NODE_UNLOCK(_an) mtx_unlock(&(_an)->an_mtx) #define ATH_NODE_LOCK_ASSERT(_an) mtx_assert(&(_an)->an_mtx, MA_OWNED) Index: sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c =================================================================== --- sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c (revision 248564) +++ sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c (working copy) @@ -554,6 +554,10 @@ HALASSERT((OS_REG_READ(ah, AR_Q_TXD) & (1 << q)) == 0); OS_REG_WRITE(ah, AR_Q_TXE, 1 << q); + + /* XXX flush? */ + (void) OS_REG_READ(ah, AR_Q_TXE); + return AH_TRUE; } Index: sys/dev/ath/if_ath_tx.c =================================================================== --- sys/dev/ath/if_ath_tx.c (revision 248564) +++ sys/dev/ath/if_ath_tx.c (working copy) @@ -715,8 +715,10 @@ /* link descriptor */ *txq->axq_link = bf->bf_daddr; } + ATH_TXQ_LOCK(txq); ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); ath_hal_gettxdesclinkptr(sc->sc_ah, bf->bf_lastds, &txq->axq_link); + ATH_TXQ_UNLOCK(txq); } /* @@ -774,6 +776,7 @@ /* For now, so not to generate whitespace diffs */ if (1) { + ATH_TXQ_LOCK(txq); #ifdef IEEE80211_SUPPORT_TDMA int qbusy; @@ -899,6 +902,7 @@ txq->axq_aggr_depth++; ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link); ath_hal_txstart(ah, txq->axq_qnum); + ATH_TXQ_UNLOCK(txq); ATH_KTR(sc, ATH_KTR_TX, 1, "ath_tx_handoff: txq=%u, txstart", txq->axq_qnum); } @@ -915,8 +919,7 @@ struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf, *bf_last; - ATH_TX_LOCK_ASSERT(sc); - + ATH_TXQ_LOCK_ASSERT(txq); /* This is always going to be cleared, empty or not */ txq->axq_flags &= ~ATH_TXQ_PUTPENDING; @@ -3380,6 +3383,11 @@ __func__, SEQNO(bf->bf_state.bfs_seqno)); #endif } + + /* Strip it out of an aggregate list if it was in one */ + bf->bf_next = NULL; + + /* Insert on the free queue to be freed by the caller */ TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); } Index: sys/dev/ath/if_ath.c =================================================================== --- sys/dev/ath/if_ath.c (revision 248564) +++ sys/dev/ath/if_ath.c (working copy) @@ -2364,14 +2364,17 @@ /* Restart TX completion and pending TX */ if (reset_type == ATH_RESET_NOLOSS) { - ATH_TX_LOCK(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { + ATH_TXQ_LOCK(&sc->sc_txq[i]); ath_txq_restart_dma(sc, &sc->sc_txq[i]); + ATH_TXQ_UNLOCK(&sc->sc_txq[i]); + + ATH_TX_LOCK(sc); ath_txq_sched(sc, &sc->sc_txq[i]); + ATH_TX_UNLOCK(sc); } } - ATH_TX_UNLOCK(sc); } /* @@ -2922,6 +2925,9 @@ ath_txqmove(struct ath_txq *dst, struct ath_txq *src) { + ATH_TXQ_LOCK_ASSERT(src); + ATH_TXQ_LOCK_ASSERT(dst); + TAILQ_CONCAT(&dst->axq_q, &src->axq_q, bf_list); dst->axq_link = src->axq_link; src->axq_link = NULL; @@ -3401,6 +3407,7 @@ txq->axq_softc = sc; TAILQ_INIT(&txq->axq_q); TAILQ_INIT(&txq->axq_tidq); + ATH_TXQ_LOCK_INIT(sc, txq); } /* @@ -3585,6 +3592,7 @@ ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum); sc->sc_txqsetup &= ~(1<axq_qnum); + ATH_TXQ_LOCK_DESTROY(txq); } /* @@ -3837,11 +3845,11 @@ nacked = 0; for (;;) { - ATH_TX_LOCK(sc); + ATH_TXQ_LOCK(txq); txq->axq_intrcnt = 0; /* reset periodic desc intr count */ bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); break; } ds = bf->bf_lastds; /* XXX must be setup correctly! */ @@ -3869,7 +3877,7 @@ ATH_KTR(sc, ATH_KTR_TXCOMP, 3, "ath_tx_processq: txq=%u, bf=%p ds=%p, HAL_EINPROGRESS", txq->axq_qnum, bf, ds); - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); break; } ATH_TXQ_REMOVE(txq, bf, bf_list); @@ -3906,7 +3914,7 @@ ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi, ts->ts_rssi); } - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); /* * Update statistics and call completion @@ -4286,7 +4294,7 @@ * we do not need to block ath_tx_proc */ for (ix = 0;; ix++) { - ATH_TX_LOCK(sc); + ATH_TXQ_LOCK(txq); bf = TAILQ_FIRST(&txq->axq_q); if (bf == NULL) { txq->axq_link = NULL; @@ -4301,7 +4309,7 @@ * very fruity very quickly. */ txq->axq_fifo_depth = 0; - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); break; } ATH_TXQ_REMOVE(txq, bf, bf_list); @@ -4337,7 +4345,7 @@ * Clear ATH_BUF_BUSY; the completion handler * will free the buffer. */ - ATH_TX_UNLOCK(sc); + ATH_TXQ_UNLOCK(txq); bf->bf_flags &= ~ATH_BUF_BUSY; if (bf->bf_comp) bf->bf_comp(sc, bf, 1);