sched_ext: Don't enable non-ext tasks in the sub-sched task loops

Root enable and scx_post_fork() enable a task only if it's on the ext class.
Tasks on other classes, possible under an SCX_OPS_SWITCH_PARTIAL root, are
left READY and enabled by switching_to_scx() when they switch over. The sub
enable-commit pass and the sub-disable re-home loop enable unconditionally,
so a fair-class READY task in the subtree becomes ENABLED while not on
sched_ext. A later switch to SCHED_EXT then trips the task state validation
WARN (ENABLED with the previous state not READY) and calls ops.enable() a
second time.

Gate scx_enable_task() on the task's class in both loops.

Fixes: 337ec00b1d ("sched_ext: Implement cgroup sub-sched enabling and disabling")
Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Andrea Righi <arighi@nvidia.com>
This commit is contained in:
Tejun Heo 2026-07-16 10:46:04 -10:00
parent 8c13364db9
commit 5cdc928598

View File

@ -6010,15 +6010,22 @@ static void scx_sub_disable(struct scx_sched *sch)
/* /*
* $p is initialized for $parent and still attached to * $p is initialized for $parent and still attached to
* @sch. Disable and exit for @sch, switch over to * @sch. Disable and exit for @sch, switch over to
* $parent, override the state to READY to account for * $parent and override the state to READY to account
* $p having already been initialized, and then enable. * for $p having already been initialized.
*/ */
scx_disable_and_exit_task(sch, p); scx_disable_and_exit_task(sch, p);
scx_set_task_state(p, SCX_TASK_INIT_BEGIN); scx_set_task_state(p, SCX_TASK_INIT_BEGIN);
scx_set_task_state(p, SCX_TASK_INIT); scx_set_task_state(p, SCX_TASK_INIT);
scx_set_task_sched(p, parent); scx_set_task_sched(p, parent);
scx_set_task_state(p, SCX_TASK_READY); scx_set_task_state(p, SCX_TASK_READY);
scx_enable_task(parent, p);
/*
* A task on a non-ext class, possible under an
* %SCX_OPS_SWITCH_PARTIAL root, stays READY and is
* enabled by switching_to_scx() if it switches over.
*/
if (p->sched_class == &ext_sched_class)
scx_enable_task(parent, p);
} }
task_rq_unlock(rq, p, &rf); task_rq_unlock(rq, p, &rf);
@ -7726,10 +7733,14 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
/* /*
* $p is now only initialized for @sch and READY, which * $p is now only initialized for @sch and READY, which
* is what we want. Assign it to @sch and enable. * is what we want. Assign it to @sch and, if it's on
* the ext class, enable. A non-ext task, possible under
* an %SCX_OPS_SWITCH_PARTIAL root, stays READY and is
* enabled by switching_to_scx() if it switches over.
*/ */
scx_set_task_sched(p, sch); scx_set_task_sched(p, sch);
scx_enable_task(sch, p); if (p->sched_class == &ext_sched_class)
scx_enable_task(sch, p);
p->scx.flags &= ~SCX_TASK_SUB_INIT; p->scx.flags &= ~SCX_TASK_SUB_INIT;
} }