scx_bpfland: fix turbo boost domain nullifying primary domain limits

When creating the turbo boost scheduling domain, we might use a full CPU
mask (selecting all possible CPUs) to indicate "do not prioritize turbo
boost CPUs" or when all CPUs have the same maximum frequency.

This approach works when the primary domain also contains all the CPUs,
as the complete overlap allows the CPU selection logic to ignore the
turbo boost domain and start picking CPUs directly from the primary
domain.

However, if the primary domain doesn't include all CPUs, the two domains
won't fully overlap, which can lead to the turbo boost domain
incorrectly including all CPUs, thereby negating the restrictions set by
the primary scheduling domain.

To resolve this, an empty CPU mask should be used for the turbo boost
domain when turbo boost CPUs aren't prioritized. If the turbo boost
domain is empty, it should be entirely bypassed, and the selection
should proceed directly to the primary domain.

Reported-by: Changwoo Min <changwoo@igalia.com>
Signed-off-by: Andrea Righi <andrea.righi@linux.dev>
This commit is contained in:
Andrea Righi 2024-08-27 13:36:50 +02:00
parent 09cff560aa
commit e0f49a338a
2 changed files with 5 additions and 2 deletions

View File

@ -592,7 +592,9 @@ retry:
* Try to dispatch on the turbo boosted CPUs first. If we can't find
* any idle CPU, re-try again with the primary scheduling domain.
*/
if (do_turbo && !bpf_cpumask_equal(cast_mask(turbo), cast_mask(primary))) {
if (do_turbo &&
!bpf_cpumask_empty(cast_mask(turbo)) &&
!bpf_cpumask_equal(cast_mask(turbo), cast_mask(primary))) {
bpf_cpumask_and(p_mask, p->cpus_ptr, cast_mask(turbo));
} else {
bpf_cpumask_and(p_mask, p->cpus_ptr, cast_mask(primary));

View File

@ -434,9 +434,10 @@ impl<'a> Scheduler<'a> {
"balance_power" => get_primary_cpus(Powermode::Turbo).unwrap_or(Vec::new()),
&_ => Vec::new(),
};
// If no turbo-boosted CPUs are selected, use an empty CPU mask, so that tasks are
// scheduled directly to the primary domain, bypassing the turbo boost domain.
if cpus.is_empty() {
let mut cpumask = Cpumask::new()?;
cpumask.setall();
cpumask
} else {
Cpumask::from_str(&cpus_to_cpumask(&cpus))?