Implement PCI device reset handler to allow the network device to get re-initialized and function after a PCI-level reset.
This is necessary for the adapter to avoid TX queue timeouts occurring after the PCI reset is performed via sysfs during its operation. The reset codepath may trigger a number of dependencies in the reset of the driver, so that it is necessary to check if the netdev is present and running there. Signed-off-by: Sergey Temerkhanov <[email protected]> Reviewed-by: Przemyslaw Korba <[email protected]> Reviewed-by: Aleksandr Loktionov <[email protected]> --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 42dac766c907..1865b604ace7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -12358,6 +12358,94 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev) return result; } +/** + * ixgbe_pci_reset_prepare - called before the pci bus is reset. + * @pdev: Pointer to PCI device + * + * Quiesce the driver in preparation for a PCI function reset. Called from + * pci_dev_save_and_disable() before the core saves config state and writes + * PCI_COMMAND_INTX_DISABLE to clear bus mastering and MMIO decode, so MMIO + * access to the device is still valid here. + */ +static void ixgbe_pci_reset_prepare(struct pci_dev *pdev) +{ + struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev; + + if (!adapter) + return; + + netdev = adapter->netdev; + + rtnl_lock(); + netif_device_detach(netdev); + if (netif_running(netdev)) + ixgbe_close_suspend(adapter); + rtnl_unlock(); + + /* __IXGBE_RESETTING is intentionally not set here: it is spun on + * while holding rtnl by ixgbe_reinit_locked(), ixgbe_dcbnl_devreset() + * and the ethtool reset paths, so holding it across the rtnl drop + * would deadlock those callers against ixgbe_pci_reset_done(), which + * needs to re-acquire rtnl. During the reset window concurrent + * rtnl-holding paths must treat the netdev as detached, while teardown + * paths also observe __IXGBE_DOWN set by ixgbe_down() via + * ixgbe_close_suspend(), matching the existing ixgbe_io_error_detected() + * flow. + */ + + if (test_bit(__IXGBE_SERVICE_INITED, &adapter->state)) { + /* The service timer was already stopped by ixgbe_down() via + * ixgbe_close_suspend(); if the netdev was not running, the + * timer is not armed. Only the currently queued service task + * (if any) still needs to be flushed here. + */ + cancel_work_sync(&adapter->service_task); + clear_bit(__IXGBE_SERVICE_SCHED, &adapter->state); + } +} + +/** + * ixgbe_pci_reset_done - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Re-initialize the device after a PCI function reset. The PCI core has + * already called pci_restore_state() before invoking this callback, so the + * saved Command register (including bus mastering) is back in place. + */ +static void ixgbe_pci_reset_done(struct pci_dev *pdev) +{ + struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); + struct net_device *netdev; + bool running; + int err = 0; + + if (!adapter) + return; + + netdev = adapter->netdev; + + rtnl_lock(); + adapter->hw.hw_addr = adapter->io_addr; + ixgbe_reset(adapter); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_WUS, ~0); + running = netif_running(netdev); + if (running) { + err = ixgbe_open(netdev); + if (err) { + e_dev_err("Cannot re-open netdev after PCI reset: %d. A new reset is needed.\n", + err); + dev_close(netdev); + } + } + /* Restore presence so userspace can retry later. If ixgbe_open() failed, + * dev_close() cleared IFF_UP first so netif_device_attach() will not wake + * Tx queues without a successful open. + */ + netif_device_attach(netdev); + rtnl_unlock(); +} + /** * ixgbe_io_resume - called when traffic can start flowing again. * @pdev: Pointer to PCI device @@ -12390,6 +12478,8 @@ static const struct pci_error_handlers ixgbe_err_handler = { .error_detected = ixgbe_io_error_detected, .slot_reset = ixgbe_io_slot_reset, .resume = ixgbe_io_resume, + .reset_prepare = ixgbe_pci_reset_prepare, + .reset_done = ixgbe_pci_reset_done, }; static DEFINE_SIMPLE_DEV_PM_OPS(ixgbe_pm_ops, ixgbe_suspend, ixgbe_resume); -- 2.53.0
