summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/sched/ext/ext.c31
1 files changed, 18 insertions, 13 deletions
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 90b3df2cdbbe..27b027833f06 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -3658,6 +3658,8 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
(rq->scx.flags & SCX_RQ_PROXY_PENDING);
bool proxy_block = rq->scx.flags & SCX_RQ_PROXY_BLOCKING;
bool rescue_keep = false;
+ bool going_idle = !proxy_put && next &&
+ sched_class_above(&ext_sched_class, next->sched_class);
rq->scx.flags &= ~SCX_RQ_PROXY_PENDING;
@@ -3763,25 +3765,28 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
* locally runnable and can legitimately go idle with @p still
* runnable (see do_pick_task_scx()).
*/
- if (!proxy_put && next &&
- sched_class_above(&ext_sched_class, next->sched_class) &&
- scx_task_can_stay_on_cpu(rq, p)) {
+ if (going_idle && scx_task_can_stay_on_cpu(rq, p)) {
WARN_ON_ONCE(!sched_core_enabled(rq) &&
!(sch->ops.flags & SCX_OPS_ENQ_LAST));
scx_do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
-
- /*
- * A task put on the local DSQ runs, as anywhere else.
- * Here the insert can't reschedule on its own: the pick
- * has settled on @next and @p is still curr, so
- * resched_curr() would flag @p and __schedule() clears
- * that right after the pick. Flag @next instead.
- */
- if (p->scx.dsq == &rq->scx.local_dsq)
- set_tsk_need_resched(next);
} else {
scx_do_enqueue_task(rq, p, 0, -1);
}
+
+ /*
+ * @p may be on the local DSQ now. With %SCX_ENQ_LAST the
+ * scheduler can put the last runnable task there. The plain
+ * insert runs when @p cannot stay on this cpu, and rescue can
+ * still admit it here.
+ *
+ * The cpu must not go idle with @p queued on its local DSQ. The
+ * usual resched doesn't work: the pick has settled on @next and
+ * @p is still curr, so resched_curr() would set need_resched on
+ * @p, which __schedule() clears right after the pick. Set it on
+ * @next instead.
+ */
+ if (going_idle && p->scx.dsq == &rq->scx.local_dsq)
+ set_tsk_need_resched(next);
}
switch_class: