[PATCH v4 0/5] sched: Handle split scheduling and execution contexts in task ticks

From: Hui Su

Date: Wed Sep 09 2026 - 05:43:49 EST


Proxy execution allows a task to execute on behalf of a different
scheduling context. Scheduler tick users do not all operate on the same
context: scheduling-class state and core-scheduling slice decisions belong
to the donor, while NUMA/cache work and RT watchdog state follow the task
that is actually executing.

The sched_class::task_tick() interface currently receives a single task
argument. Under cross-class proxy execution this is insufficient because
both the donor class and the execution class can have tick work to perform.

v3 handled the NUMA and cache cases with a common execution-context helper.
Following Peter Zijlstra's suggestion, v4 instead removes the task argument
from sched_class::task_tick() and lets each class derive the state it owns
from the runqueue. The common dispatcher invokes the donor class first and,
when the execution class differs, invokes that class afterwards.

The series is organized as follows:

- Patch 1 introduces the split-context task_tick() dispatch mechanism while
keeping the existing class-specific behavior donor-gated.
- Patches 2 and 3 move NUMA and cache tick work to the FAIR execution
context.
- Patch 4 keeps RT scheduling-class state and RR time-slice management with
the donor while associating the RT watchdog with the execution task. It
also handles an RT execution context whose donor belongs to another
scheduling class.
- Patch 5 fixes the complementary core-scheduling issue found during
review. task_tick_core() correctly remains donor-based, but the donor's
sum_exec_runtime does not advance while another task executes on its
behalf. Measure the donor's consumed slice in the task-clock domain
instead.

The RT watchdog follows the task whose execution runtime advances, while
RT scheduling-class state remains associated with the donor.

Changes in v4 (since v3):
- Rework the series around per-class task_tick() dispatch as suggested by
Peter Zijlstra, replacing the v3 common execution-context helper.
- Call the donor class before a different execution class so runtime
accounting is updated before execution-context consumers run.
- Keep task_tick() outside CONFIG_SCHED_HRTICK and preserve queued/hrtick
behavior.
- Keep task_tick_numa() and task_tick_cache() local to fair.c and preserve
the ordinary FAIR tick ordering.
- Fold in the RT watchdog proxy-execution fix, rebased on the new
task_tick() interface, including execution-only RT callbacks with another
donor class.
- Preserve RT-policy watchdog intervals across temporary DL PI boosts while
still clearing stale intervals when non-RT owners leave RT proxy service.
- Add the separate core-scheduling donor slice-accounting fix identified
during review, using a task-clock selection baseline.
- Extend the proxy-execution reproducers to cover FAIR/RT/DL cross-class
and same-class dispatch, NUMA/cache work, RT watchdog behavior, and
core-slice accounting.

v3: https://lore.kernel.org/r/20260904085244.799276-1-sh_def@xxxxxxx

Testing:
- Built kernel/sched/ at each patch boundary and built the final x86_64
bzImage. Also tested CONFIG_SCHED_HRTICK=n, CONFIG_POSIX_TIMERS=n,
CONFIG_SCHED_CLASS_EXT=y with proxy execution disabled,
CONFIG_SCHED_CORE=n, CONFIG_NUMA_BALANCING=n, CONFIG_SCHED_CACHE=n,
and CONFIG_FAIR_GROUP_SCHED=n.
- Verified NUMA and cache execution-context ticks and actual work callbacks
in a two-node, two-SMT-pair QEMU guest with FAIR, RT, and DL donors.
NO_HZ_FULL remote ticks were also exercised.
- Exercised RT-to-FAIR, DL-to-FAIR, FAIR-to-FAIR, RT-to-RT, DL-to-RT,
and DL-to-RR split-context dispatch. hrtick callbacks with queued=1 were
also exercised.
- Verified FIFO/RR watchdog attribution, blocking and nested proxy
execution, SIGXCPU, hard RLIMIT_RTTIME handling, and DL-donor/RT-owner
execution. For DL-to-RR execution-only callbacks, the execution owner's
watchdog advanced while its rt.time_slice remained unchanged.
- Reproduced an incorrect 123-to-0 timeout reset when a SCHED_FIFO owner
was PI-boosted into the DL class. With the policy guard, the timeout
remained 123 during the boost and after deboosting; a FAIR owner leaving
RT proxy service still reset its timeout from 123 to zero.
- Compared the existing and task-clock core-slice predicates over 89,900
non-proxy samples across HZ=100/250/1000 and nice -10/0/+10, with zero
mismatches. Under proxy execution the donor's sum_exec_runtime delta
remained zero while the task-clock delta advanced and reached the
force-idle reschedule condition.
- Exercised dynamic nice and cgroup-weight changes, migration, and
execution-owner handoff.
- Ran a 31-minute KASAN/LOCKDEP soak covering 33 cycles x 13 modes
(429 cases), with no failures or kernel fault reports.

Hui Su (5):
sched: Dispatch task ticks for donor and execution classes
sched/numa: Drive NUMA task tick from execution context
sched/cache: Drive cache task tick from execution context
sched/rt: Fix RT watchdog accounting for proxy execution
sched/core: Fix donor slice accounting under proxy execution

include/linux/sched.h | 3 ++
kernel/sched/core.c | 43 +++++++++++++++++++++++++--
kernel/sched/deadline.c | 10 +++++--
kernel/sched/ext/ext.c | 10 +++++--
kernel/sched/fair.c | 64 ++++++++++++++++++++++++----------------
kernel/sched/idle.c | 8 ++---
kernel/sched/rt.c | 23 +++++++++++----
kernel/sched/sched.h | 2 +-
kernel/sched/stop_task.c | 5 ++--
9 files changed, 121 insertions(+), 47 deletions(-)


base-commit: df2908090cda368b01ff43709f51890076c56157
--
2.55.0