e1000e: update Tx Unit hang detection message
authorBruce Allan <bruce.w.allan@intel.com>
Fri, 20 Nov 2009 23:28:56 +0000 (23:28 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 21 Nov 2009 19:34:32 +0000 (11:34 -0800)
The Tx unit hang detection code in e1000e detects other hangs caused by
hardware components (e.g. Rx, DMA units), but it is not possible to detect
exactly which component is hung so it has always assumed a Tx unit hang.
When dumping a message to the system log because of a hang, this patch adds
more data to help narrow the cause of the issue and makes the message
non-Tx-specific.  Because this new code reads PHY registers which can
sleep, move it off to a workqueue.  This and all previously existing work
tasks in the driver are now cancelled when the driver is removed.

Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/e1000e/e1000.h
drivers/net/e1000e/netdev.c

index 5ac8675..c9fcef7 100644 (file)
@@ -364,6 +364,7 @@ struct e1000_adapter {
        struct work_struct downshift_task;
        struct work_struct update_phy_task;
        struct work_struct led_blink_task;
+       struct work_struct print_hang_task;
 };
 
 struct e1000_info {
index 39f01d9..11a5274 100644 (file)
@@ -544,15 +544,27 @@ static void e1000_put_txbuf(struct e1000_adapter *adapter,
        buffer_info->time_stamp = 0;
 }
 
-static void e1000_print_tx_hang(struct e1000_adapter *adapter)
+static void e1000_print_hw_hang(struct work_struct *work)
 {
+       struct e1000_adapter *adapter = container_of(work,
+                                                    struct e1000_adapter,
+                                                    print_hang_task);
        struct e1000_ring *tx_ring = adapter->tx_ring;
        unsigned int i = tx_ring->next_to_clean;
        unsigned int eop = tx_ring->buffer_info[i].next_to_watch;
        struct e1000_tx_desc *eop_desc = E1000_TX_DESC(*tx_ring, eop);
+       struct e1000_hw *hw = &adapter->hw;
+       u16 phy_status, phy_1000t_status, phy_ext_status;
+       u16 pci_status;
+
+       e1e_rphy(hw, PHY_STATUS, &phy_status);
+       e1e_rphy(hw, PHY_1000T_STATUS, &phy_1000t_status);
+       e1e_rphy(hw, PHY_EXT_STATUS, &phy_ext_status);
 
-       /* detected Tx unit hang */
-       e_err("Detected Tx Unit Hang:\n"
+       pci_read_config_word(adapter->pdev, PCI_STATUS, &pci_status);
+
+       /* detected Hardware unit hang */
+       e_err("Detected Hardware Unit Hang:\n"
              "  TDH                  <%x>\n"
              "  TDT                  <%x>\n"
              "  next_to_use          <%x>\n"
@@ -561,7 +573,12 @@ static void e1000_print_tx_hang(struct e1000_adapter *adapter)
              "  time_stamp           <%lx>\n"
              "  next_to_watch        <%x>\n"
              "  jiffies              <%lx>\n"
-             "  next_to_watch.status <%x>\n",
+             "  next_to_watch.status <%x>\n"
+             "MAC Status             <%x>\n"
+             "PHY Status             <%x>\n"
+             "PHY 1000BASE-T Status  <%x>\n"
+             "PHY Extended Status    <%x>\n"
+             "PCI Status             <%x>\n",
              readl(adapter->hw.hw_addr + tx_ring->head),
              readl(adapter->hw.hw_addr + tx_ring->tail),
              tx_ring->next_to_use,
@@ -569,7 +586,12 @@ static void e1000_print_tx_hang(struct e1000_adapter *adapter)
              tx_ring->buffer_info[eop].time_stamp,
              eop,
              jiffies,
-             eop_desc->upper.fields.status);
+             eop_desc->upper.fields.status,
+             er32(STATUS),
+             phy_status,
+             phy_1000t_status,
+             phy_ext_status,
+             pci_status);
 }
 
 /**
@@ -643,14 +665,16 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter)
        }
 
        if (adapter->detect_tx_hung) {
-               /* Detect a transmit hang in hardware, this serializes the
-                * check with the clearing of time_stamp and movement of i */
+               /*
+                * Detect a transmit hang in hardware, this serializes the
+                * check with the clearing of time_stamp and movement of i
+                */
                adapter->detect_tx_hung = 0;
                if (tx_ring->buffer_info[i].time_stamp &&
                    time_after(jiffies, tx_ring->buffer_info[i].time_stamp
                               + (adapter->tx_timeout_factor * HZ))
                    && !(er32(STATUS) & E1000_STATUS_TXOFF)) {
-                       e1000_print_tx_hang(adapter);
+                       schedule_work(&adapter->print_hang_task);
                        netif_stop_queue(netdev);
                }
        }
@@ -5118,6 +5142,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
        INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
        INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);
        INIT_WORK(&adapter->update_phy_task, e1000e_update_phy_task);
+       INIT_WORK(&adapter->print_hang_task, e1000_print_hw_hang);
 
        /* Initialize link parameters. User can change them with ethtool */
        adapter->hw.mac.autoneg = 1;
@@ -5241,6 +5266,11 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
        del_timer_sync(&adapter->watchdog_timer);
        del_timer_sync(&adapter->phy_info_timer);
 
+       cancel_work_sync(&adapter->reset_task);
+       cancel_work_sync(&adapter->watchdog_task);
+       cancel_work_sync(&adapter->downshift_task);
+       cancel_work_sync(&adapter->update_phy_task);
+       cancel_work_sync(&adapter->print_hang_task);
        flush_scheduled_work();
 
        /*