e1000: resume and io_resume bring the device back up without rtnl_lock and deadlock against e1000_reset_task on the NAPI state machine

From: 徐晗翔 <hidden>
Date: 2026-10-27 11:11:40
Also in: lkml

Both e1000 device-recovery paths (e1000_resume() after system
suspend and e1000_io_resume() after a PCI Error Recovery reset) call
e1000_up() while holding no lock at all, so a concurrently running
e1000_reset_task (which is RTNL-protected) slips past the
__E1000_DOWN gate that the recovery path itself just opened, parks in
napi_disable() holding the netdev instance lock and RTNL while
waiting for NAPI_STATE_SCHED to clear -- an event whose only producer
is the recovery thread's napi_enable(), which needs that very lock --
an unresolvable mutual wait that leaves RTNL held forever, hanging
every network configuration operation on the system and preventing
device_resume() from ever completing.
The reset_task work survives system suspend by construction
(system_wq is not freezable and the CPUs are halted, so a pre-suspend
scheduled reset_task cannot run until resume), and in the PCI ER case
the AER error itself produces it (hardware upset -> TX hang -> dev
watchdog -> ndo_tx_timeout -> schedule_work) -- the async opponent is
not a coincidence but part of the same event chain.  I verified the
deadlock on a pristine Linux 7.3 (KASAN kernel, no source
modification) with a probe module that only widens the natural ~10 ms
window: both sides of the mutual wait were observed directly (the
reset_task parked inside napi_disable holding the lock, and the
resume thread blocking on napi_enable for the same lock), after
which resume never completes and the host cannot wake the guest.
The sibling drivers handle both contexts correctly (e1000e wraps its
thaw path in rtnl_lock() and reuses it for io_resume; igb_io_resume()
takes rtnl_lock() and re-checks its DOWN bit) -- e1000 is the only
driver of the family unprotected in both.  Recovery requires a
reboot; triggering is suspend/resume cycling (or an AER event) under
ordinary network load, i.e. a local denial of service.
The detailed description:
* Test environment / affected version
  Linux version 7.3.0 (gcc 13.3.0, GNU ld 2.42) #1 SMP PREEMPT_DYNAMIC
  -- vanilla mainline 7.3 release tarball (kernel.org), KASAN enabled,
  source unmodified.  QEMU/KVM x86_64 guest with an emulated e1000 NIC
  on a user network; real S3 suspend ("echo mem > /sys/power/state")
  with RTC wakealarm.  The verification instrument (attached) is a
  loadable kprobe module; it changes timing only.
* The two unprotected recovery paths (drivers/net/ethernet/intel/
  e1000/e1000_main.c, 7.3 line numbers)
  - e1000_resume() (:5161-5204): no rtnl_lock, no __E1000_RESETTING
    participation; when netif_running(): request_irq, e1000_reset(),
    then e1000_up().
  - e1000_io_resume() (:5311-5326): same shape ("Implementation
    resembles the second-half of the e1000_resume routine"); when
    netif_running(): e1000_up() + netif_device_attach(), no lock.
  Their locked counterparts that create the precondition are:
  __e1000_shutdown() under rtnl_lock() (calls e1000_down(),
  :5084-5086) for the PM case, and e1000_io_error_detected() under
  rtnl_lock() (:5247-5260, calls e1000_down() when running) for the
  ER case.  Both leave __E1000_DOWN=1 and the NAPI in its
  disabled state, i.e. NAPI_STATE_SCHED=1.
* The gate and the deadlock
  e1000_up() order (:381-399):
      e1000_configure(adapter);                 /* :386, DOWN still 1 */
      clear_bit(__E1000_DOWN, &adapter->flags); /* :388  <-- gate    */
      napi_enable(&adapter->napi);              /* :390              */
  The async opponent, e1000_reset_task (:3503-3512): rtnl_lock();
  e1000_reinit_locked(); rtnl_unlock().  e1000_reinit_locked()
  (:531-543) spins on __E1000_RESETTING, then checks
  test_bit(__E1000_DOWN) at :537 -- the check the recovery path's
  clear_bit() at :388 flips.  If reset_task reads DOWN==0 it enters
  e1000_down() (:481-529; includes msleep(10) at :500) and reaches
  napi_disable() at :513.
  The core-level amplifier: napi_disable() runs under the netdev
  instance lock and its wait loop has no timeout
  (net/core/dev.c:7742-7747; the loop at :7712-7715 does
  usleep_range while holding the lock, waiting for
  NAPI_STATE_SCHED).  A freshly added or disabled NAPI has SCHED=1
  (net/core/dev.c:7679-7680); the ONLY operation that clears it is
  napi_enable() -- which must take the same instance lock
  (dev.c:7777-7782).  With no poll running (IRQs not yet enabled in
  the recovery flow), the wait can only be satisfied by the other
  thread of the pair:
      reset_task : napi_disable() holds netdev_lock + RTNL,
                   waits forever for SCHED to clear
      resume     : napi_enable() (the only SCHED clearer)
                   blocks forever on netdev_lock
  The harmful window is the resume thread being preempted/delayed
  >= ~10 ms between the two adjacent statements :388 and :390 (the
  reset_task needs ~10 ms from its DOWN check to napi_disable
  because of the msleep(10) in e1000_down), plus the reset_task
  entering the lock first.  That is an ordinary preemption quantum
  on a loaded, RT, or virtualized system.  All other interleavings
  are safe: reset_task reading DOWN before the gate skips reinit in
  a few instructions; napi_enable() winning the lock first makes the
  later napi_disable() pass instantly (SCHED already cleared) and
  the pair completes normally; the double-napi_enable BUG_ON is
  unreachable (it would need the whole >=10 ms reinit inside the
  gap between two adjacent statements).
* Why the opponent always exists
  - PM context: reset_task is scheduled by e1000_tx_timeout() or by
    e1000_watchdog() on "link lost with outstanding TX" -- ordinary
    conditions under load.  system_wq is not freezable and S3 halts
    the CPUs, so any reset_task scheduled before suspend provably
    survives to resume time (it simply cannot run during suspend).
  - PCI ER context: the AER error that triggers the recovery flow
    upsets the hardware, hangs TX, and the dev watchdog then calls
    e1000_tx_timeout() -> schedule_work(&adapter->reset_task): the
    async opponent is generated by the same event chain that starts
    the recovery.  This makes the ER context the most reachable of
    the two.
* Consequences
  reset_task holds RTNL forever: every later rtnl_lock() user (all
  network configuration, most ethtool/netlink operations) queues
  forever.  The recovery thread never returns: in the PM context
  device_resume() does not complete (system wakeup hangs -- in my
  run the host's wakeup poke was ineffective because the guest was
  stuck mid-resume, not asleep); in the ER context the recovery
  flow wedges and the device stays offline.  hung-task / soft-lockup
  reports follow; only a reboot recovers.  No memory-safety issue:
  all objects stay alive; this is a pure liveness bug.
* Evidence (pristine kernel, timing-only instrument; module attached)
  e1000race.c is a kprobe module: it parks the resume thread at the
  gate point (kprobe on napi_enable filtered by the return address
  of the call inside e1000_up -- widening the natural >=10 ms window
  to 5 s), schedules a real ndo_tx_timeout via a delayed work (the
  exact indirect call dev_watchdog would make; QEMU's e1000 model
  completes TX immediately so a real watchdog TX hang is not
  reachable in the VM), and logs both napi_disable entries and
  reset_task executions.  Observed on the S3/resume path (serial
  console, timestamps compressed):
    [56.468] napi_disable entered by kworker/u16:4      <- shutdown
              side (__e1000_shutdown -> e1000_down); NAPI still
              enabled, returns normally; leaves SCHED=1
    [56.42-58.22]   S3: freeze, CPUs off, ACPI low-level resume, CPUs on
    [58.267] resume thread (kworker/u16:3) parked 5000 ms before
              napi_enable; clear_bit(__E1000_DOWN) already executed
    [58.323] trigger: invoking eth0->ndo_tx_timeout -> schedules
              e1000_reset_task inside the resume window
    [58.336] reset_task execution #1
    [58.343] e1000 0000:00:02.0 eth0: Reset adapter      <- driver log
    [58.360] napi_disable entered by kworker/1:1        <- reset_task
              parks here holding netdev_lock + RTNL
    [63.283] resume stall done; napi_enable now contends netdev_lock
    (silence: no "PM: suspend exit", no "Restarting tasks";
     `ip link set eth0 down` queued forever; host wakeup ineffective;
     the VM had to be killed by timeout)
  Both sides of the mutual wait were thus observed directly.  The
  in-VM probes also dumped the RTNL waiter's kernel stack
  (rtnl_lock wait) and the D-state kworker.  Negative control:
  identical kernel, no window widening, three full link-churn +
  S3/resume cycles -- all three resumed, an RTNL probe returned
  instantly, zero splats and zero hung tasks (the natural window is
  the >=10 ms preemption; short cycles do not hit it, matching the
  analysis).
  Instrument usage (attached e1000race.c; needs a kernel with
  CONFIG_E1000=y and KPROBES; the ra-offset parameter
  napi_ret_off=0x38 matches my KASAN build and is recomputable for
  other builds):
    make -C <kernel-build-dir> M=$PWD modules
    # in the guest:
    insmod e1000race.ko stall_napi_ms=5000
    ip link set eth0 up; ip addr add 10.0.2.15/24 dev eth0
    echo 1 > /sys/module/e1000race/parameters/armed
    echo 0  > /sys/class/rtc/rtc0/wakealarm
    echo +6 > /sys/class/rtc/rtc0/wakealarm
    echo mem > /sys/power/state
    # after the RTC wake: the timeline above; resume never completes.


Regards,
Hanxiang Xu
xuhx@hust.edu.cn

Attachments

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help