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

Reply via email to