Added debug in case the TX queue locked up (You need to do athdebug +watchdog
authorbenoit <benoit@0192ed92-7a03-0410-a25b-9323aeb14dbd>
Sun, 20 Apr 2008 21:06:15 +0000 (21:06 +0000)
committerbenoit <benoit@0192ed92-7a03-0410-a25b-9323aeb14dbd>
Sun, 20 Apr 2008 21:06:15 +0000 (21:06 +0000)
to see those debug message).
Fixed a bug where we removed TX descriptors too early. This should fix NETDEV
WATCHDOG error messages. Apparently, TX descriptor status is updated by the
hardware before TXDP.

git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3552 0192ed92-7a03-0410-a25b-9323aeb14dbd

ath/if_ath.c

index 6ff61d51152b0e3724d47f049149eb851b4101a5..de6ef9f770d44b7c102e7c0ed80008cf2323f2dd 100644 (file)
@@ -2855,6 +2855,54 @@ ath_desc_swap(struct ath_desc *ds)
 #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
  */
@@ -8260,6 +8308,17 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *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;
@@ -8545,6 +8604,7 @@ static void
 ath_tx_timeout(struct net_device *dev)
 {
        struct ath_softc *sc = dev->priv;
+       int i;
 
        if (ath_chan_unavail(sc))
                return;
@@ -8553,6 +8613,11 @@ ath_tx_timeout(struct net_device *dev)
                (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 */