[PATCH 04/10] sched/ext: Avoid migrating blocked tasks with proxy execution

From: Andrea Righi

Date: Wed May 06 2026 - 13:47:43 EST


From: John Stultz <jstultz@xxxxxxxxxx>

With proxy execution enabled, mutex blocked tasks stay on the runqueue.
Later with donor migration they will be migrated when necessary by the
core scheduler to boost lock owners.

Don't try to migrate mutex blocked tasks, the proxy logic will handle
that.

Co-developed-by: Andrea Righi <arighi@xxxxxxxxxx>
Signed-off-by: Andrea Righi <arighi@xxxxxxxxxx>
Signed-off-by: John Stultz <jstultz@xxxxxxxxxx>
---
kernel/sched/ext.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index c410afd28fb6d..d64b1283fa851 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -2320,6 +2320,14 @@ static bool task_can_run_on_remote_rq(struct scx_sched *sch,

WARN_ON_ONCE(task_cpu(p) == cpu);

+ /* Make sure tasks aren't on a cpu */
+ if (task_on_cpu(task_rq(p), p))
+ return false;
+
+ /* Don't migrate blocked tasks, proxy-exec will handle this */
+ if (task_is_blocked(p))
+ return false;
+
/*
* If @p has migration disabled, @p->cpus_ptr is updated to contain only
* the pinned CPU in migrate_disable_switch() while @p is being switched
@@ -3063,6 +3071,23 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
if (p->scx.flags & SCX_TASK_QUEUED) {
set_task_runnable(rq, p);

+ /*
+ * Mutex-blocked donors stay queued on the runqueue under proxy
+ * execution, but the donor never runs as itself, proxy-exec
+ * walks the blocked_on chain on the next __schedule() and runs
+ * the lock owner in its place.
+ *
+ * Put the donor on the local DSQ directly, so pick_next_task()
+ * can still see it, find_proxy_task() will be invoked on
+ * next->blocked_on and either run the chain owner here, or call
+ * proxy_force_return() and let BPF make a new dispatch decision
+ * once the task is no longer blocked.
+ */
+ if (task_is_blocked(p)) {
+ dispatch_enqueue(sch, rq, &rq->scx.local_dsq, p, 0);
+ goto switch_class;
+ }
+
/*
* If @p has slice left and is being put, @p is getting
* preempted by a higher priority scheduler class or core-sched
--
2.54.0