Merge branch 'for-7.2-fixes' into for-7.3

Pull to receive:

 477869bfaf ("sched_ext: Reject setting disallow from init_task outside the enable path")
 5f8b69642d ("sched_ext: Take cgroup_lock() first in scx_cgroup_lock()")
 8c13364db9 ("sched_ext: Skip sub-disable teardown for never-linked sub-schedulers")
 5cdc928598 ("sched_ext: Don't enable non-ext tasks in the sub-sched task loops")

as dependencies for the upcoming cgroup migration patchset and to
resolve the conflicts with the ext.c/sub.c split on for-7.3.

5f8b69642d comments scx_cgroup_lock() which for-7.3 exported for
sub.c. Resolved by keeping the exported version with the comment.

8c13364db9 and 5cdc928598 patch the pre-split sub-sched enable and
disable paths in ext.c which for-7.3 moved to sub.c. Resolved by
applying the never-linked teardown skip and the class gates to sub.c.

Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Tejun Heo 2026-07-17 21:42:18 -10:00
commit 7c2cd76770
3 changed files with 54 additions and 14 deletions

View File

@ -265,11 +265,11 @@ struct sched_ext_entity {
* to %SCHED_EXT with -%EACCES.
*
* Can be set from ops.init_task() while the BPF scheduler is being
* loaded (!scx_init_task_args->fork). If set and the task's policy is
* already %SCHED_EXT, the task's policy is rejected and forcefully
* reverted to %SCHED_NORMAL. The number of such events are reported
* through /sys/kernel/debug/sched_ext::nr_rejected. Setting this flag
* during fork is not allowed.
* loaded. If set and the task's policy is already %SCHED_EXT, the
* task's policy is rejected and forcefully reverted to %SCHED_NORMAL.
* The number of such events are reported through
* /sys/kernel/sched_ext/nr_rejected. Setting this flag from any other
* ops.init_task() invocation, such as during fork, fails the scheduler.
*/
bool disallow; /* reject switching into SCX */

View File

@ -3511,6 +3511,9 @@ int __scx_init_task(struct scx_sched *sch, struct task_struct *p, bool fork)
} else if (unlikely(fork)) {
scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] during fork",
p->comm, p->pid);
} else if (unlikely(scx_enable_state() != SCX_ENABLING)) {
scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] outside the enable path",
p->comm, p->pid);
} else {
struct rq *rq;
struct rq_flags rf;
@ -3872,6 +3875,17 @@ static void reweight_task_scx(struct rq *rq, struct task_struct *p,
if (task_dead_and_done(p))
return;
/*
* When switching sched_class away from SCX, reweight_task_scx()
* is called _after_ scx_disable_task(). Skip calling ops.set_weight()
* since the BPF scheduler may have already forgotten the task in
* ops.disable().
* p->scx.weight will be recalculated in scx_enable_task() if the task
* ever returns to SCX class.
*/
if (scx_get_task_state(p) != SCX_TASK_ENABLED)
return;
p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight));
if (SCX_HAS_OP(sch, set_weight))
SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight);
@ -4495,20 +4509,25 @@ static struct cgroup *root_cgroup(void)
return &cgrp_dfl_root.cgrp;
}
/*
* cgroup_lock() must nest outside the rwsem write side: a writer waiting
* for cgroup_mutex deadlocks with cgroup teardown, which holds it while
* draining a set_* file write blocked on the rwsem behind the writer.
*/
void scx_cgroup_lock(void)
{
cgroup_lock();
#ifdef CONFIG_EXT_GROUP_SCHED
percpu_down_write(&scx_cgroup_ops_rwsem);
#endif
cgroup_lock();
}
void scx_cgroup_unlock(void)
{
cgroup_unlock();
#ifdef CONFIG_EXT_GROUP_SCHED
percpu_up_write(&scx_cgroup_ops_rwsem);
#endif
cgroup_unlock();
}
#else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */
static inline struct cgroup *root_cgroup(void) { return NULL; }

View File

@ -812,6 +812,15 @@ void scx_sub_disable(struct scx_sched *sch)
percpu_down_write(&scx_fork_rwsem);
scx_cgroup_lock();
/*
* An enable that failed before scx_link_sched() never owned a cgroup or
* task and won't be waited on by an ancestor's drain_descendants().
* Nothing to reparent and walking the tasks can misbehave as the task
* ownership invariant (either owned by self or parent) does not hold.
*/
if (list_empty(&sch->sibling))
goto dump;
set_cgroup_sched(sch_cgroup(sch), parent);
scx_task_iter_start(&sti, sch->cgrp);
@ -824,8 +833,8 @@ void scx_sub_disable(struct scx_sched *sch)
continue;
/*
* By the time control reaches here, all descendant schedulers
* should already have been disabled.
* By the time control reaches here, all linked descendant
* schedulers should have been disabled.
*/
WARN_ON_ONCE(!scx_task_on_sched(sch, p));
@ -876,15 +885,22 @@ void scx_sub_disable(struct scx_sched *sch)
/*
* $p is initialized for $parent and still attached to
* @sch. Disable and exit for @sch, switch over to
* $parent, override the state to READY to account for
* $p having already been initialized, and then enable.
* $parent and override the state to READY to account
* for $p having already been initialized.
*/
scx_disable_and_exit_task(sch, p);
scx_set_task_state(p, SCX_TASK_INIT_BEGIN);
scx_set_task_state(p, SCX_TASK_INIT);
scx_set_task_sched(p, parent);
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);
@ -892,6 +908,7 @@ void scx_sub_disable(struct scx_sched *sch)
}
scx_task_iter_stop(&sti);
dump:
scx_disable_dump(sch);
scx_cgroup_unlock();
@ -1219,10 +1236,14 @@ void scx_sub_enable_workfn(struct kthread_work *work)
/*
* $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_enable_task(sch, p);
if (p->sched_class == &ext_sched_class)
scx_enable_task(sch, p);
p->scx.flags &= ~SCX_TASK_SUB_INIT;
}