[PATCH 0/3] sched/psi: Fix rtpoll teardown races

From: Guopeng Zhang

Date: Fri Jul 17 2026 - 05:20:23 EST


From: Guopeng Zhang <zhangguopeng@xxxxxxxxxx>

Last-trigger rtpoll teardown clears rtpoll_task under
rtpoll_trigger_lock, but has to stop the old worker after dropping the
lock because the worker takes the same lock. A new trigger can therefore
install a replacement worker before the old worker exits. Lockless
psi_schedule_rtpoll_work() readers can also overlap with teardown after
having observed the old worker.

This leaves three separate races:

- the old worker can consume a timer wakeup intended for its replacement
and then exit, leaving the replacement asleep while
rtpoll_scheduled remains set;

- a lockless reader that observed the old rtpoll_task can call
mod_timer() after teardown called timer_delete(), leaving a stale timer
pending after the last trigger has been removed;

- after a replacement has published its worker and armed the timer, the
old teardown can clear rtpoll_scheduled, allowing a later task change
to rearm an already pending timer unnecessarily.

The patches are ordered as follows:

1. Check for worker stop before consuming the shared wakeup, and process
every wakeup that is consumed.
2. Wait for pre-existing RCU readers before deleting the timer
synchronously under rtpoll_trigger_lock.
3. Building on patch 2, clear rtpoll_scheduled during the locked timer
teardown, before a replacement worker can be published.

Testing
=======

Temporary A/B instrumentation directly exercised the timer rearm race
addressed by patch 2. One lockless RCU reader per group was held for up to
10 ms after it read a non-NULL rtpoll_task. On the unfixed kernel,
teardown unpublished the worker, deleted the timer, and released the
reader. The reader then observed the NULL pointer and rearmed the timer
with a 10-second expiry. On the fixed kernel, synchronize_rcu() waited for
the reader to rearm the timer and leave its read-side critical section
before timer_delete_sync() removed it.

After the grace period and before stopping the old worker, the
instrumentation checked under rtpoll_trigger_lock whether the timer was
pending while no replacement worker was published. It logged and
synchronously deleted any timer found this way.

Both runs used the same test VM, kernel configuration, script, workload,
and instrumentation.

Without patch 2:

CPU workers: 128
Duration: 60 seconds
Trigger cycles: 2029
CPU PSI some total delta: 60561126 usec
Pending timer after teardown: observed

With patch 2:

CPU workers: 128
Duration: 60 seconds
Trigger cycles: 2045
CPU PSI some total delta: 60423170 usec
Pending timer after teardown: not observed in 2045 trigger cycles

The temporary instrumentation is not part of this series.

Guopeng Zhang (3):
sched/psi: Avoid losing wakeups during rtpoll worker replacement
sched/psi: Prevent stale timer rearm after rtpoll teardown
sched/psi: Avoid clobbering rtpoll_scheduled during teardown

kernel/sched/psi.c | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)


base-commit: 1a1757b76427f6201bfe0bf1bea9f7574f332a93
--
2.43.0