[PATCH 05/12] sched_ext: Fix ops.running/stopping() pairing for proxy-exec donors
Andrea Righi <[email protected]> Tue, 21 Jul 2026 08:31:26 +0200
| Newsgroups | dev.linux.lists.sched-ext,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
With proxy-exec, pick_next_task() can return a task with p->is_blocked set (a proxy donor). put_prev_set_next_task() then calls set_next_task_scx() on this "ghost" task even though the task only provides scheduling context and never actually runs. Calling ops.running() for such a donor produces a spurious running event. Simply suppressing ops.running() is not sufficient because the following put_prev_task_scx() would still invoke ops.stopping(), resulting in an unpaired stopping event. Introduce SCX_TASK_RUN_TRACKED to track whether a task entered an ops.running()/stopping() session. Set and clear the flag independently of ops.running() and ops.stopping(), as the callbacks are independently optional. Invoke ops.running() only for non-blocked tasks and invoke ops.stopping() only after entering a tracked session. This keeps the callbacks paired for proxy donors while preserving stopping notifications for schedulers which only implement ops.stopping(). This is a preparatory change for enabling proxy execution together with sched_ext. The explicit running-state tracking is also required by later donor-based accounting: it prevents an EXT owner executing for a non-EXT donor from being treated as the active EXT scheduling context when it is dequeued. Signed-off-by: Andrea Righi <[email protected]> --- include/linux/sched/ext.h | 2 ++ kernel/sched/ext/ext.c | 33 +++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h index 78b2f289cb986..901772d8ec15d 100644 --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -103,6 +103,8 @@ enum scx_ent_flags { SCX_TASK_SUB_INIT = 1 << 4, /* task being initialized for a sub sched */ SCX_TASK_IMMED = 1 << 5, /* task is on local DSQ with %SCX_ENQ_IMMED */ + SCX_TASK_RUN_TRACKED = 1 << 6, /* task is in an ops.running()/stopping() session */ + /* * Bits 8 to 10 are used to carry task state: * diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index fd1f55c53662b..30d5965d09108 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -2217,9 +2217,13 @@ static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int core_deq_ * information meaningful to the BPF scheduler and can be suppressed by * skipping the callbacks if the task is !QUEUED. */ - if (SCX_HAS_OP(sch, stopping) && task_current(rq, p)) { - update_curr_scx(rq); - SCX_CALL_OP_TASK(sch, stopping, rq, p, false); + if (task_current(rq, p) && + (p->scx.flags & SCX_TASK_RUN_TRACKED)) { + if (SCX_HAS_OP(sch, stopping)) { + update_curr_scx(rq); + SCX_CALL_OP_TASK(sch, stopping, rq, p, false); + } + p->scx.flags &= ~SCX_TASK_RUN_TRACKED; } if (SCX_HAS_OP(sch, quiescent) && !task_on_rq_migrating(p)) @@ -2921,9 +2925,17 @@ static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first) p->se.exec_start = rq_clock_task(rq); - /* see dequeue_task_scx() on why we skip when !QUEUED */ - if (SCX_HAS_OP(sch, running) && (p->scx.flags & SCX_TASK_QUEUED)) - SCX_CALL_OP_TASK(sch, running, rq, p); + /* + * See dequeue_task_scx() for why we skip when !QUEUED. A blocked task can + * remain queued here only as a retained proxy donor. Skip it because it + * provides scheduling context but never runs itself. + */ + if ((p->scx.flags & SCX_TASK_QUEUED) && !p->is_blocked) { + if (SCX_HAS_OP(sch, running)) + SCX_CALL_OP_TASK(sch, running, rq, p); + + p->scx.flags |= SCX_TASK_RUN_TRACKED; + } clr_task_runnable(p, true); @@ -3029,8 +3041,13 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, update_curr_scx(rq); /* see dequeue_task_scx() on why we skip when !QUEUED */ - if (SCX_HAS_OP(sch, stopping) && (p->scx.flags & SCX_TASK_QUEUED)) - SCX_CALL_OP_TASK(sch, stopping, rq, p, true); + if ((p->scx.flags & SCX_TASK_QUEUED) && + (p->scx.flags & SCX_TASK_RUN_TRACKED)) { + if (SCX_HAS_OP(sch, stopping)) + SCX_CALL_OP_TASK(sch, stopping, rq, p, true); + + p->scx.flags &= ~SCX_TASK_RUN_TRACKED; + } if (p->scx.flags & SCX_TASK_QUEUED) { set_task_runnable(rq, p); -- 2.55.0