Message ID | 20210802105632.10361-2-aaron.ma@canonical.com |
---|---|
State | New |
Headers | show |
Series | Fix system hang after unplug tbt dock | expand |
On 02.08.21 12:56, Aaron Ma wrote: > BugLink: https://bugs.launchpad.net/bugs/1938689 > > 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 > > Signed-off-by: Aaron Ma <aaron.ma@canonical.com> > (cherry picked from commit 25475b830989e2879bd0fd60d7a14a1db0df8f5c > https://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git > branch dev-queue) > Signed-off-by: Aaron Ma <aaron.ma@canonical.com> Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com> Thanks > --- > drivers/net/ethernet/intel/igc/igc_main.c | 32 ++++++++++++++--------- > drivers/net/ethernet/intel/igc/igc_ptp.c | 3 ++- > 2 files changed, 21 insertions(+), 14 deletions(-) > > diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c > index f1adf154ec4a..49ba039f66db 100644 > --- a/drivers/net/ethernet/intel/igc/igc_main.c > +++ b/drivers/net/ethernet/intel/igc/igc_main.c > @@ -146,6 +146,9 @@ static void igc_release_hw_control(struct igc_adapter *adapter) > struct igc_hw *hw = &adapter->hw; > u32 ctrl_ext; > > + if (!pci_device_is_present(adapter->pdev)) > + return; > + > /* Let firmware take over control of h/w */ > ctrl_ext = rd32(IGC_CTRL_EXT); > wr32(IGC_CTRL_EXT, > @@ -4035,26 +4038,29 @@ void igc_down(struct igc_adapter *adapter) > > igc_ptp_suspend(adapter); > > - /* disable receives in the hardware */ > - rctl = rd32(IGC_RCTL); > - wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); > - /* flush and sleep below */ > - > + if (pci_device_is_present(adapter->pdev)) { > + /* disable receives in the hardware */ > + rctl = rd32(IGC_RCTL); > + wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); > + /* flush and sleep below */ > + } > /* set trans_start so we don't get spurious watchdogs during reset */ > netif_trans_update(netdev); > > netif_carrier_off(netdev); > netif_tx_stop_all_queues(netdev); > > - /* disable transmits in the hardware */ > - tctl = rd32(IGC_TCTL); > - tctl &= ~IGC_TCTL_EN; > - wr32(IGC_TCTL, tctl); > - /* flush both disables and wait for them to finish */ > - wrfl(); > - usleep_range(10000, 20000); > + if (pci_device_is_present(adapter->pdev)) { > + /* disable transmits in the hardware */ > + tctl = rd32(IGC_TCTL); > + tctl &= ~IGC_TCTL_EN; > + wr32(IGC_TCTL, tctl); > + /* flush both disables and wait for them to finish */ > + wrfl(); > + usleep_range(10000, 20000); > > - igc_irq_disable(adapter); > + igc_irq_disable(adapter); > + } > > adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE; > > diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c > index 69617d2c1be2..4ae19c6a3247 100644 > --- a/drivers/net/ethernet/intel/igc/igc_ptp.c > +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c > @@ -849,7 +849,8 @@ void igc_ptp_suspend(struct igc_adapter *adapter) > adapter->ptp_tx_skb = NULL; > 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); > } > > /** >
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index f1adf154ec4a..49ba039f66db 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -146,6 +146,9 @@ static void igc_release_hw_control(struct igc_adapter *adapter) struct igc_hw *hw = &adapter->hw; u32 ctrl_ext; + if (!pci_device_is_present(adapter->pdev)) + return; + /* Let firmware take over control of h/w */ ctrl_ext = rd32(IGC_CTRL_EXT); wr32(IGC_CTRL_EXT, @@ -4035,26 +4038,29 @@ void igc_down(struct igc_adapter *adapter) igc_ptp_suspend(adapter); - /* disable receives in the hardware */ - rctl = rd32(IGC_RCTL); - wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); - /* flush and sleep below */ - + if (pci_device_is_present(adapter->pdev)) { + /* disable receives in the hardware */ + rctl = rd32(IGC_RCTL); + wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN); + /* flush and sleep below */ + } /* set trans_start so we don't get spurious watchdogs during reset */ netif_trans_update(netdev); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); - /* disable transmits in the hardware */ - tctl = rd32(IGC_TCTL); - tctl &= ~IGC_TCTL_EN; - wr32(IGC_TCTL, tctl); - /* flush both disables and wait for them to finish */ - wrfl(); - usleep_range(10000, 20000); + if (pci_device_is_present(adapter->pdev)) { + /* disable transmits in the hardware */ + tctl = rd32(IGC_TCTL); + tctl &= ~IGC_TCTL_EN; + wr32(IGC_TCTL, tctl); + /* flush both disables and wait for them to finish */ + wrfl(); + usleep_range(10000, 20000); - igc_irq_disable(adapter); + igc_irq_disable(adapter); + } adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE; diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 69617d2c1be2..4ae19c6a3247 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -849,7 +849,8 @@ void igc_ptp_suspend(struct igc_adapter *adapter) adapter->ptp_tx_skb = NULL; 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); } /**