[PATCH 02/11] sched: Add helper to block retained proxy donors
Andrea Righi <[email protected]> Thu, 16 Jul 2026 15:20:37 +0200
| Newsgroups | dev.linux.lists.sched-ext,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Scheduler ownership changes may need to turn a mutex-blocked task retained on the runqueue for proxy execution back into a normally blocked task. Add sched_proxy_block_task() to perform that transition while holding p->pi_lock and the task's runqueue lock. Move an active donor off the CPU first, and complete any delayed dequeue so a following sched_change cannot preserve and re-enqueue the task. This is a preparatory change to support proxy execution with sched_ext. Signed-off-by: Andrea Righi <[email protected]> --- kernel/sched/core.c | 39 +++++++++++++++++++++++++++++++++++++++ kernel/sched/sched.h | 6 ++++++ 2 files changed, 45 insertions(+) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f47bcfb25033a..e7827cffc2f0c 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6764,6 +6764,45 @@ static void proxy_deactivate(struct rq *rq, struct task_struct *donor) block_task(rq, donor, state); } +/* + * Remove a retained proxy donor before changing its scheduler ownership. + * The caller holds p->pi_lock, so p cannot wake and migrate after block_task() + * drops it from the runqueue. + * + * Unlike the regular schedule() path, this must leave @p fully dequeued. + * DELAY_DEQUEUE may keep a blocked fair task queued with sched_delayed set, + * which would let the following sched_change preserve and re-enqueue it under + * the new scheduler. Complete an existing or newly-created delayed dequeue + * before returning. + */ +void sched_proxy_block_task(struct rq *rq, struct task_struct *p) +{ + unsigned long state = READ_ONCE(p->__state); + + lockdep_assert_held(&p->pi_lock); + lockdep_assert_rq_held(rq); + + if (!p->is_blocked || !task_on_rq_queued(p)) + return; + if (WARN_ON_ONCE(state == TASK_RUNNING)) + return; + + if (task_current_donor(rq, p)) { + proxy_resched_idle(rq); + /* Kick the execution context if @rq is remote. */ + resched_curr(rq); + } + + if (!p->se.sched_delayed) + block_task(rq, p, state); + if (p->se.sched_delayed) + dequeue_task(rq, p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | + DEQUEUE_NOCLOCK); + + WARN_ON_ONCE(task_on_rq_queued(p)); + WARN_ON_ONCE(p->se.sched_delayed); +} + static inline void proxy_release_rq_lock(struct rq *rq, struct rq_flags *rf) __releases(__rq_lockp(rq)) { diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 64d79e9efc3d1..437c4f68c9e53 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2477,6 +2477,12 @@ static inline bool task_is_blocked(struct task_struct *p) return !!p->blocked_on; } +#ifdef CONFIG_SCHED_PROXY_EXEC +void sched_proxy_block_task(struct rq *rq, struct task_struct *p); +#else +static inline void sched_proxy_block_task(struct rq *rq, struct task_struct *p) {} +#endif + static inline int task_on_cpu(struct rq *rq, struct task_struct *p) { return p->on_cpu; -- 2.55.0