ixgbevf: Add code to check for Tx hang
authorEmil Tantilov <emil.s.tantilov@intel.com>
Wed, 28 Jan 2015 03:21:24 +0000 (03:21 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 6 Feb 2015 03:58:44 +0000 (19:58 -0800)
This patch adds code to allow for Tx hang checking.  The idea is to provide
more robust debug info in the event of a transmit unit hang. Similar to the
logic in ixgbe.

CC: Alexander Duyck <alexander.h.duyck@redhat.com>
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Tested-by: Phil Schmitt <phillip.j.schmitt@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c

index 65cda344eec92ab0fbca7f9b5e18b259c555a8c0..65c2aee2a083c8eac81fdf22d87f326f8218a665 100644 (file)
 #define BP_EXTENDED_STATS
 #endif
 
+#define IXGBE_MAX_TXD_PWR      14
+#define IXGBE_MAX_DATA_PER_TXD BIT(IXGBE_MAX_TXD_PWR)
+
+/* Tx Descriptors needed, worst case */
+#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IXGBE_MAX_DATA_PER_TXD)
+#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
+
 /* wrapper around a pointer to a socket buffer,
  * so a DMA handle can be stored along with the buffer */
 struct ixgbevf_tx_buffer {
@@ -85,6 +92,18 @@ struct ixgbevf_rx_queue_stats {
        u64 csum_err;
 };
 
+enum ixgbevf_ring_state_t {
+       __IXGBEVF_TX_DETECT_HANG,
+       __IXGBEVF_HANG_CHECK_ARMED,
+};
+
+#define check_for_tx_hang(ring) \
+       test_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+#define set_check_for_tx_hang(ring) \
+       set_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+#define clear_check_for_tx_hang(ring) \
+       clear_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state)
+
 struct ixgbevf_ring {
        struct ixgbevf_ring *next;
        struct net_device *netdev;
@@ -101,7 +120,7 @@ struct ixgbevf_ring {
                struct ixgbevf_tx_buffer *tx_buffer_info;
                struct ixgbevf_rx_buffer *rx_buffer_info;
        };
-
+       unsigned long state;
        struct ixgbevf_stats stats;
        struct u64_stats_sync syncp;
        union {
index a4b3d66b39a0406d0c0d8a06a36342b1beeb0ac8..87f9f8686b6fe7d240759d22483d6a841f22f0ba 100644 (file)
@@ -199,14 +199,64 @@ static void ixgbevf_unmap_and_free_tx_resource(struct ixgbevf_ring *tx_ring,
        /* tx_buffer must be completely set up in the transmit path */
 }
 
-#define IXGBE_MAX_TXD_PWR      14
-#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR)
+static u64 ixgbevf_get_tx_completed(struct ixgbevf_ring *ring)
+{
+       return ring->stats.packets;
+}
+
+static u32 ixgbevf_get_tx_pending(struct ixgbevf_ring *ring)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(ring->netdev);
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       u32 head = IXGBE_READ_REG(hw, IXGBE_VFTDH(ring->reg_idx));
+       u32 tail = IXGBE_READ_REG(hw, IXGBE_VFTDT(ring->reg_idx));
+
+       if (head != tail)
+               return (head < tail) ?
+                       tail - head : (tail + ring->count - head);
+
+       return 0;
+}
+
+static inline bool ixgbevf_check_tx_hang(struct ixgbevf_ring *tx_ring)
+{
+       u32 tx_done = ixgbevf_get_tx_completed(tx_ring);
+       u32 tx_done_old = tx_ring->tx_stats.tx_done_old;
+       u32 tx_pending = ixgbevf_get_tx_pending(tx_ring);
+
+       clear_check_for_tx_hang(tx_ring);
 
-/* Tx Descriptors needed, worst case */
-#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IXGBE_MAX_DATA_PER_TXD)
-#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
+       /* Check for a hung queue, but be thorough. This verifies
+        * that a transmit has been completed since the previous
+        * check AND there is at least one packet pending. The
+        * ARMED bit is set to indicate a potential hang.
+        */
+       if ((tx_done_old == tx_done) && tx_pending) {
+               /* make sure it is true for two checks in a row */
+               return test_and_set_bit(__IXGBEVF_HANG_CHECK_ARMED,
+                                       &tx_ring->state);
+       }
+       /* reset the countdown */
+       clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &tx_ring->state);
+
+       /* update completed stats and continue */
+       tx_ring->tx_stats.tx_done_old = tx_done;
+
+       return false;
+}
+
+/**
+ * ixgbevf_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ **/
+static void ixgbevf_tx_timeout(struct net_device *netdev)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
 
-static void ixgbevf_tx_timeout(struct net_device *netdev);
+       /* Do the reset outside of interrupt context */
+       schedule_work(&adapter->reset_task);
+}
 
 /**
  * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes
@@ -311,6 +361,37 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector,
        q_vector->tx.total_bytes += total_bytes;
        q_vector->tx.total_packets += total_packets;
 
+       if (check_for_tx_hang(tx_ring) && ixgbevf_check_tx_hang(tx_ring)) {
+               struct ixgbe_hw *hw = &adapter->hw;
+               union ixgbe_adv_tx_desc *eop_desc;
+
+               eop_desc = tx_ring->tx_buffer_info[i].next_to_watch;
+
+               pr_err("Detected Tx Unit Hang\n"
+                      "  Tx Queue             <%d>\n"
+                      "  TDH, TDT             <%x>, <%x>\n"
+                      "  next_to_use          <%x>\n"
+                      "  next_to_clean        <%x>\n"
+                      "tx_buffer_info[next_to_clean]\n"
+                      "  next_to_watch        <%p>\n"
+                      "  eop_desc->wb.status  <%x>\n"
+                      "  time_stamp           <%lx>\n"
+                      "  jiffies              <%lx>\n",
+                      tx_ring->queue_index,
+                      IXGBE_READ_REG(hw, IXGBE_VFTDH(tx_ring->reg_idx)),
+                      IXGBE_READ_REG(hw, IXGBE_VFTDT(tx_ring->reg_idx)),
+                      tx_ring->next_to_use, i,
+                      eop_desc, (eop_desc ? eop_desc->wb.status : 0),
+                      tx_ring->tx_buffer_info[i].time_stamp, jiffies);
+
+               netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
+
+               /* schedule immediate reset if we believe we hung */
+               schedule_work(&adapter->reset_task);
+
+               return true;
+       }
+
 #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
        if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
                     (ixgbevf_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
@@ -1479,6 +1560,8 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter,
        txdctl |= (1 << 8) |    /* HTHRESH = 1 */
                  32;          /* PTHRESH = 32 */
 
+       clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state);
+
        IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl);
 
        /* poll to verify queue is enabled */
@@ -2643,6 +2726,12 @@ static void ixgbevf_watchdog(unsigned long data)
        if (test_bit(__IXGBEVF_DOWN, &adapter->state))
                goto watchdog_short_circuit;
 
+       /* Force detection of hung controller */
+       if (netif_carrier_ok(adapter->netdev)) {
+               for (i = 0; i < adapter->num_tx_queues; i++)
+                       set_check_for_tx_hang(adapter->tx_ring[i]);
+       }
+
        /* get one bit for every active tx/rx interrupt vector */
        for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) {
                struct ixgbevf_q_vector *qv = adapter->q_vector[i];
@@ -2656,18 +2745,6 @@ watchdog_short_circuit:
        schedule_work(&adapter->watchdog_task);
 }
 
-/**
- * ixgbevf_tx_timeout - Respond to a Tx Hang
- * @netdev: network interface device structure
- **/
-static void ixgbevf_tx_timeout(struct net_device *netdev)
-{
-       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
-
-       /* Do the reset outside of interrupt context */
-       schedule_work(&adapter->reset_task);
-}
-
 static void ixgbevf_reset_task(struct work_struct *work)
 {
        struct ixgbevf_adapter *adapter;