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
- Makefile [application/octet-stream] 21 bytes
- e1000race.c [text/plain] 8208 bytes · preview