[PATCH 05/15] sched_ext: Restore a rescuee's slice after ops.stopping()
From: Tejun Heo
Date: Fri Oct 23 2026 - 01:33:15 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, so a protected task's
protection outlasts ops.stopping() and a slice write from there is refused.
The scheduler has to be able to handle the refusal anyway, as a write on a
protected task is refused whenever it is made, and ops.stopping() is too
late to extend a slice as the next task is already picked by the time it
runs.
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 065ecdf3e3bf..4b976beb2f6d 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -3558,17 +3558,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.
@@ -3581,6 +3570,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;
@@ -3605,12 +3606,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