diff options
Diffstat (limited to 'sys/dev/ath/if_ath.c')
-rw-r--r-- | sys/dev/ath/if_ath.c | 111 |
1 files changed, 64 insertions, 47 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 11ecd1a..7601f2f 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -3750,39 +3750,6 @@ ath_tx_update_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni, } /* - * 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 +3868,6 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) 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 +3880,6 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched) */ 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 +4151,50 @@ ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf) } /* + * Free the holding buffer if it exists + */ +static void +ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq) +{ + ATH_TXBUF_LOCK_ASSERT(sc); + + if (txq->axq_holdingbf == NULL) + return; + + txq->axq_holdingbf->bf_flags &= ~ATH_BUF_BUSY; + ath_returnbuf_tail(sc, txq->axq_holdingbf); + 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; + + ATH_TXBUF_LOCK_ASSERT(sc); + + /* 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 +4214,20 @@ ath_freebuf(struct ath_softc *sc, struct ath_buf *bf) 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_TXBUF_LOCK(sc); + ath_txq_addholdingbuf(sc, bf); + ATH_TXBUF_UNLOCK(sc); + 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 +4280,6 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) * 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 +4341,13 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) } /* + * Free the holding buffer if it exists + */ + ATH_TXBUF_LOCK(sc); + ath_txq_freeholdingbuf(sc, txq); + ATH_TXBUF_UNLOCK(sc); + + /* * Drain software queued frames which are on * active TIDs. */ |