[PATCH 14/17] sched_ext: Fix the bypass load balancer stranding tasks on cpus where the host lacks SCX_CAP_BASE

From: Tejun Heo

Date: Fri Oct 23 2026 - 13:57:20 EST


The bypass load balancer spreads a host's bypassed tasks over the cpus of a
node by moving them between its per-cpu bypass DSQs. It counts every cpu of
the node as capacity and takes any cpu below the target as a donee. A cpu's
dispatch only descends into schedulers holding SCX_CAP_BASE on it, so a
bypass DSQ on a cpu where the host lacks BASE is never consumed. A task
moved there sits until the host regains the cap or the bypass window ends.

Count only cpus where the host holds BASE as capacity and pick donees from
them. The target rises to spread the node's tasks over the cpus that can run
them, and a capless cpu receives nothing. A capless cpu with tasks is still
an ordinary donor, offloaded down to the donor target when its queue grows
past the threshold; draining it is left to the following patches.

Fixes: 147d1885f390 ("sched_ext: Add the SCX_CAP_ENQ_IMMED cap")
Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
---
kernel/sched/ext/ext.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 54150fdc3bbd..8d87dca8bb62 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -6652,7 +6652,9 @@ static void bypass_lb_node(struct scx_sched *sch, int node)
u32 nr = READ_ONCE(scx_bypass_dsq(sch, cpu)->nr);

nr_tasks += nr;
- nr_cpus++;
+ /* only a cpu whose dispatch consumes its DSQ is capacity */
+ if (!scx_missing_caps(sch, cpu, SCX_CAP_BASE))
+ nr_cpus++;

before_min = min(nr, before_min);
before_max = max(nr, before_max);
@@ -6671,7 +6673,9 @@ static void bypass_lb_node(struct scx_sched *sch, int node)

cpumask_clear(donee_mask);
for_each_cpu_and(cpu, cpu_online_mask, node_mask) {
- if (READ_ONCE(scx_bypass_dsq(sch, cpu)->nr) < nr_target)
+ /* a donee's DSQ is consumed only if @sch holds %SCX_CAP_BASE */
+ if (READ_ONCE(scx_bypass_dsq(sch, cpu)->nr) < nr_target &&
+ !scx_missing_caps(sch, cpu, SCX_CAP_BASE))
cpumask_set_cpu(cpu, donee_mask);
}

--
2.56.0