#endif
}
+static void
+ath_txq_dump(struct ath_softc *sc, struct ath_txq *txq)
+{
+ int j;
+ struct ath_buf *bf;
+
+ DPRINTF(sc, ATH_DEBUG_WATCHDOG,
+ "txq:%p : axq_qnum:%u axq_depth:%d axq_link:%p TXDP:%08x\n",
+ txq, txq->axq_qnum, txq->axq_depth, txq->axq_link,
+ ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum));
+
+ j = 0;
+ STAILQ_FOREACH(bf, &txq->axq_q, bf_list) {
+ DPRINTF(sc, ATH_DEBUG_WATCHDOG,
+ " [%3u] bf_daddr:%08x ds_link:%08x ds_hw3:%08x\n",
+ j++,
+ bf->bf_daddr, bf->bf_desc->ds_link,
+ bf->bf_desc->ds_hw[3]);
+ }
+}
+
+/* Check TXDP (HW queue head) and SW queue head */
+
+static void
+ath_txq_check(struct ath_softc *sc, struct ath_txq *txq)
+{
+ struct ath_hal * ah = sc->sc_ah;
+ struct ath_buf *bf;
+ u_int32_t txdp;
+ int sw_head_printed = 0;
+ int hw_head_printed = 0;
+
+ txdp = ath_hal_gettxbuf(ah, txq->axq_qnum);
+
+ STAILQ_FOREACH(bf, &txq->axq_q, bf_list) {
+ if (!sw_head_printed)
+ sw_head_printed = 1;
+ if (!hw_head_printed && txdp == bf->bf_daddr)
+ hw_head_printed = 1;
+ }
+
+ if (sw_head_printed && !hw_head_printed) {
+ DPRINTF(sc, ATH_DEBUG_WATCHDOG,
+ "Q:%u BUG TXDP:%08x not in queue (%d elements)\n",
+ txq->axq_qnum, txdp, txq->axq_depth);
+ }
+}
+
/*
* Insert a buffer on a txq
*/
goto bf_fail;
}
+ /* We make sure we don't remove the TX descriptor on
+ * which the HW is pointing since it contains the
+ * ds_link field, except if this is the last TX
+ * descriptor in the queue */
+
+ if ((txq->axq_depth > 1) &&
+ (bf->bf_daddr == ath_hal_gettxbuf(ah, txq->axq_qnum))) {
+ ATH_TXQ_UNLOCK_IRQ_EARLY(txq);
+ goto bf_fail;
+ }
+
ATH_TXQ_REMOVE_HEAD(txq, bf_list);
if (txq->axq_depth <= 0)
txq->axq_link = NULL;
ath_tx_timeout(struct net_device *dev)
{
struct ath_softc *sc = dev->priv;
+ int i;
if (ath_chan_unavail(sc))
return;
(dev->flags & IFF_RUNNING) ? "" : "NOT ",
sc->sc_invalid ? "in" : "");
+ for (i=0; i<HAL_NUM_TX_QUEUES; i++) {
+ ath_txq_check(sc, &sc->sc_txq[i]);
+ ath_txq_dump(sc, &sc->sc_txq[i]);
+ }
+
if ((dev->flags & IFF_RUNNING) && !sc->sc_invalid) {
sc->sc_stats.ast_watchdog++;
ath_reset(dev); /* Avoid taking a semaphore in ath_init */