diff options
| author | Tejun Heo <tj@kernel.org> | 2026-10-13 11:26:22 -1000 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-10-13 11:26:22 -1000 |
| commit | a5ba5c61951dc00d296c79e96f0259b87861ce8c (patch) | |
| tree | 0c9f088e82abfb32ee90987b0dd3b533b4fd14e6 | |
| parent | d1350c523f481769182604d758ecfcdf3597b402 (diff) | |
| parent | 2935062be77113c1c4565ca9d0c72180449aafee (diff) | |
sched_ext: Merge branch 'for-7.3-fixes' into for-7.4
Pull to receive 2935062be771 ("sched_ext: Wait for SCX_OPSS_DISPATCHING
before moving tasks off DSQs") and resolve its conflicts with the proxy
execution and reject DSQ work here so that for-next merges clean.
Conflicts:
- unlink_dsq_and_switch_rq_lock(): efa7bc7d7195 ("sched_ext: Avoid relocking
DSQ during remote consumption") clears p->scx.dsq in the success branch
and 2935062be771 adds the wait there. Kept both, the wait after the clear,
so that the proxy reject in consume_remote_task() is covered too.
- scx_reenq_reject(): 18f5c699d1d0 ("sched_ext: Rename
scx_reenq_wait_dispatching() to scx_wait_dispatching()") renames the call
in sub.c while cea8b60e4aee ("sched_ext: Move reject DSQ draining into
core") moved the function into ext.c. Dropped the sub.c side and applied
the rename at the new home.
Signed-off-by: Tejun Heo <tj@kernel.org>
| -rw-r--r-- | kernel/sched/ext/ext.c | 77 | ||||
| -rw-r--r-- | kernel/sched/ext/idle.c | 50 | ||||
| -rw-r--r-- | kernel/sched/ext/internal.h | 2 |
3 files changed, 88 insertions, 41 deletions
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 0e4174fa75b6..a2e0382ce3dd 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -2596,11 +2596,14 @@ static void wakeup_preempt_scx(struct rq *rq, struct task_struct *p, int wake_fl void scx_move_local_task_to_local_dsq(struct scx_sched *sch, struct task_struct *p, u64 enq_flags, struct rq *dst_rq) { - struct scx_dispatch_q *dst_dsq = scx_resolve_local_dsq(sch, dst_rq, p, &enq_flags); + struct scx_dispatch_q *dst_dsq; lockdep_assert_rq_held(dst_rq); WARN_ON_ONCE(p->scx.holding_cpu >= 0); + scx_wait_dispatching(p); + + dst_dsq = scx_resolve_local_dsq(sch, dst_rq, p, &enq_flags); if (enq_flags & (SCX_ENQ_HEAD | SCX_ENQ_PREEMPT | SCX_ENQ_PREEMPT_LAZY)) dsq_insert_head(dst_dsq, p); @@ -2858,6 +2861,7 @@ static bool unlink_dsq_and_switch_rq_lock(struct task_struct *p, * takes the !dsq path instead of retaking @dsq->lock. */ p->scx.dsq = NULL; + scx_wait_dispatching(p); return true; } @@ -2917,6 +2921,8 @@ static struct rq *move_task_between_dsqs(struct scx_sched *sch, lockdep_assert_held(&src_dsq->lock); lockdep_assert_rq_held(src_rq); + scx_wait_dispatching(p); + if (dst_dsq->id == SCX_DSQ_LOCAL) { dst_rq = container_of(dst_dsq, struct rq, scx.local_dsq); /* @@ -4882,11 +4888,12 @@ static bool local_task_should_reenq(struct rq *rq, struct task_struct *p, } /* - * The dispatcher stores the final ops_state after dropping the DSQ lock, so @p - * can be found on a DSQ while still %SCX_OPSS_DISPATCHING. Reenqueueing @p - * before that store lands would have it clobber the new %SCX_OPSS_QUEUED. + * The dispatcher runs ops.dequeue() and stores the final ops_state after + * dropping the DSQ lock, so @p can be found on a DSQ while still + * %SCX_OPSS_DISPATCHING. Wait for the dispatch to finish before operating on + * @p. */ -void scx_reenq_wait_dispatching(struct task_struct *p) +void scx_wait_dispatching(struct task_struct *p) { if (unlikely(atomic_long_read_acquire(&p->scx.ops_state) == SCX_OPSS_DISPATCHING)) wait_ops_state(p, SCX_OPSS_DISPATCHING); @@ -4935,7 +4942,7 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags) if (!local_task_should_reenq(rq, p, &reenq_flags, &reason)) continue; - scx_reenq_wait_dispatching(p); + scx_wait_dispatching(p); scx_dispatch_dequeue(rq, p); WARN_ON_ONCE(p->scx.flags & SCX_TASK_REENQ_REASON_MASK); @@ -5057,7 +5064,7 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag } /* @p is on @dsq, its rq and @dsq are locked */ - scx_reenq_wait_dispatching(p); + scx_wait_dispatching(p); dispatch_dequeue_locked(p, dsq); raw_spin_unlock(&dsq->lock); @@ -5168,7 +5175,7 @@ static void scx_reenq_reject(struct rq *rq) WARN_ON_ONCE(p->migration_pending); } - scx_reenq_wait_dispatching(p); + scx_wait_dispatching(p); scx_dispatch_dequeue(rq, p); list_add_tail(&p->scx.dsq_list.node, &tasks); @@ -6560,15 +6567,12 @@ static void enable_bypass_dsp(struct scx_sched *sch) /* * When a sub-sched bypasses, its tasks are queued on the bypass DSQs of - * the nearest non-bypassing ancestor or root. As enable_bypass_dsp() is - * called iff @sch is not already bypassed due to an ancestor bypassing, - * we can assume that the parent is not bypassing and thus will be the - * host of the bypass DSQs. - * - * While the situation may change in the future, the following - * guarantees that the nearest non-bypassing ancestor or root has bypass - * dispatch enabled while a descendant is bypassing, which is all that's - * required. + * the nearest non-bypassing ancestor or root. The parent hosts them + * unless it is bypassing too. Then they sit further up and the parent + * consumes only its own bypass DSQs, empty until it leaves bypass and + * becomes the host. Either way the nearest non-bypassing ancestor or + * root has bypass dispatch enabled while a descendant is bypassing, + * which is all that's required. * * scx_bypass_dsp_enabled() test is used to determine whether to enter * the bypass dispatch handling path from both bypassing and hosting @@ -6677,35 +6681,46 @@ void scx_bypass(struct scx_sched *sch, bool bypass) struct scx_sched *pos; unsigned long flags; int cpu; + bool changed; raw_spin_lock_irqsave(&scx_bypass_lock, flags); if (bypass) { - if (!inc_bypass_depth(sch)) - goto unlock; - - enable_bypass_dsp(sch); + changed = inc_bypass_depth(sch); + if (changed) + enable_bypass_dsp(sch); } else { - if (!dec_bypass_depth(sch)) - goto unlock; + changed = dec_bypass_depth(sch); } /* * Bypass state is propagated to all descendants - an scx_sched bypasses - * if itself or any of its ancestors are in bypass mode. + * if itself or any of its ancestors are in bypass mode. A descendant's + * depth is the sum of its own bypasses and its ancestors', so every + * call propagates whether or not @sch changed state. The descendants' + * claims are released below with @sch's, after their tasks have moved. */ raw_spin_lock(&scx_sched_lock); scx_for_each_descendant_pre(pos, sch) { if (pos == sch) continue; - if (bypass) - inc_bypass_depth(pos); - else + if (bypass) { + if (inc_bypass_depth(pos)) + enable_bypass_dsp(pos); + } else { dec_bypass_depth(pos); + } } raw_spin_unlock(&scx_sched_lock); /* + * A descendant's depth is at least @sch's, so no bypass state changed + * unless @sch's did. + */ + if (!changed) + goto unlock; + + /* * No task property is changing. We just need to make sure all currently * queued tasks are re-queued according to the new scx_bypassing() * state. As an optimization, walk each rq's runnable_list instead of @@ -6783,8 +6798,12 @@ void scx_bypass(struct scx_sched *sch, bool bypass) } /* disarming must come after moving all tasks out of the bypass DSQs */ - if (!bypass) - scx_disable_bypass_dsp(sch); + if (!bypass) { + guard(raw_spinlock)(&scx_sched_lock); + scx_for_each_descendant_pre(pos, sch) + if (!pos->bypass_depth) + scx_disable_bypass_dsp(pos); + } unlock: raw_spin_unlock_irqrestore(&scx_bypass_lock, flags); } diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c index de5ec21384b0..922fbc3065cc 100644 --- a/kernel/sched/ext/idle.c +++ b/kernel/sched/ext/idle.c @@ -962,12 +962,43 @@ static bool is_bpf_migration_disabled(const struct task_struct *p) return p->migration_disabled; } +static s32 select_cpu_restricted(struct task_struct *p, s32 prev_cpu, + const struct cpumask *allowed, u64 flags, + bool migration_disabled) +{ + s32 cpu; + + if (migration_disabled) + cpu = task_cpu(p); + else + cpu = cpumask_first(p->cpus_ptr); + + if (!cpumask_test_cpu(cpu, p->cpus_ptr)) + return -EBUSY; + + if (allowed && !cpumask_test_cpu(cpu, allowed)) + return -EBUSY; + + if (flags & SCX_PICK_IDLE_IN_NODE) { + int node = scx_cpu_node_if_enabled(prev_cpu); + + if (scx_cpu_node_if_enabled(cpu) != node) + return -EBUSY; + } + + if (!scx_idle_test_and_clear_cpu(cpu)) + return -EBUSY; + + return cpu; +} + static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p, s32 prev_cpu, u64 wake_flags, const struct cpumask *allowed, u64 flags) { unsigned long irq_flags; bool we_locked = false; + bool migration_disabled; s32 cpu; if (!scx_cpu_valid(sch, prev_cpu, NULL)) @@ -1004,20 +1035,17 @@ static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p, /* * This may also be called from ops.enqueue(), so we need to handle - * per-CPU tasks as well. For these tasks, we can skip all idle CPU - * selection optimizations and simply check whether the previously - * used CPU is idle and within the allowed cpumask. + * per-CPU tasks as well. For these tasks, skip general idle CPU + * selection and validate the task's only usable CPU directly. */ - if (p->nr_cpus_allowed == 1 || is_bpf_migration_disabled(p)) { - if (cpumask_test_cpu(prev_cpu, allowed ?: p->cpus_ptr) && - scx_idle_test_and_clear_cpu(prev_cpu)) - cpu = prev_cpu; - else - cpu = -EBUSY; - } else { + migration_disabled = is_bpf_migration_disabled(p); + + if (p->nr_cpus_allowed == 1 || migration_disabled) + cpu = select_cpu_restricted(p, prev_cpu, allowed, flags, + migration_disabled); + else cpu = scx_select_cpu_dfl(p, prev_cpu, wake_flags, allowed ?: p->cpus_ptr, flags); - } if (we_locked) raw_spin_unlock_irqrestore(&p->pi_lock, irq_flags); diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index c2ccd268f1c0..e1afcd4e043f 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -2255,7 +2255,7 @@ void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags); u64 __scx_bpf_now(struct rq *rq); void schedule_dsq_reenq(struct scx_sched *sch, struct scx_dispatch_q *dsq, u64 reenq_flags, struct rq *locked_rq); -void scx_reenq_wait_dispatching(struct task_struct *p); +void scx_wait_dispatching(struct task_struct *p); int __scx_init_task(struct scx_sched *sch, struct task_struct *p, struct cgroup *cgrp, bool fork); void scx_enable_task(struct scx_sched *sch, struct task_struct *p); |
