summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2026-10-26 14:17:33 -1000
committerTejun Heo <tj@kernel.org>2026-10-27 09:40:56 -1000
commit15c5a52c1f193159f0a2faa1e3b03d112125cc17 (patch)
treee26a89560f463d9416c4179ab5974fbc19928782 /kernel
parent2e68cfad33faad70187df89b4c9f93ae439b7946 (diff)
sched_ext: Clear holding_cpu where the claim is confirmed
The bypass load balancer will take tasks off a remote cpu's bypass DSQ with unlink_dsq_and_switch_rq_lock() in the following patches and insert them into another bypass DSQ. Clearing holding_cpu after the claim is up to each claimant, so the balancer would need a clear of its own. unlink_dsq_and_switch_rq_lock() marks a task with holding_cpu for the gap between dropping the DSQ lock and taking the task's rq lock, so that a dequeue in that gap can tell the claimant it lost. Once the claimant holds the rq lock and finds the marker intact, nothing can take the task away under it and the marker has no reader left. Each claimant clears it at its own site after that point: - dispatch_to_local_dsq(), which runs the handshake inline, in its same-rq and fallback branches - scx_proxy_reject_task() for the proxy reject - the dequeue inside deactivate_task() for both remote moves Clear it where the claim is confirmed instead and drop the explicit clears. No behavior change. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/sched/ext/ext.c19
-rw-r--r--kernel/sched/ext/internal.h11
2 files changed, 17 insertions, 13 deletions
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 3c3fb8c18eab..afd961aad11d 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -2865,7 +2865,6 @@ static void scx_proxy_reject_task(struct scx_sched *sch, struct rq *rq,
lockdep_assert_rq_held(rq);
WARN_ON_ONCE(p->scx.flags & SCX_TASK_REENQ_REASON_MASK);
- p->scx.holding_cpu = -1;
p->scx.flags |= SCX_TASK_REENQ_PROXY;
scx_divert_strip_flags(p, &enq_flags);
@@ -2899,8 +2898,9 @@ static void scx_proxy_reject_task(struct scx_sched *sch, struct rq *rq,
* tell whether we lost to dequeue by testing whether the holding_cpu still
* points to this CPU. See scx_dispatch_dequeue() for the counterpart.
*
- * On return, @dsq is unlocked and @src_rq is locked. Returns %true if @p is
- * still valid and detached from @dsq. %false if lost to dequeue.
+ * On return, @dsq is unlocked and @src_rq is locked. Return %true if @p is
+ * still valid and detached from @dsq, with holding_cpu cleared. Return %false
+ * if @p was lost to a dequeue.
*/
static bool unlink_dsq_and_switch_rq_lock(struct task_struct *p,
struct scx_dispatch_q *dsq,
@@ -2923,11 +2923,13 @@ static bool unlink_dsq_and_switch_rq_lock(struct task_struct *p,
if (likely(p->scx.holding_cpu == cpu) && !WARN_ON_ONCE(src_rq != task_rq(p))) {
/*
* Keep ->dsq set until we own @src_rq so that a racing dequeue
- * can find @dsq and clear holding_cpu. Once ownership is
- * confirmed, clear it under @src_rq so that deactivate_task()
- * takes the !dsq path instead of retaking @dsq->lock.
+ * can find @dsq and clear holding_cpu. Once we own @src_rq, the
+ * handshake is over. Clear ->dsq so that deactivate_task()
+ * takes the !dsq path instead of retaking @dsq->lock. Clear
+ * holding_cpu with it so that no caller has to.
*/
p->scx.dsq = NULL;
+ p->scx.holding_cpu = -1;
scx_wait_dispatching(p);
return true;
}
@@ -3163,20 +3165,21 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
!WARN_ON_ONCE(src_rq != task_rq(p))) {
bool fallback = false;
+ /* the handshake is over, see unlink_dsq_and_switch_rq_lock() */
+ p->scx.holding_cpu = -1;
+
/*
* If @p is staying on the same rq, there's no need to go
* through the full deactivate/activate cycle. Optimize by
* abbreviating move_remote_task_to_local_dsq().
*/
if (src_rq == dst_rq) {
- p->scx.holding_cpu = -1;
scx_dispatch_enqueue(sch, dst_rq, &dst_rq->scx.local_dsq, p,
slice, vtime, enq_flags | SCX_ENQ_APPLY_SLICE);
} else if (unlikely(task_proxy_unsafe_to_move(p))) {
fallback = true;
scx_proxy_reject_task(sch, src_rq, p, enq_flags);
} else if (unlikely(!task_can_run_on_remote_rq(sch, p, dst_rq, true))) {
- p->scx.holding_cpu = -1;
fallback = true;
scx_dispatch_enqueue(sch, src_rq, find_global_dsq(sch, task_cpu(p)),
p, slice, vtime,
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 80c746054ce7..752a9a2a94b0 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -2157,12 +2157,13 @@ static const char *scx_enable_state_str[] = {
* 4. Lock src_rq (where the task currently lives)
* 5. Verify p->scx.holding_cpu == current CPU, if not, dequeue won the
* race (dequeue clears holding_cpu to -1 when it takes the task), in
- * this case migration is aborted
- * 6. If src_rq == dst_rq: clear holding_cpu and enqueue directly
- * into dst_rq's local DSQ (no lock swap needed)
+ * this case migration is aborted. Otherwise the claim is confirmed and
+ * holding_cpu is cleared here, see unlink_dsq_and_switch_rq_lock()
+ * 6. If src_rq == dst_rq: enqueue directly into dst_rq's local DSQ (no lock
+ * swap needed)
* 7. Otherwise, verify under src_rq lock that the task can be moved to dst_rq
- * (CPU affinity, migration_disabled, etc.). If not, clear holding_cpu,
- * leave the task on src_rq, and enqueue it on the fallback DSQ.
+ * (CPU affinity, migration_disabled, etc.). If not, leave the task on
+ * src_rq, and enqueue it on the fallback DSQ.
* 8. Otherwise (i.e. if the task can be moved to dst_rq), call
* move_remote_task_to_local_dsq(), which releases src_rq, locks dst_rq,
* and performs the deactivate/activate migration cycle