* [PATCH iwl-net v2 1/4] ixgbe: Refactor device operations to check whether netdev is available
2026-09-22 11:35 [PATCH iwl-net v2 0/4] ixgbe: fix FCoE/XDP issues and PCI reset handling Sergey Temerkhanov
@ 2026-09-22 11:35 ` Sergey Temerkhanov
2026-09-25 17:22 ` Simon Horman
2026-09-22 11:35 ` [PATCH iwl-net v2 2/4] ixgbe: Fix FCoE refcount taking for unsupported adapters Sergey Temerkhanov
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Sergey Temerkhanov @ 2026-09-22 11:35 UTC (permalink / raw)
To: intel-wired-lan; +Cc: netdev
Refactor several ixgbe driver operations to check whether the
netdev they operate on is enabled. This will allow the system
to get synchronized, for example, during the PCI resets.
Fixes: f7f37e7ff2b9 ("ixgbe: handle close/suspend race with netif_device_detach/present")
Signed-off-by: Sergey Temerkhanov <sergey.temerkhanov@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 5 +++
.../net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 13 +++++-
.../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 34 +++++++++------
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 | 42 +++++++++++++++++--
drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c | 4 +-
7 files changed, 101 insertions(+), 27 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index a5304390f975..bac3b85a1d63 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -955,6 +955,11 @@ extern char ixgbe_driver_name[];
extern char ixgbe_default_device_descr[];
#endif /* IXGBE_FCOE */
+static inline bool ixgbe_netif_running(struct net_device *netdev)
+{
+ return netif_running(netdev) && netif_device_present(netdev);
+}
+
int ixgbe_open(struct net_device *netdev);
int ixgbe_close(struct net_device *netdev);
void ixgbe_up(struct ixgbe_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
index 382d097e4b11..4420d3c32996 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
@@ -277,17 +277,23 @@ static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority,
static void ixgbe_dcbnl_devreset(struct net_device *dev)
{
struct ixgbe_adapter *adapter = ixgbe_from_netdev(dev);
+ bool running;
+
+ if (!netif_device_present(dev))
+ return;
+
+ running = netif_running(dev);
while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- if (netif_running(dev))
+ if (running)
dev->netdev_ops->ndo_stop(dev);
ixgbe_clear_interrupt_scheme(adapter);
ixgbe_init_interrupt_scheme(adapter);
- if (netif_running(dev))
+ if (running)
dev->netdev_ops->ndo_open(dev);
clear_bit(__IXGBE_RESETTING, &adapter->state);
@@ -515,6 +521,9 @@ static int ixgbe_dcbnl_ieee_setets(struct net_device *dev,
if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
+ if (!netif_device_present(dev))
+ return -ENETDOWN;
+
if (!adapter->ixgbe_ieee_ets) {
adapter->ixgbe_ieee_ets = kmalloc_obj(struct ieee_ets);
if (!adapter->ixgbe_ieee_ets)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index 1e1db8435506..f6258d1276e3 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -468,6 +468,9 @@ static int ixgbe_set_link_ksettings(struct net_device *netdev,
u32 advertised, old;
int err = 0;
+ if (!netif_device_present(netdev))
+ return -ENETDOWN;
+
if ((hw->phy.media_type == ixgbe_media_type_copper) ||
(hw->phy.multispeed_fiber)) {
/*
@@ -578,9 +581,9 @@ static void ixgbe_set_pauseparam_finalize(struct net_device *netdev,
/* If the thing changed then we'll update and use new autoneg. */
if (memcmp(fc, &hw->fc, sizeof(*fc))) {
hw->fc = *fc;
- if (netif_running(netdev))
+ if (ixgbe_netif_running(netdev))
ixgbe_reinit_locked(adapter);
- else
+ else if (netif_device_present(netdev))
ixgbe_reset(adapter);
}
}
@@ -1268,7 +1271,7 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
usleep_range(1000, 2000);
- if (!netif_running(adapter->netdev)) {
+ if (!ixgbe_netif_running(adapter->netdev)) {
for (i = 0; i < adapter->num_tx_queues; i++)
adapter->tx_ring[i]->count = new_tx_count;
for (i = 0; i < adapter->num_xdp_queues; i++)
@@ -2251,10 +2254,11 @@ static void ixgbe_diag_test(struct net_device *netdev,
struct ethtool_test *eth_test, u64 *data)
{
struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev);
- bool if_running = netif_running(netdev);
+ bool if_running = ixgbe_netif_running(netdev);
- if (ixgbe_removed(adapter->hw.hw_addr)) {
- e_err(hw, "Adapter removed - test blocked\n");
+ if (ixgbe_removed(adapter->hw.hw_addr) ||
+ !netif_device_present(netdev)) {
+ e_err(hw, "Adapter removed or detached - test blocked\n");
data[0] = 1;
data[1] = 1;
data[2] = 1;
@@ -2468,7 +2472,7 @@ static int ixgbe_nway_reset(struct net_device *netdev)
{
struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev);
- if (netif_running(netdev))
+ if (ixgbe_netif_running(netdev))
ixgbe_reinit_locked(adapter);
return 0;
@@ -2652,7 +2656,8 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
else
/* rx only or mixed */
q_vector->itr = rx_itr_param;
- ixgbe_write_eitr(q_vector);
+ if (netif_device_present(netdev))
+ ixgbe_write_eitr(q_vector);
}
/*
@@ -3696,6 +3701,9 @@ static int ixgbe_set_eee_e610(struct net_device *netdev,
kedata->eee_enabled)
return -EOPNOTSUPP;
+ if (!netif_device_present(netdev))
+ return -ENETDOWN;
+
hw->phy.eee_speeds_advertised = kedata->eee_enabled ?
hw->phy.eee_speeds_supported : 0;
@@ -3711,9 +3719,9 @@ static int ixgbe_set_eee_e610(struct net_device *netdev,
else
adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED;
- if (netif_running(netdev))
+ if (ixgbe_netif_running(netdev))
ixgbe_reinit_locked(adapter);
- else
+ else if (netif_device_present(netdev))
ixgbe_reset(adapter);
return 0;
@@ -3795,9 +3803,9 @@ static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_keee *edata)
}
/* reset link */
- if (netif_running(netdev))
+ if (ixgbe_netif_running(netdev))
ixgbe_reinit_locked(adapter);
- else
+ else if (netif_device_present(netdev))
ixgbe_reset(adapter);
return 0;
@@ -3879,7 +3887,7 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags)
adapter->flags2 = flags2;
/* reset interface to repopulate queues */
- if (netif_running(netdev))
+ if (ixgbe_netif_running(netdev))
ixgbe_reinit_locked(adapter);
}
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 a219590b3118..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 *);
@@ -7395,7 +7396,7 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu)
/* must set new MTU before calling down or up */
WRITE_ONCE(netdev->mtu, new_mtu);
- if (netif_running(netdev))
+ if (ixgbe_netif_running(netdev))
ixgbe_reinit_locked(adapter);
return 0;
@@ -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();
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
index 89f96c463f02..02820982b202 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c
@@ -39,7 +39,7 @@ static int ixgbe_xsk_pool_enable(struct ixgbe_adapter *adapter,
if (err)
return err;
- if_running = netif_running(adapter->netdev) &&
+ if_running = ixgbe_netif_running(adapter->netdev) &&
ixgbe_enabled_xdp_adapter(adapter);
if (if_running)
@@ -71,7 +71,7 @@ static int ixgbe_xsk_pool_disable(struct ixgbe_adapter *adapter, u16 qid)
if (!pool)
return -EINVAL;
- if_running = netif_running(adapter->netdev) &&
+ if_running = ixgbe_netif_running(adapter->netdev) &&
ixgbe_enabled_xdp_adapter(adapter);
if (if_running)
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH iwl-net v2 3/4] ixgbe: Restore previous XDP program on ixgbe_setup_tc() failure
2026-09-22 11:35 [PATCH iwl-net v2 0/4] ixgbe: fix FCoE/XDP issues and PCI reset handling Sergey Temerkhanov
2026-09-22 11:35 ` [PATCH iwl-net v2 1/4] ixgbe: Refactor device operations to check whether netdev is available Sergey Temerkhanov
2026-09-22 11:35 ` [PATCH iwl-net v2 2/4] ixgbe: Fix FCoE refcount taking for unsupported adapters Sergey Temerkhanov
@ 2026-09-22 11:35 ` Sergey Temerkhanov
2026-09-25 17:23 ` Simon Horman
2026-09-22 11:35 ` [PATCH iwl-net v2 4/4] ixgbe: Implement PCI reset handler Sergey Temerkhanov
3 siblings, 1 reply; 9+ messages in thread
From: Sergey Temerkhanov @ 2026-09-22 11:35 UTC (permalink / raw)
To: intel-wired-lan; +Cc: netdev
When enabling or disabling an XDP program requires a queue
reconfiguration, ixgbe_xdp_setup() swaps in the new program with
xchg() before calling ixgbe_setup_tc(). If ixgbe_setup_tc() fails,
the adapter was left pointing at the new program even though the
reconfiguration did not complete, leaking the reference to the old
program and leaving inconsistent state.
ixgbe_setup_tc() may have already reconfigured the queues for the new
program before failing. The XDP ring layout then no longer matches the
previous program, for example when removing a program has removed the
XDP TX rings. Restore the previous program and rebuild the queues for
it so adapter->xdp_prog remains consistent with the ring configuration.
Otherwise, a later open could run XDP without XDP TX rings and
dereference a NULL ring. ixgbe_setup_tc() handles the device having
been left down by the failed reconfiguration.
Fixes: 3fe1d0a48d21 ("ixgbe: XDP: fix checker warning from rcu pointer")
Signed-off-by: Sergey Temerkhanov <sergey.temerkhanov@intel.com>
Reviewed-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 40 ++++++++++++++++++-
1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 1f23a035a90b..6ef51b822a50 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7348,6 +7348,25 @@ static void ixgbe_free_all_rx_resources(struct ixgbe_adapter *adapter)
ixgbe_free_rx_resources(adapter->rx_ring[i]);
}
+static bool ixgbe_ring_resources_allocated(struct ixgbe_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ if (adapter->tx_ring[i] && adapter->tx_ring[i]->desc)
+ return true;
+
+ for (i = 0; i < adapter->num_xdp_queues; i++)
+ if (adapter->xdp_ring[i] && adapter->xdp_ring[i]->desc)
+ return true;
+
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ if (adapter->rx_ring[i] && adapter->rx_ring[i]->desc)
+ return true;
+
+ return false;
+}
+
/**
* ixgbe_max_xdp_frame_size - returns the maximum allowed frame size for XDP
* @adapter: device handle, pointer to adapter
@@ -9921,6 +9940,7 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc)
{
struct ixgbe_adapter *adapter = ixgbe_from_netdev(dev);
struct ixgbe_hw *hw = &adapter->hw;
+ bool running;
/* Hardware supports up to 8 traffic classes */
if (tc > adapter->dcb_cfg.num_tcs.pg_tcs)
@@ -9933,7 +9953,17 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc)
* match packet buffer alignment. Unfortunately, the
* hardware is not flexible enough to do this dynamically.
*/
- if (netif_running(dev))
+ if (!netif_device_present(dev))
+ return -ENETDOWN;
+
+ running = netif_running(dev);
+
+ /* If a previous ixgbe_open() failed, the netdev can still be
+ * administratively up after IRQs and ring resources have already been
+ * released. Skip ixgbe_close() only in that state; ixgbe_down() leaves
+ * resources for ixgbe_close() to release.
+ */
+ if (running && ixgbe_ring_resources_allocated(adapter))
ixgbe_close(dev);
else
ixgbe_reset(adapter);
@@ -10972,8 +11002,14 @@ static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog)
synchronize_rcu();
err = ixgbe_setup_tc(dev, adapter->hw_tcs);
- if (err)
+ if (err) {
+ xchg(&adapter->xdp_prog, old_prog);
+ err = ixgbe_setup_tc(dev, adapter->hw_tcs);
+ /* An ndo_bpf error leaves old_prog attached in the core. */
+ if (err)
+ return err;
return -EINVAL;
+ }
if (!prog)
xdp_features_clear_redirect_target(dev);
} else {
--
2.53.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH iwl-net v2 4/4] ixgbe: Implement PCI reset handler
2026-09-22 11:35 [PATCH iwl-net v2 0/4] ixgbe: fix FCoE/XDP issues and PCI reset handling Sergey Temerkhanov
` (2 preceding siblings ...)
2026-09-22 11:35 ` [PATCH iwl-net v2 3/4] ixgbe: Restore previous XDP program on ixgbe_setup_tc() failure Sergey Temerkhanov
@ 2026-09-22 11:35 ` Sergey Temerkhanov
2026-09-25 17:24 ` Simon Horman
3 siblings, 1 reply; 9+ messages in thread
From: Sergey Temerkhanov @ 2026-09-22 11:35 UTC (permalink / raw)
To: intel-wired-lan; +Cc: netdev
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.
Fixes: 711d57796f5c ("PCI: expose function reset capability in sysfs")
Signed-off-by: Sergey Temerkhanov <sergey.temerkhanov@intel.com>
Reviewed-by: Przemyslaw Korba <przemyslaw.korba@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 +
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 111 ++++++++++++++++++
2 files changed, 112 insertions(+)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index bac3b85a1d63..88247e760bda 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -912,6 +912,7 @@ enum ixgbe_state_t {
__IXGBE_PTP_TX_IN_PROGRESS,
__IXGBE_RESET_REQUESTED,
__IXGBE_PHY_INIT_COMPLETE,
+ __IXGBE_PCI_RESET_PREPARED,
};
struct ixgbe_cb {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 6ef51b822a50..da26d1013f3e 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -10537,6 +10537,9 @@ void ixgbe_do_reset(struct net_device *netdev)
{
struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev);
+ if (!netif_device_present(netdev))
+ return;
+
if (netif_running(netdev))
ixgbe_reinit_locked(adapter);
else
@@ -12396,6 +12399,7 @@ static int ixgbe_rebuild_fcoe_queues(struct ixgbe_adapter *adapter)
ixgbe_clear_interrupt_scheme(adapter);
err = ixgbe_init_interrupt_scheme(adapter);
+ /* Keep reinit_pending set so ixgbe_open() retries before using queues. */
if (err)
return err;
@@ -12404,6 +12408,110 @@ static int ixgbe_rebuild_fcoe_queues(struct ixgbe_adapter *adapter)
return 0;
}
+/**
+ * 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();
+ if (!netif_device_present(netdev)) {
+ rtnl_unlock();
+ return;
+ }
+
+ set_bit(__IXGBE_PCI_RESET_PREPARED, &adapter->state);
+ 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();
+ if (!test_and_clear_bit(__IXGBE_PCI_RESET_PREPARED, &adapter->state)) {
+ rtnl_unlock();
+ return;
+ }
+
+ adapter->hw.hw_addr = adapter->io_addr;
+ ixgbe_reset(adapter);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_WUS, ~0);
+ running = netif_running(netdev);
+
+ err = ixgbe_rebuild_fcoe_queues(adapter);
+ if (err)
+ e_dev_err("Cannot rebuild queue layout after PCI reset: %d\n",
+ err);
+ if (running && !err) {
+ err = ixgbe_open(netdev);
+ if (err)
+ e_dev_err("Cannot re-open netdev after PCI reset: %d. A new reset is needed.\n",
+ err);
+ }
+ if (err && running)
+ dev_close(netdev);
+ /* Restore presence so userspace can retry later. If recovery 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
@@ -12441,6 +12549,7 @@ static void ixgbe_io_resume(struct pci_dev *pdev)
if (err && running)
dev_close(netdev);
+ /* A failed running device is down; attach permits a later open retry. */
netif_device_attach(netdev);
rtnl_unlock();
}
@@ -12449,6 +12558,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
^ permalink raw reply related [flat|nested] 9+ messages in thread