|
@@ -544,15 +544,27 @@ static void e1000_put_txbuf(struct e1000_adapter *adapter,
|
|
buffer_info->time_stamp = 0;
|
|
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;
|
|
struct e1000_ring *tx_ring = adapter->tx_ring;
|
|
unsigned int i = tx_ring->next_to_clean;
|
|
unsigned int i = tx_ring->next_to_clean;
|
|
unsigned int eop = tx_ring->buffer_info[i].next_to_watch;
|
|
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_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"
|
|
" TDH <%x>\n"
|
|
" TDT <%x>\n"
|
|
" TDT <%x>\n"
|
|
" next_to_use <%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"
|
|
" time_stamp <%lx>\n"
|
|
" next_to_watch <%x>\n"
|
|
" next_to_watch <%x>\n"
|
|
" jiffies <%lx>\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->head),
|
|
readl(adapter->hw.hw_addr + tx_ring->tail),
|
|
readl(adapter->hw.hw_addr + tx_ring->tail),
|
|
tx_ring->next_to_use,
|
|
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,
|
|
tx_ring->buffer_info[eop].time_stamp,
|
|
eop,
|
|
eop,
|
|
jiffies,
|
|
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) {
|
|
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;
|
|
adapter->detect_tx_hung = 0;
|
|
if (tx_ring->buffer_info[i].time_stamp &&
|
|
if (tx_ring->buffer_info[i].time_stamp &&
|
|
time_after(jiffies, tx_ring->buffer_info[i].time_stamp
|
|
time_after(jiffies, tx_ring->buffer_info[i].time_stamp
|
|
+ (adapter->tx_timeout_factor * HZ))
|
|
+ (adapter->tx_timeout_factor * HZ))
|
|
&& !(er32(STATUS) & E1000_STATUS_TXOFF)) {
|
|
&& !(er32(STATUS) & E1000_STATUS_TXOFF)) {
|
|
- e1000_print_tx_hang(adapter);
|
|
|
|
|
|
+ schedule_work(&adapter->print_hang_task);
|
|
netif_stop_queue(netdev);
|
|
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->watchdog_task, e1000_watchdog_task);
|
|
INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);
|
|
INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);
|
|
INIT_WORK(&adapter->update_phy_task, e1000e_update_phy_task);
|
|
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 */
|
|
/* Initialize link parameters. User can change them with ethtool */
|
|
adapter->hw.mac.autoneg = 1;
|
|
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->watchdog_timer);
|
|
del_timer_sync(&adapter->phy_info_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();
|
|
flush_scheduled_work();
|
|
|
|
|
|
/*
|
|
/*
|