[PATCH 05/18 v2] sched/eevdf: Add min slice check when selecting CPU
From: Vincent Guittot
Date: Fri Oct 02 2026 - 11:48:29 EST
Add a new level for selecting CPU when select_task_rq_fair() fails to find
an idle CPU. This last level will compare the slice to select a CPU where
the task could run 1st.
This helps a waking task to select a CPU where a longer slice runs
instead of one where a task with the same or shorter slice already run.
Signed-off-by: Vincent Guittot <vincent.guittot@xxxxxxxxxx>
---
kernel/sched/fair.c | 106 ++++++++++++++++++++++++++++----------------
1 file changed, 67 insertions(+), 39 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index eeac0aaba3cd..19a0e67827f7 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -8771,10 +8771,11 @@ static int select_idle_smt(struct task_struct *p, struct sched_domain *sd, int t
* comparing the average scan cost (tracked in sd->avg_scan_cost) against the
* average idle time for this rq (as found in rq->avg_idle).
*/
-static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool has_idle_core, int target)
+static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool has_idle_core, int *best)
{
struct cpumask *cpus = this_cpu_cpumask_var_ptr(select_rq_mask);
- int i, cpu, idle_cpu = -1, nr = INT_MAX;
+ int i, cpu, idle_cpu = -1, slice_cpu = -1, target = *best, nr = INT_MAX;
+ unsigned long task_slice;
if (sched_feat(SIS_UTIL) && sd->shared) {
/*
@@ -8795,6 +8796,8 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
if (!cpumask_and(cpus, sched_domain_span(sd), p->cpus_ptr))
return -1;
+ task_slice = p->se.slice;
+
if (static_branch_unlikely(&sched_cluster_active)) {
struct sched_group *sg = sd->groups;
@@ -8814,6 +8817,10 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
if ((unsigned int)idle_cpu < nr_cpumask_bits)
return idle_cpu;
}
+
+ if (slice_cpu == -1 &&
+ task_slice < get_rq_min_slice(cpu_rq(cpu)))
+ slice_cpu = cpu;
}
cpumask_andnot(cpus, cpus, sched_group_span(sg));
}
@@ -8832,11 +8839,18 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
if ((unsigned int)idle_cpu < nr_cpumask_bits)
break;
}
+
+ if (slice_cpu == -1 &&
+ task_slice < get_rq_min_slice(cpu_rq(cpu)))
+ slice_cpu = cpu;
}
if (has_idle_core)
set_idle_cores(target, false);
+ if (slice_cpu != -1)
+ *best = slice_cpu;
+
return idle_cpu;
}
@@ -8851,14 +8865,19 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
*
* Rank Val Tier Meaning
* ------------------------------ --- ------ ---------------------------
- * ASYM_IDLE_UCLAMP_MISFIT -4 core Idle core; capacity fits
+ * ASYM_BUSY_FITS -6 core Busy core but capacity fits
+ * and task can preempt current.
+ * ASYM_IDLE_UCLAMP_MISFIT -5 core Idle core; capacity fits
* util but uclamp_min misses.
- * ASYM_IDLE_COMPLETE_MISFIT -3 core Idle core; capacity does
+ * ASYM_IDLE_COMPLETE_MISFIT -4 core Idle core; capacity does
* not fit. Still beats every
* thread-tier rank: a busy
* sibling cuts effective
* capacity more than a
* misfit hurts a quiet core.
+ * ASYM_BUSY_THREAD_FITS -3 thread Busy CPU and SMT sibling but
+ * capacity fits and task can
+ * preempt current.
* ASYM_IDLE_THREAD_FITS -2 thread Busy SMT sibling; capacity
* fits util + uclamp.
* ASYM_IDLE_THREAD_UCLAMP_MISFIT -1 thread Busy SMT sibling; capacity
@@ -8868,24 +8887,26 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
* ASYM_IDLE_THREAD_MISFIT 0 thread Busy SMT sibling; capacity
* does not fit.
*
- * ASYM_IDLE_CORE_BIAS (-3) is an offset, not a state. On an idle core,
+ * ASYM_IDLE_CORE_BIAS (-4) is an offset, not a state. On an idle core,
* fits += ASYM_IDLE_CORE_BIAS rebases thread-tier ranks into the core tier:
*
- * ASYM_IDLE_THREAD_UCLAMP_MISFIT (-1) + BIAS -> ASYM_IDLE_UCLAMP_MISFIT (-4)
- * ASYM_IDLE_THREAD_MISFIT (0) + BIAS -> ASYM_IDLE_COMPLETE_MISFIT (-3)
+ * ASYM_IDLE_THREAD_UCLAMP_MISFIT (-1) + BIAS -> ASYM_IDLE_UCLAMP_MISFIT (-5)
+ * ASYM_IDLE_THREAD_MISFIT (0) + BIAS -> ASYM_IDLE_COMPLETE_MISFIT (-4)
*
* ASYM_IDLE_THREAD_FITS (-2) is never rebased because a fully-fitting idle-core
* candidate early-returns from select_idle_capacity().
*/
enum asym_fits_state {
- ASYM_IDLE_UCLAMP_MISFIT = -4,
+ ASYM_BUSY_FITS = -6,
+ ASYM_IDLE_UCLAMP_MISFIT,
ASYM_IDLE_COMPLETE_MISFIT,
ASYM_IDLE_THREAD_FITS,
+ ASYM_BUSY_THREAD_FITS,
ASYM_IDLE_THREAD_UCLAMP_MISFIT,
ASYM_IDLE_THREAD_MISFIT,
/* util_fits_cpu() bias for idle core */
- ASYM_IDLE_CORE_BIAS = -3,
+ ASYM_IDLE_CORE_BIAS = -4,
};
/*
@@ -8904,6 +8925,7 @@ select_idle_capacity(struct task_struct *p, struct sched_domain *sd, int target)
bool has_idle_core = sched_smt_active() && test_idle_cores(target);
unsigned long task_util, util_min, util_max, best_cap = 0;
int fits, best_fits = ASYM_IDLE_THREAD_MISFIT;
+ unsigned long task_slice;
int cpu, best_cpu = -1;
struct cpumask *cpus;
int nr = INT_MAX;
@@ -8914,6 +8936,7 @@ select_idle_capacity(struct task_struct *p, struct sched_domain *sd, int target)
task_util = task_util_est(p);
util_min = uclamp_eff_value(p, UCLAMP_MIN);
util_max = uclamp_eff_value(p, UCLAMP_MAX);
+ task_slice = p->se.slice;
if (sched_feat(SIS_UTIL) && sd->shared) {
/*
@@ -8937,42 +8960,47 @@ select_idle_capacity(struct task_struct *p, struct sched_domain *sd, int target)
if (!has_idle_core && --nr <= 0)
return best_cpu;
- if (!choose_idle_cpu(cpu, p))
- continue;
-
fits = util_fits_cpu(task_util, util_min, util_max, cpu);
- /*
- * Perfect fit: capacity satisfies util + uclamp and the CPU
- * sits on a fully-idle SMT core, this is a !SMT system, or
- * there is no idle core to find.
- * Short-circuit the rank-based selection and return
- * immediately.
- */
- if (fits > 0 && preferred_core)
- return cpu;
- /*
- * Only the min performance hint (i.e. uclamp_min) doesn't fit.
- * Look for the CPU with best capacity.
- */
- else if (fits < 0)
+ if (choose_idle_cpu(cpu, p)) {
+ /*
+ * Perfect fit: capacity satisfies util + uclamp and the CPU
+ * sits on a fully-idle SMT core, this is a !SMT system, or
+ * there is no idle core to find.
+ * Short-circuit the rank-based selection and return
+ * immediately.
+ */
+ if (fits > 0 && preferred_core)
+ return cpu;
+ /*
+ * Only the min performance hint (i.e. uclamp_min) doesn't fit.
+ * Look for the CPU with best capacity.
+ */
+ else if (fits < 0)
+ cpu_cap = get_actual_cpu_capacity(cpu);
+ /*
+ * fits > 0 implies we are not on a preferred core, but the util
+ * fits CPU capacity. Set fits to ASYM_IDLE_THREAD_FITS
+ * so the effective range becomes
+ * [ASYM_IDLE_THREAD_FITS, ASYM_IDLE_THREAD_MISFIT], where:
+ * ASYM_IDLE_THREAD_MISFIT - does not fit
+ * ASYM_IDLE_THREAD_UCLAMP_MISFIT - fits with the exception of UCLAMP_MIN
+ * ASYM_IDLE_THREAD_FITS - fits with the exception of preferred_core
+ */
+ else if (fits > 0)
+ fits = ASYM_IDLE_THREAD_FITS;
+
+ } else if (fits > 0 && task_slice < get_rq_min_slice(cpu_rq(cpu))) {
+ fits = ASYM_BUSY_THREAD_FITS;
cpu_cap = get_actual_cpu_capacity(cpu);
- /*
- * fits > 0 implies we are not on a preferred core, but the util
- * fits CPU capacity. Set fits to ASYM_IDLE_THREAD_FITS
- * so the effective range becomes
- * [ASYM_IDLE_THREAD_FITS, ASYM_IDLE_THREAD_MISFIT], where:
- * ASYM_IDLE_THREAD_MISFIT - does not fit
- * ASYM_IDLE_THREAD_UCLAMP_MISFIT - fits with the exception of UCLAMP_MIN
- * ASYM_IDLE_THREAD_FITS - fits with the exception of preferred_core
- */
- else if (fits > 0)
- fits = ASYM_IDLE_THREAD_FITS;
+ } else {
+ continue;
+ }
/*
* If we are on a preferred core, translate the range of fits
* of [ASYM_IDLE_THREAD_UCLAMP_MISFIT, ASYM_IDLE_THREAD_MISFIT] to
- * [ASYM_IDLE_UCLAMP_MISFIT, ASYM_IDLE_COMPLETE_MISFIT].
+ * [ASYM_IDLE_THREAD_MISFIT_IDLE_UCLAMP_MISFIT, ASYM_IDLE_COMPLETE_MISFIT].
* This ensures that an idle core is always given priority over
* (partially) busy core.
*
@@ -9147,7 +9175,7 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
}
}
- i = select_idle_cpu(p, sd, has_idle_core, target);
+ i = select_idle_cpu(p, sd, has_idle_core, &target);
if ((unsigned)i < nr_cpumask_bits)
return i;
--
2.53.0