e1000e_down could be entered simultaneously by the reset task and the runtime PM suspend callback. Prevent this by acquiring a runtime PM reference in the reset task.
The intention is that: * A reset attempted during runtime suspend should drop silently; the interface is already going down. * A runtime suspend attempted during a reset will be retried later. * All callers of e1000e_down are now serialized. Signed-off-by: Matt Vollrath <[email protected]> Fixes: 23606cf5d119 ("e1000e / PCI / PM: Add basic runtime PM support (rev. 4)") Cc: [email protected] Assisted-by: Claude:claude-5-fable --- drivers/net/ethernet/intel/e1000e/netdev.c | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 47ff3c6ab451..4c15ca307b08 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -6004,20 +6004,37 @@ static void e1000_tx_timeout(struct net_device *netdev, unsigned int __always_un static void e1000_reset_task(struct work_struct *work) { struct e1000_adapter *adapter; + struct device *dev; + int rc; + adapter = container_of(work, struct e1000_adapter, reset_task); + dev = &adapter->pdev->dev; rtnl_lock(); + + /* Runtime suspend downs the device without holding rtnl. Hold a + * runtime PM reference so it cannot start underneath the reset, and + * skip the reset if the device is already suspending or suspended: + * resuming resets the hardware anyway. + */ + rc = pm_runtime_get_if_active(dev); + if (!rc) + goto out_unlock; + /* don't run the task if already down */ - if (test_bit(__E1000_DOWN, &adapter->state)) { - rtnl_unlock(); - return; - } + if (test_bit(__E1000_DOWN, &adapter->state)) + goto out_put; if (!(adapter->flags & FLAG_RESTART_NOW)) { e1000e_dump(adapter); e_err("Reset adapter unexpectedly\n"); } e1000e_reinit_locked(adapter); + +out_put: + if (rc > 0) + pm_runtime_put(dev); +out_unlock: rtnl_unlock(); } -- 2.43.0
