summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath/if_ath.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ath/if_ath.c')
-rw-r--r--sys/dev/ath/if_ath.c111
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.
*/
OpenPOWER on IntegriCloud