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