When the network device is detached, defer rebuilding the FCoE queue layout
until it is opened or resumed after PCI error recovery.

Fixes: f7f37e7ff2b9 ("ixgbe: handle close/suspend race with 
netif_device_detach/present")
Signed-off-by: Sergey Temerkhanov <[email protected]>
Reviewed-by: Aleksandr Loktionov <[email protected]>
---
 drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 29 ++++++++++----
 drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h |  1 +
 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 40 ++++++++++++++++++-
 3 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c 
b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
index 011fda9c6193..54d8b079ada4 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
@@ -830,27 +830,34 @@ static void ixgbe_fcoe_ddp_disable(struct ixgbe_adapter 
*adapter)
  *
  * Turns on FCoE offload feature in 82599.
  *
- * Returns : 0 indicates success or -EINVAL on failure
+ * Returns: 0 on success or a negative error code on failure
  */
 int ixgbe_fcoe_enable(struct net_device *netdev)
 {
        struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev);
        struct ixgbe_fcoe *fcoe = &adapter->fcoe;
+       bool running;
 
+       /* libfcoe calls ndo_fcoe_disable() even when this callback fails. */
        atomic_inc(&fcoe->refcnt);
 
+       if (!netif_device_present(netdev))
+               return -ENETDOWN;
+
        if (!(adapter->flags & IXGBE_FLAG_FCOE_CAPABLE))
                return -EINVAL;
 
        if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED)
                return -EINVAL;
 
+       running = netif_running(netdev);
+
        e_info(drv, "Enabling FCoE offload features.\n");
 
        if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
                e_warn(probe, "Enabling FCoE on PF will disable legacy VFs\n");
 
-       if (netif_running(netdev))
+       if (running)
                netdev->netdev_ops->ndo_stop(netdev);
 
        /* Allocate per CPU memory to track DDP pools */
@@ -864,8 +871,9 @@ int ixgbe_fcoe_enable(struct net_device *netdev)
        /* release existing queues and reallocate them */
        ixgbe_clear_interrupt_scheme(adapter);
        ixgbe_init_interrupt_scheme(adapter);
+       adapter->fcoe.reinit_pending = false;
 
-       if (netif_running(netdev))
+       if (running)
                netdev->netdev_ops->ndo_open(netdev);
 
        return 0;
@@ -882,6 +890,7 @@ int ixgbe_fcoe_enable(struct net_device *netdev)
 int ixgbe_fcoe_disable(struct net_device *netdev)
 {
        struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev);
+       bool running;
 
        if (!atomic_dec_and_test(&adapter->fcoe.refcnt))
                return -EINVAL;
@@ -889,8 +898,10 @@ int ixgbe_fcoe_disable(struct net_device *netdev)
        if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
                return -EINVAL;
 
+       running = ixgbe_netif_running(netdev);
+
        e_info(drv, "Disabling FCoE offload features.\n");
-       if (netif_running(netdev))
+       if (running)
                netdev->netdev_ops->ndo_stop(netdev);
 
        /* Free per CPU memory to track DDP pools */
@@ -903,10 +914,14 @@ int ixgbe_fcoe_disable(struct net_device *netdev)
        netdev_features_change(netdev);
 
        /* release existing queues and reallocate them */
-       ixgbe_clear_interrupt_scheme(adapter);
-       ixgbe_init_interrupt_scheme(adapter);
+       if (netif_device_present(netdev)) {
+               ixgbe_clear_interrupt_scheme(adapter);
+               ixgbe_init_interrupt_scheme(adapter);
+       } else {
+               adapter->fcoe.reinit_pending = true;
+       }
 
-       if (netif_running(netdev))
+       if (running)
                netdev->netdev_ops->ndo_open(netdev);
 
        return 0;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h 
b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
index 724f5382329f..81de590268cc 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
@@ -58,6 +58,7 @@ struct ixgbe_fcoe {
        dma_addr_t extra_ddp_buffer_dma;
        unsigned long mode;
        u8 up;
+       bool reinit_pending;
 };
 
 #endif /* _IXGBE_FCOE_H */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c 
b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 42d3594827f7..1f23a035a90b 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -182,6 +182,7 @@ EXPORT_SYMBOL(ixgbe_xdp_locking_key);
 static struct workqueue_struct *ixgbe_wq;
 
 static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev);
+static int ixgbe_rebuild_fcoe_queues(struct ixgbe_adapter *adapter);
 static void ixgbe_watchdog_link_is_down(struct ixgbe_adapter *);
 static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *);
 static void ixgbe_watchdog_update_link(struct ixgbe_adapter *);
@@ -7423,6 +7424,10 @@ int ixgbe_open(struct net_device *netdev)
        if (test_bit(__IXGBE_TESTING, &adapter->state))
                return -EBUSY;
 
+       err = ixgbe_rebuild_fcoe_queues(adapter);
+       if (err)
+               return err;
+
        netif_carrier_off(netdev);
 
        /* allocate transmit descriptors */
@@ -12345,6 +12350,24 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct 
pci_dev *pdev)
        return result;
 }
 
+static int ixgbe_rebuild_fcoe_queues(struct ixgbe_adapter *adapter)
+{
+#ifdef IXGBE_FCOE
+       int err;
+
+       if (!adapter->fcoe.reinit_pending)
+               return 0;
+
+       ixgbe_clear_interrupt_scheme(adapter);
+       err = ixgbe_init_interrupt_scheme(adapter);
+       if (err)
+               return err;
+
+       adapter->fcoe.reinit_pending = false;
+#endif
+       return 0;
+}
+
 /**
  * ixgbe_io_resume - called when traffic can start flowing again.
  * @pdev: Pointer to PCI device
@@ -12356,6 +12379,8 @@ static void ixgbe_io_resume(struct pci_dev *pdev)
 {
        struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
        struct net_device *netdev = adapter->netdev;
+       bool running;
+       int err;
 
 #ifdef CONFIG_PCI_IOV
        if (adapter->vferr_refcount) {
@@ -12366,8 +12391,19 @@ static void ixgbe_io_resume(struct pci_dev *pdev)
 
 #endif
        rtnl_lock();
-       if (netif_running(netdev))
-               ixgbe_open(netdev);
+       running = netif_running(netdev);
+       err = ixgbe_rebuild_fcoe_queues(adapter);
+       if (err)
+               e_dev_err("Cannot rebuild queue layout after PCI error 
recovery: %d\n",
+                         err);
+       if (running && !err) {
+               err = ixgbe_open(netdev);
+               if (err)
+                       e_dev_err("Cannot re-open netdev after PCI error 
recovery: %d\n",
+                                 err);
+       }
+       if (err && running)
+               dev_close(netdev);
 
        netif_device_attach(netdev);
        rtnl_unlock();
-- 
2.53.0

Reply via email to