Commit 4b799595 authored by Aaron Ma's avatar Aaron Ma Committed by Tony Nguyen

igc: fix page fault when thunderbolt is unplugged

After unplug thunderbolt dock with i225, pciehp interrupt is triggered,
remove call will read/write mmio address which is already disconnected,
then cause page fault and make system hang.

Check PCI state to remove device safely.

Trace:
BUG: unable to handle page fault for address: 000000000000b604
Oops: 0000 [#1] SMP NOPTI
RIP: 0010:igc_rd32+0x1c/0x90 [igc]
Call Trace:
igc_ptp_suspend+0x6c/0xa0 [igc]
igc_ptp_stop+0x12/0x50 [igc]
igc_remove+0x7f/0x1c0 [igc]
pci_device_remove+0x3e/0xb0
__device_release_driver+0x181/0x240

Fixes: 13b5b7fd ("igc: Add support for Tx/Rx rings")
Fixes: b03c49cd ("igc: Save PTP time before a reset")
Signed-off-by: default avatarAaron Ma <aaron.ma@canonical.com>
Tested-by: default avatarDvora Fuxbrumer <dvorax.fuxbrumer@linux.intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent ffc9c3eb
...@@ -149,6 +149,9 @@ static void igc_release_hw_control(struct igc_adapter *adapter) ...@@ -149,6 +149,9 @@ static void igc_release_hw_control(struct igc_adapter *adapter)
struct igc_hw *hw = &adapter->hw; struct igc_hw *hw = &adapter->hw;
u32 ctrl_ext; u32 ctrl_ext;
if (!pci_device_is_present(adapter->pdev))
return;
/* Let firmware take over control of h/w */ /* Let firmware take over control of h/w */
ctrl_ext = rd32(IGC_CTRL_EXT); ctrl_ext = rd32(IGC_CTRL_EXT);
wr32(IGC_CTRL_EXT, wr32(IGC_CTRL_EXT,
...@@ -4449,26 +4452,29 @@ void igc_down(struct igc_adapter *adapter) ...@@ -4449,26 +4452,29 @@ void igc_down(struct igc_adapter *adapter)
igc_ptp_suspend(adapter); igc_ptp_suspend(adapter);
/* disable receives in the hardware */ if (pci_device_is_present(adapter->pdev)) {
rctl = rd32(IGC_RCTL); /* disable receives in the hardware */
wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); rctl = rd32(IGC_RCTL);
/* flush and sleep below */ wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN);
/* flush and sleep below */
}
/* set trans_start so we don't get spurious watchdogs during reset */ /* set trans_start so we don't get spurious watchdogs during reset */
netif_trans_update(netdev); netif_trans_update(netdev);
netif_carrier_off(netdev); netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev); netif_tx_stop_all_queues(netdev);
/* disable transmits in the hardware */ if (pci_device_is_present(adapter->pdev)) {
tctl = rd32(IGC_TCTL); /* disable transmits in the hardware */
tctl &= ~IGC_TCTL_EN; tctl = rd32(IGC_TCTL);
wr32(IGC_TCTL, tctl); tctl &= ~IGC_TCTL_EN;
/* flush both disables and wait for them to finish */ wr32(IGC_TCTL, tctl);
wrfl(); /* flush both disables and wait for them to finish */
usleep_range(10000, 20000); wrfl();
usleep_range(10000, 20000);
igc_irq_disable(adapter); igc_irq_disable(adapter);
}
adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE; adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
......
...@@ -849,7 +849,8 @@ void igc_ptp_suspend(struct igc_adapter *adapter) ...@@ -849,7 +849,8 @@ void igc_ptp_suspend(struct igc_adapter *adapter)
adapter->ptp_tx_skb = NULL; adapter->ptp_tx_skb = NULL;
clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state);
igc_ptp_time_save(adapter); if (pci_device_is_present(adapter->pdev))
igc_ptp_time_save(adapter);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment