mwl8k: allow more time for transmit rings to drain
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / drivers / net / wireless / mwl8k.c
index 793a83e10e169feffd4ad595a1a98e36950045de..a16255810d1559fc0e528cbd4832c1e4e36dbdd5 100644 (file)
@@ -1237,99 +1237,106 @@ static inline void mwl8k_tx_start(struct mwl8k_priv *priv)
        ioread32(priv->regs + MWL8K_HIU_INT_CODE);
 }
 
-struct mwl8k_txq_info {
-       u32 fw_owned;
-       u32 drv_owned;
-       u32 unused;
-       u32 len;
-       u32 head;
-       u32 tail;
-};
-
-static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv,
-                               struct mwl8k_txq_info *txinfo)
+static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
 {
-       int count, desc, status;
-       struct mwl8k_tx_queue *txq;
-       struct mwl8k_tx_desc *tx_desc;
-       int ndescs = 0;
+       struct mwl8k_priv *priv = hw->priv;
+       int i;
 
-       memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info));
+       for (i = 0; i < MWL8K_TX_QUEUES; i++) {
+               struct mwl8k_tx_queue *txq = priv->txq + i;
+               int fw_owned = 0;
+               int drv_owned = 0;
+               int unused = 0;
+               int desc;
 
-       for (count = 0; count < MWL8K_TX_QUEUES; count++) {
-               txq = priv->txq + count;
-               txinfo[count].len = txq->stats.len;
-               txinfo[count].head = txq->head;
-               txinfo[count].tail = txq->tail;
                for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
-                       tx_desc = txq->txd + desc;
-                       status = le32_to_cpu(tx_desc->status);
+                       struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
+                       u32 status;
 
+                       status = le32_to_cpu(tx_desc->status);
                        if (status & MWL8K_TXD_STATUS_FW_OWNED)
-                               txinfo[count].fw_owned++;
+                               fw_owned++;
                        else
-                               txinfo[count].drv_owned++;
+                               drv_owned++;
 
                        if (tx_desc->pkt_len == 0)
-                               txinfo[count].unused++;
+                               unused++;
                }
-       }
 
-       return ndescs;
+               printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d "
+                      "fw_owned=%d drv_owned=%d unused=%d\n",
+                      wiphy_name(hw->wiphy), i,
+                      txq->stats.len, txq->head, txq->tail,
+                      fw_owned, drv_owned, unused);
+       }
 }
 
 /*
  * Must be called with priv->fw_mutex held and tx queues stopped.
  */
+#define MWL8K_TX_WAIT_TIMEOUT_MS       1000
+
 static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
 {
        struct mwl8k_priv *priv = hw->priv;
        DECLARE_COMPLETION_ONSTACK(tx_wait);
-       u32 count;
-       unsigned long timeout;
+       int retry;
+       int rc;
 
        might_sleep();
 
+       /*
+        * The TX queues are stopped at this point, so this test
+        * doesn't need to take ->tx_lock.
+        */
+       if (!priv->pending_tx_pkts)
+               return 0;
+
+       retry = 0;
+       rc = 0;
+
        spin_lock_bh(&priv->tx_lock);
-       count = priv->pending_tx_pkts;
-       if (count)
-               priv->tx_wait = &tx_wait;
-       spin_unlock_bh(&priv->tx_lock);
+       priv->tx_wait = &tx_wait;
+       while (!rc) {
+               int oldcount;
+               unsigned long timeout;
 
-       if (count) {
-               struct mwl8k_txq_info txinfo[MWL8K_TX_QUEUES];
-               int index;
-               int newcount;
+               oldcount = priv->pending_tx_pkts;
 
+               spin_unlock_bh(&priv->tx_lock);
                timeout = wait_for_completion_timeout(&tx_wait,
-                                       msecs_to_jiffies(5000));
-               if (timeout)
-                       return 0;
-
+                           msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
                spin_lock_bh(&priv->tx_lock);
-               priv->tx_wait = NULL;
-               newcount = priv->pending_tx_pkts;
-               mwl8k_scan_tx_ring(priv, txinfo);
-               spin_unlock_bh(&priv->tx_lock);
 
-               printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n",
-                      __func__, __LINE__, count, newcount);
+               if (timeout) {
+                       WARN_ON(priv->pending_tx_pkts);
+                       if (retry) {
+                               printk(KERN_NOTICE "%s: tx rings drained\n",
+                                      wiphy_name(hw->wiphy));
+                       }
+                       break;
+               }
 
-               for (index = 0; index < MWL8K_TX_QUEUES; index++)
-                       printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u "
-                              "DRV:%u U:%u\n",
-                                       index,
-                                       txinfo[index].len,
-                                       txinfo[index].head,
-                                       txinfo[index].tail,
-                                       txinfo[index].fw_owned,
-                                       txinfo[index].drv_owned,
-                                       txinfo[index].unused);
+               if (priv->pending_tx_pkts < oldcount) {
+                       printk(KERN_NOTICE "%s: timeout waiting for tx "
+                              "rings to drain (%d -> %d pkts), retrying\n",
+                              wiphy_name(hw->wiphy), oldcount,
+                              priv->pending_tx_pkts);
+                       retry = 1;
+                       continue;
+               }
+
+               priv->tx_wait = NULL;
+
+               printk(KERN_ERR "%s: tx rings stuck for %d ms\n",
+                      wiphy_name(hw->wiphy), MWL8K_TX_WAIT_TIMEOUT_MS);
+               mwl8k_dump_tx_rings(hw);
 
-               return -ETIMEDOUT;
+               rc = -ETIMEDOUT;
        }
+       spin_unlock_bh(&priv->tx_lock);
 
-       return 0;
+       return rc;
 }
 
 #define MWL8K_TXD_SUCCESS(status)                              \