mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 13:14:02 +02:00
sched_ext: Use @prev's scheduler for the keep decisions in dispatch_one()
dispatch_one() tests ops flags and bypass state against the root scheduler
in both places where it decides to keep running @prev: the early keep of a
@prev with slice left tests the root's bypass state, and the keep-last at
the end tests the root's SCX_OPS_ENQ_LAST and bypass state. Both are
properties of the scheduler @prev belongs to, and put_prev_task_scx(), which
acts on the outcome, reads them from that scheduler. When @prev belongs to a
sub-scheduler the two sides disagree.
The keep-last case is visible. The root set SCX_OPS_ENQ_LAST, so a lone
@prev of a sub-scheduler is not kept and is enqueued with SCX_ENQ_LAST to a
sub-scheduler that never opted in. This trips the WARN_ON_ONCE in
put_prev_task_scx() for the missing flag, and the sub-scheduler queues the
task like any other and triggers no follow-up scheduling event, which can
lead to stalls.
Test SCX_OPS_ENQ_LAST and bypass state on @prev's sched in both places and
charge SCX_EV_DISPATCH_KEEP_LAST to it. Read the sched at each decision, as
the dispatch in between can drop the rq lock.
Fixes: 88234b075c ("sched_ext: Introduce scx_task_sched[_rcu]()")
Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Andrea Righi <arighi@nvidia.com>
This commit is contained in:
parent
3265ef0b67
commit
90f19b2816
|
|
@ -2955,7 +2955,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
|
|||
* test.
|
||||
*/
|
||||
if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice &&
|
||||
!scx_bypassing(root_sch, cpu)) {
|
||||
!scx_bypassing(scx_task_sched(prev), cpu)) {
|
||||
verdict = SCX_DSP_PREV;
|
||||
goto has_tasks;
|
||||
}
|
||||
|
|
@ -2972,15 +2972,20 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
|
|||
goto has_tasks;
|
||||
|
||||
/*
|
||||
* Didn't find another task to run. Keep running @prev unless
|
||||
* %SCX_OPS_ENQ_LAST is in effect.
|
||||
* Didn't find another task to run. Keep running @prev unless its own
|
||||
* scheduler set %SCX_OPS_ENQ_LAST and takes the enqueue instead, see
|
||||
* put_prev_task_scx(). Read the scheduler here as the dispatch above
|
||||
* may have dropped the rq lock while @prev changed class or scheduler.
|
||||
*/
|
||||
if ((prev->scx.flags & SCX_TASK_QUEUED) &&
|
||||
(!(root_sch->ops.flags & SCX_OPS_ENQ_LAST) || scx_bypassing(root_sch, cpu)) &&
|
||||
scx_task_can_stay_on_cpu(rq, prev)) {
|
||||
__scx_add_event(root_sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
|
||||
verdict = SCX_DSP_PREV;
|
||||
goto has_tasks;
|
||||
if (prev->scx.flags & SCX_TASK_QUEUED) {
|
||||
struct scx_sched *prev_sch = scx_task_sched(prev);
|
||||
|
||||
if ((!(prev_sch->ops.flags & SCX_OPS_ENQ_LAST) ||
|
||||
scx_bypassing(prev_sch, cpu)) && scx_task_can_stay_on_cpu(rq, prev)) {
|
||||
__scx_add_event(prev_sch, SCX_EV_DISPATCH_KEEP_LAST, 1);
|
||||
verdict = SCX_DSP_PREV;
|
||||
goto has_tasks;
|
||||
}
|
||||
}
|
||||
rq->scx.flags &= ~SCX_RQ_IN_DISPATCH;
|
||||
return SCX_DSP_NONE;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user