[PATCH 07/17] sched_ext: Restore a rescuee's slice after ops.stopping()
From: Tejun Heo
Date: Fri Oct 23 2026 - 14:01:57 EST
put_prev_task_scx() restores a preempted rescuee's unserved slice through
scx_rescue_keep() before it calls ops.stopping(). A rescuee whose slice the
scheduler zeroes there is left to the reinsert instead:
1. ops.stopping() zeroes the rescuee's slice
2. the reinsert condition special-cases the rescuee and keeps it on the
local DSQ with a zero slice
3. the pick's default refill supplies a slice, or the task runs on a zero
slice until the tick when its scheduler lacks BASE
Move the keep after the stopping call so that scx_rescue_keep() restores the
remainder whether a preemption or ops.stopping() zeroed the slice, and drop
the special case from the reinsert condition as a rescuee reaching it always
has a slice. A rescuee whose slice ops.stopping() zeroed now goes to the
tail of the local DSQ with its remainder restored, behind what the scheduler
wanted to run first, instead of staying at the head on a zero slice.
The slice end in the same block moves with it, which changes nothing for a
protected task. Its slice is the unserved remainder, and scx_rescue_charge()
ends the protection in the same update_curr_scx() call that zeroes the
slice, so the block never sees a protected task in either position.
v2: The description no longer says that a protected task's protection
outlasts ops.stopping().
Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
---
kernel/sched/ext/ext.c | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 70bc76ed4aa0..a9961dc33af0 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -3565,17 +3565,6 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
update_curr_scx(rq);
- /*
- * If the slice is consumed, protection ends with it. A rescuee
- * preempted beforehand keeps going, see scx_rescue_keep().
- */
- if (!p->scx.slice) {
- if (unlikely(p == scx_rescuee(rq)))
- rescue_keep = scx_rescue_keep(rq, p);
- if (!rescue_keep)
- scx_task_slice_ended(rq, p);
- }
-
/*
* Preserve the running session when proxy execution refreshes the same
* donor around an execution-context switch on this rq.
@@ -3588,6 +3577,18 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
p->scx.flags &= ~SCX_TASK_RUN_TRACKED;
}
+ /*
+ * If the slice is consumed, protection ends with it. A rescuee keeps
+ * going when a preemption or ops.stopping() zeroed its slice, see
+ * scx_rescue_keep().
+ */
+ if (!p->scx.slice) {
+ if (unlikely(p == scx_rescuee(rq)))
+ rescue_keep = scx_rescue_keep(rq, p);
+ if (!rescue_keep)
+ scx_task_slice_ended(rq, p);
+ }
+
if (p->scx.flags & SCX_TASK_QUEUED) {
if (proxy_block)
goto switch_class;
@@ -3612,12 +3613,8 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
* The exception is an IMMED donor put by proxy_resched_idle():
* the CPU isn't busy, proxy resolution is only retrying through
* idle, so keep the donor where it can be picked again.
- *
- * An open rescue must keep @p on the local DSQ even if the
- * scheduler zeroed the slice in ops.stopping() above.
*/
- if ((p->scx.slice || unlikely(p == scx_rescuee(rq))) &&
- !scx_bypassing(sch, cpu_of(rq))) {
+ if (p->scx.slice && !scx_bypassing(sch, cpu_of(rq))) {
if ((p->scx.flags & SCX_TASK_IMMED) && !proxy_put) {
p->scx.flags |= SCX_TASK_REENQ_PREEMPTED;
scx_do_enqueue_task(rq, p, SCX_ENQ_REENQ, -1);
--
2.56.0