sched_ext: Block proxy donors before taking control

Proxy execution retains mutex-blocked donors on the runqueue so their
scheduling context can execute a lock owner. sched_ext cannot safely
inherit such donors unless the BPF scheduler explicitly participates in
their admission and ordering.

Make sched_ext reject retained donors by default. Implement
scx_allow_proxy_exec() to force blocked EXT tasks through the regular
block path in schedule().

A donor retained by another scheduling class may already be queued when
sched_ext is enabled or when a task switches to SCHED_EXT. Fully block
the donor before sched_change_begin() records its queued state. This
prevents sched_ext from importing a proxy session which the BPF
scheduler did not admit, while leaving ordinary FAIR, RT and DL class
transitions untouched.

This is a preparatory change to support proxy execution with sched_ext.

Signed-off-by: Andrea Righi <arighi@nvidia.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Andrea Righi 2026-09-22 18:51:45 +02:00 committed by Tejun Heo
parent 6ff8452f63
commit be8be8acb2
3 changed files with 39 additions and 2 deletions

View File

@ -26,7 +26,22 @@ DEFINE_RAW_SPINLOCK(scx_sched_lock);
bool scx_allow_proxy_exec(const struct task_struct *p)
{
return true;
return p->sched_class != &ext_sched_class;
}
/*
* End retained proxy execution before sched_ext takes ownership of @p.
* Called with @p's pi and rq locks held immediately before
* sched_change_begin(). The caller must pass DEQUEUE_NOCLOCK so the rq clock
* is updated only once.
*/
static void scx_prepare_task_sched_change(struct task_struct *p)
{
lockdep_assert_held(&p->pi_lock);
lockdep_assert_rq_held(task_rq(p));
update_rq_clock(task_rq(p));
sched_proxy_block_task(task_rq(p), p);
}
/*
@ -4438,7 +4453,7 @@ int scx_check_setscheduler(struct task_struct *p, int policy)
{
lockdep_assert_rq_held(task_rq(p));
/* if disallow, reject transitioning into SCX */
/* If disallow, reject transitioning into SCX. */
if (scx_enabled() && READ_ONCE(p->scx.disallow) &&
p->policy != policy && policy == SCHED_EXT)
return -EACCES;
@ -4446,6 +4461,20 @@ int scx_check_setscheduler(struct task_struct *p, int policy)
return 0;
}
/*
* Don't carry a donor retained by another class into sched_ext. The caller
* has updated the rq clock and invokes this immediately before
* sched_change_begin() records the task's queued state.
*/
void scx_prepare_setscheduler(struct task_struct *p, int policy)
{
lockdep_assert_held(&p->pi_lock);
lockdep_assert_rq_held(task_rq(p));
if (scx_enabled() && p->policy != policy && policy == SCHED_EXT)
sched_proxy_block_task(task_rq(p), p);
}
static void process_ddsp_deferred_locals(struct rq *rq)
{
struct task_struct *p;
@ -7994,6 +8023,10 @@ static void scx_root_enable_workfn(struct kthread_work *work)
if (old_class != new_class)
queue_flags |= DEQUEUE_CLASS;
if (new_class == &ext_sched_class) {
scx_prepare_task_sched_change(p);
queue_flags |= DEQUEUE_NOCLOCK;
}
scoped_guard (sched_change, p, queue_flags) {
scx_set_task_slice(p, READ_ONCE(sch->slice_dfl));

View File

@ -18,6 +18,7 @@ bool scx_can_stop_tick(struct rq *rq);
void scx_rq_activate(struct rq *rq);
void scx_rq_deactivate(struct rq *rq);
int scx_check_setscheduler(struct task_struct *p, int policy);
void scx_prepare_setscheduler(struct task_struct *p, int policy);
bool task_should_scx(int policy);
bool scx_allow_ttwu_queue(const struct task_struct *p);
bool scx_allow_proxy_exec(const struct task_struct *p);
@ -62,6 +63,7 @@ static inline bool scx_can_stop_tick(struct rq *rq) { return true; }
static inline void scx_rq_activate(struct rq *rq) {}
static inline void scx_rq_deactivate(struct rq *rq) {}
static inline int scx_check_setscheduler(struct task_struct *p, int policy) { return 0; }
static inline void scx_prepare_setscheduler(struct task_struct *p, int policy) {}
static inline bool task_on_scx(const struct task_struct *p) { return false; }
static inline bool scx_allow_ttwu_queue(const struct task_struct *p) { return true; }
static inline bool scx_allow_proxy_exec(const struct task_struct *p) { return true; }

View File

@ -678,6 +678,8 @@ int __sched_setscheduler(struct task_struct *p,
if (prev_class != next_class)
queue_flags |= DEQUEUE_CLASS;
scx_prepare_setscheduler(p, policy);
scoped_guard (sched_change, p, queue_flags) {
if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS)) {