sched_ext: Merge branch 'for-7.3-fixes' into for-7.4

Pull to receive 680edc7f23 ("sched_ext: Don't queue balance callbacks from
a core-sched pick") and resolve its conflict with the proxy execution work
here so that for-next merges clean.

Conflicts:

- do_pick_task_scx(): 680edc7f23 samples sched_core_enabled() into a new
  local next to the prev declaration, which 3d6577ea93 ("sched_ext: Split
  curr|donor references properly") changed to rq->donor. Kept rq->donor and
  added the local.

Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Tejun Heo 2026-10-17 12:08:31 -10:00
commit 9c0af54cb4

View File

@ -3735,7 +3735,8 @@ static struct task_struct *first_local_task(struct rq *rq)
}
/*
* Run dispatch and queue the follow-up work for a pick.
* Run dispatch for a pick. The follow-up work is queued by do_pick_task_scx()
* once the pick is final.
*/
static enum scx_dsp_verdict dispatch_pick(struct rq *rq, struct rq_flags *rf,
struct task_struct *prev)
@ -3745,18 +3746,6 @@ static enum scx_dsp_verdict dispatch_pick(struct rq *rq, struct rq_flags *rf,
rq_unpin_lock(rq, rf);
verdict = dispatch_one(rq, prev);
rq_repin_lock(rq, rf);
maybe_queue_balance_callback(rq);
/*
* Defer to a balance callback which can drop rq lock and enable IRQs.
* Waiting directly in the pick path would deadlock against CPUs sending
* us IPIs (e.g. TLB flushes) while we wait for them.
*/
if (unlikely(rq->scx.kick_sync_pending)) {
rq->scx.kick_sync_pending = false;
queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb,
kick_sync_wait_bal_cb);
}
return verdict;
}
@ -3783,23 +3772,20 @@ static enum scx_dsp_verdict dispatch_core_pick(struct rq *rq, struct rq_flags *r
verdict = dispatch_one(rq, prev);
if (cpu_of(rq) == smp_processor_id()) {
maybe_queue_balance_callback(rq);
/* see dispatch_pick() */
if (unlikely(rq->scx.kick_sync_pending)) {
rq->scx.kick_sync_pending = false;
queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb,
kick_sync_wait_bal_cb);
}
} else if (unlikely(rq->scx.flags & SCX_RQ_BAL_CB_PENDING)) {
/*
* Balance callbacks must run in the context that queued them,
* so they can't be queued on another CPU's rq. Run the deferred
* work directly instead.
*/
/*
* A pick that returns a task isn't final under core scheduling: the
* selection goes on to pick the siblings, and their dispatch can
* release the rq lock, which can't happen with balance callbacks
* queued. Don't queue the deferred work as a balance callback, for this
* CPU or a sibling, and run it from irq_work instead.
*
* do_pick_task_scx() leaves %SCX_RQ_BAL_CB_PENDING alone under core
* scheduling, so that the early return above leaves it to the dispatch
* in flight, which handles it here.
*/
if (unlikely(rq->scx.flags & SCX_RQ_BAL_CB_PENDING)) {
rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;
run_deferred(rq);
schedule_deferred(rq);
}
rq_repin_lock(rq, rf);
@ -3822,6 +3808,7 @@ static struct task_struct *
do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
{
struct task_struct *prev = rq->donor;
bool core_sched = sched_core_enabled(rq);
enum scx_dsp_verdict verdict;
struct task_struct *p;
@ -3833,13 +3820,20 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
rq_modified_begin(rq, &ext_sched_class);
if (sched_core_enabled(rq))
/*
* Core scheduling can get enabled while dispatch_pick() has the rq lock
* released. Stick to @core_sched, so that the follow-up work below
* matches the dispatch that ran.
*/
if (core_sched)
verdict = dispatch_core_pick(rq, rf, prev);
else
verdict = dispatch_pick(rq, rf, prev);
if (verdict == SCX_DSP_RETRY)
return RETRY_TASK;
if (verdict == SCX_DSP_RETRY) {
p = RETRY_TASK;
goto out;
}
/*
* If any higher-priority sched class enqueued a runnable task on this
@ -3849,8 +3843,10 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
* If @force_scx is true, always try to pick a SCHED_EXT task,
* regardless of any higher-priority sched classes activity.
*/
if (!force_scx && rq_modified_above(rq, &ext_sched_class))
return RETRY_TASK;
if (!force_scx && rq_modified_above(rq, &ext_sched_class)) {
p = RETRY_TASK;
goto out;
}
/*
* If we're keeping @prev, replenish slice if necessary and keep running
@ -3866,7 +3862,7 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
} else {
p = first_local_task(rq);
if (!p)
return NULL;
goto out;
if (unlikely(!p->scx.slice) && scx_task_can_stay_on_cpu(rq, p)) {
struct scx_sched *sch = scx_task_sched(p);
@ -3881,6 +3877,40 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
}
}
out:
if (cpu_of(rq) != smp_processor_id())
return p;
/*
* Balance callbacks must be consumed before the rq lock is released, so
* queue them only when the pick is final. On RETRY_TASK, or after a
* NULL pick from the dl_server, the core continues picking and can
* release the rq lock (e.g., prev_balance() or another dispatch). In
* that case, run the deferred work from irq_work, which doesn't depend
* on the current rq lock section, and leave the kick sync wait pending
* for the next pick.
*/
if (p != RETRY_TASK && (p || !force_scx)) {
/* under core scheduling, see dispatch_core_pick() */
if (!core_sched)
maybe_queue_balance_callback(rq);
/*
* Defer to a balance callback which can drop rq lock and enable
* IRQs. Waiting directly in the pick path would deadlock against
* CPUs sending us IPIs (e.g., TLB flushes) while we wait for
* them.
*/
if (unlikely(rq->scx.kick_sync_pending)) {
rq->scx.kick_sync_pending = false;
queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb,
kick_sync_wait_bal_cb);
}
} else if (!core_sched && (rq->scx.flags & SCX_RQ_BAL_CB_PENDING)) {
rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;
schedule_deferred(rq);
}
return p;
}