Re: [PATCH 07/12] sched_ext: Split curr|donor references properly

[email protected] Tue, 21 Jul 2026 08:13:41 +0000
Newsgroups dev.linux.lists.sched-ext
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] task_tick_scx() unconditionally invokes ops.tick() for blocked proxy donors, violating the BPF state machine contract.
- [Medium] Missing task_current_donor() check in enqueue_task_scx() violates DSQ invariants for proxy donors.
--

--- Patch [7]: [PATCH 07/12] sched_ext: Split curr|donor references properly ---
commit 0a1e9539e4e14034ca89a7b3b4988f413e2e416b
Author: Andrea Righi <[email protected]>

sched_ext: Split curr|donor references properly

This commit updates the sched_ext scheduler class to distinguish between the
physical execution context and the scheduling context during proxy execution.
It ensures that sched_ext policy, accounting, and various BPF kfuncs observe
the donor task instead of the physically executing task.

> diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
> index b5fd91d9bca4f..a466adf8e1548 100644
> --- a/kernel/sched/ext/ext.c
> +++ b/kernel/sched/ext/ext.c

[ ... ]

> @@ -3566,9 +3577,9 @@ void scx_tick(struct rq *rq)
>  	update_other_load_avgs(rq);
>  }
>  
> -static void task_tick_scx(struct rq *rq, struct task_struct *curr, int queued)
> +static void task_tick_scx(struct rq *rq, struct task_struct *donor, int queued)
>  {
> -	struct scx_sched *sch = scx_task_sched(curr);
> +	struct scx_sched *sch = scx_task_sched(donor);
>  
>  	update_curr_scx(rq);
>  
> @@ -3577,13 +3588,13 @@ static void task_tick_scx(struct rq *rq, struct task_struct *curr, int queued)
>  	 * we can't trust the slice management or ops.core_sched_before().
>  	 */
>  	if (scx_bypassing(sch, cpu_of(rq))) {
> -		set_task_slice(curr, 0);
> -		touch_core_sched(rq, curr);
> +		set_task_slice(donor, 0);
> +		touch_core_sched(rq, donor);
>  	} else if (SCX_HAS_OP(sch, tick)) {
> -		SCX_CALL_OP_TASK(sch, tick, rq, curr);
> +		SCX_CALL_OP_TASK(sch, tick, rq, donor);
>  	}

[Severity: High]
Does this code incorrectly call ops.tick() for a blocked donor?

The commit message explicitly states that a blocked donor does not enter an
ops.running() or ops.stopping() session. However, this unconditionally invokes
ops.tick() for the donor without verifying if it is blocked or tracked as
running.

Since BPF schedulers may allocate per-CPU state upon ops.running(), could this
crash or leak state when ops.tick() fires for a task the scheduler never
tracked as running?

[ ... ]

> @@ -9172,13 +9195,15 @@ __bpf_kfunc bool scx_bpf_task_set_slice(struct task_struct *p, u64 slice,
>  		return false;
>  
>  	/*
> -	 * Directly write only when we hold the lock of the rq @p is queued or
> -	 * running on. See the slice write rules above.
> +	 * Directly write only when we hold the lock of the rq @p is queued on or
> +	 * provides the current scheduling context for. Under proxy execution,
> +	 * rq->donor owns and consumes the slice while rq->curr executes on its
> +	 * behalf. See the slice write rules above.
>  	 */
>  	locked_rq = scx_locked_rq();
>  	if (!locked_rq ||
>  	    (READ_ONCE(p->scx.runnable_cpu) != cpu_of(locked_rq) &&
> -	     !task_current(locked_rq, p))) {
> +	     !task_current_donor(locked_rq, p))) {
>  		set_task_slice_oob(sch, p, slice);
>  		return true;
>  	}

This patch updates several task_current() checks to task_current_donor(),
but it appears one was missed in enqueue_task_scx():

kernel/sched/ext/ext.c:enqueue_task_scx() {
    ...
	/*
	 * Restoring a running task will be immediately followed by
	 * set_next_task_scx() which expects the task to not be on the BPF
	 * scheduler as tasks can only start running through local DSQs. Force
	 * direct-dispatch into the local DSQ by setting the sticky_cpu. Mark
	 * IGNORE_CAPS to force entry into the local DSQ.
	 */
	if (unlikely(enq_flags & ENQUEUE_RESTORE) && task_current(rq, p)) {
		sticky_cpu = cpu_of(rq);
		enq_flags |= SCX_ENQ_IGNORE_CAPS;
	}
    ...
}

[Severity: Medium]
Should this use task_current_donor(rq, p) instead of task_current(rq, p)?

If a blocked proxy donor undergoes priority inheritance, it could be
dequeued and re-enqueued with ENQUEUE_RESTORE. Since the donor is not
physically rq->curr, task_current(rq, p) evaluates to false.

Would this bypass the local DSQ assignment and erroneously push the active
proxy donor into the BPF scheduler instead of the local DSQ?

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=7