Messages in this thread Patch in this message |  | | From | Andrea Righi <> | | Subject | [PATCH 06/10] sched_ext: Fix ops.running/stopping() pairing for proxy-exec donors | | Date | Wed, 6 May 2026 19:45:46 +0200 |
| |
With proxy-exec, pick_next_task() can return a task with blocked_on set (a proxy donor); put_prev_set_next_task() then calls set_next_task_scx() on this "ghost" task, which fires ops.running(). However, the task never actually runs.
If we simply short-circuit set_next_task_scx() for blocked tasks, we break DSQ bookkeeping. If we only skip ops.running(), we create an ops.enqueue() -> ops.stopping() pair without running, because ops.stopping() is still called in put_prev_task_scx().
Fix this by introducing a new flag SCX_TASK_IS_RUNNING to track whether ops.running() was actually called. Skip ops.running() for blocked tasks, and only call ops.stopping() if SCX_TASK_IS_RUNNING is set. This ensures that running and stopping callbacks are perfectly paired even when a blocked task is picked as a proxy donor.
Signed-off-by: Andrea Righi <arighi@nvidia.com> --- include/linux/sched/ext.h | 2 ++ kernel/sched/ext.c | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h index d05efcac794d6..5096c05d7a978 100644 --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -102,6 +102,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_IS_RUNNING = 1 << 6, /* ops.running() has been called */ + /* * Bits 8 and 9 are used to carry task state: * diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index a70f8693b906f..b6d29087ec0e8 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -2164,9 +2164,11 @@ 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)) { + if (SCX_HAS_OP(sch, stopping) && task_current(rq, p) && + (p->scx.flags & SCX_TASK_IS_RUNNING)) { update_curr_scx(rq); SCX_CALL_OP_TASK(sch, stopping, rq, p, false); + p->scx.flags &= ~SCX_TASK_IS_RUNNING; } if (SCX_HAS_OP(sch, quiescent) && !task_on_rq_migrating(p)) @@ -2986,8 +2988,11 @@ 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)) + if (SCX_HAS_OP(sch, running) && (p->scx.flags & SCX_TASK_QUEUED) && + !task_is_blocked(p)) { SCX_CALL_OP_TASK(sch, running, rq, p); + p->scx.flags |= SCX_TASK_IS_RUNNING; + } clr_task_runnable(p, true); @@ -3076,8 +3081,11 @@ 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)) + if (SCX_HAS_OP(sch, stopping) && (p->scx.flags & SCX_TASK_QUEUED) && + (p->scx.flags & SCX_TASK_IS_RUNNING)) { SCX_CALL_OP_TASK(sch, stopping, rq, p, true); + p->scx.flags &= ~SCX_TASK_IS_RUNNING; + } if (p->scx.flags & SCX_TASK_QUEUED) { set_task_runnable(rq, p); -- 2.54.0
|  |