[PATCH 07/20 v3] sched/fair: Prepare select_task_rq_fair() to be called for new cases
From: Vincent Guittot
Date: Fri Nov 06 2026 - 10:26:50 EST
Update select_task_rq_fair() to be called outside of the 3 current cases:
- wake up
- exec
- fork
We wants to select a rq in some new cases like pushing a runnable task on a
better CPU than the local one. In such case, it's not a wakeup , nor an
exec nor a fork. We ensure to not distrub these cases but still go through
EAS and fast-path.
Signed-off-by: Vincent Guittot <vincent.guittot@xxxxxxxxxx>
---
kernel/sched/fair.c | 75 +++++++++++++++++++++++++++++----------------
1 file changed, 48 insertions(+), 27 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 039dce244aaa..8f99c8bf1a80 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6170,6 +6170,18 @@ static void sync_entity_load_avg(struct sched_entity *se)
__update_load_avg_blocked_se(last_update_time, se);
}
+/*
+ * Synchronize task load avg of dequeued entity without locking
+ * the previous rq.
+ */
+static void sync_task_load_avg(struct task_struct *p)
+{
+ if (task_on_rq_queued(p))
+ return;
+
+ sync_entity_load_avg(&p->se);
+}
+
/*
* Task first catches up with cfs_rq, and then subtract
* itself from the cfs_rq (task must be off the queue now).
@@ -8802,7 +8814,7 @@ static inline int sched_balance_find_dst_cpu(struct sched_domain *sd, struct tas
* prev_cpu's last_update_time.
*/
if (!(sd_flag & SD_BALANCE_FORK))
- sync_entity_load_avg(&p->se);
+ sync_task_load_avg(p);
while (sd) {
struct sched_group *group;
@@ -9318,7 +9330,7 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
* that the task fits with CPU's capacity.
*/
if (sched_asym_cpucap_active()) {
- sync_entity_load_avg(&p->se);
+ sync_task_load_avg(p);
task_util = task_util_est(p);
util_min = uclamp_eff_value(p, UCLAMP_MIN);
util_max = uclamp_eff_value(p, UCLAMP_MAX);
@@ -9912,7 +9924,7 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
target = prev_cpu;
- sync_entity_load_avg(&p->se);
+ sync_task_load_avg(p);
if (!task_util_est(p) && p_util_min == 0)
return target;
@@ -10060,46 +10072,55 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
}
/*
- * select_task_rq_fair: Select target runqueue for the waking task in domains
- * that have the relevant SD flag set. In practice, this is SD_BALANCE_WAKE,
- * SD_BALANCE_FORK, or SD_BALANCE_EXEC.
+ * select_task_rq_fair: Select a target runqueue for the task.
+ * There are 2 ways to select the target runqueue:
+ * - The fast path which only looks for an idle CPU in the LLC or the smallest
+ * asymmetric domain (i.e. the lowest domain with all compute capacities).
+ * - The slow path which looks for the idlest CPU in the highest domain with
+ * the relevant SD flag set.
*
- * Balances load by selecting the idlest CPU in the idlest group, or under
- * certain conditions an idle sibling CPU if the domain has SD_WAKE_AFFINE set.
+ * In practice, WF_EXEC and WF_FORK uses the slow path whereas WF_TTWU and no
+ * flag (Push) uses the fast path.
*
- * Returns the target CPU number.
*/
static int
-select_task_rq_fair(struct task_struct *p, int prev_cpu, int wake_flags)
+select_task_rq_fair(struct task_struct *p, int prev_cpu, int select_flags)
{
- int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING);
+ int sync = (select_flags & WF_SYNC) && !(current->flags & PF_EXITING);
+ int want_sibling = !(select_flags & (WF_EXEC | WF_FORK));
+ int new_cpu, cpu = smp_processor_id();
struct sched_domain *tmp, *sd = NULL;
- int cpu = smp_processor_id();
- int new_cpu = prev_cpu;
- int want_affine = 0;
/* SD_flags and WF_flags share the first nibble */
- int sd_flag = wake_flags & 0xF;
+ int sd_flag = select_flags & 0xF;
+ int want_affine = 0;
/*
- * required for stable ->cpus_allowed
+ * Required for stable ->cpus_allowed
*/
lockdep_assert_held(&p->pi_lock);
- if (wake_flags & WF_TTWU) {
+
+ if (select_flags & WF_TTWU) {
record_wakee(p);
- if ((wake_flags & WF_CURRENT_CPU) &&
+ if ((select_flags & WF_CURRENT_CPU) &&
cpumask_test_cpu(cpu, p->cpus_ptr))
return cpu;
+ }
- if (!is_rd_overutilized(this_rq()->rd)) {
- new_cpu = find_energy_efficient_cpu(p, prev_cpu);
- if (new_cpu >= 0)
- return new_cpu;
- new_cpu = prev_cpu;
- }
+ /*
+ * We don't want EAS to be called for exec or fork but it should be
+ * called for any other case such as wake up or push callback.
+ */
+ if (!is_rd_overutilized(this_rq()->rd) && want_sibling) {
+ new_cpu = find_energy_efficient_cpu(p, prev_cpu);
+ if (new_cpu >= 0)
+ return new_cpu;
+ }
+ if (select_flags & WF_TTWU)
want_affine = !wake_wide(p) && cpumask_test_cpu(cpu, p->cpus_ptr);
- }
+
+ new_cpu = prev_cpu;
for_each_domain(cpu, tmp) {
/*
@@ -10133,8 +10154,8 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int wake_flags)
}
/* Fast path */
- if (wake_flags & WF_TTWU)
- return select_idle_sibling(p, prev_cpu, new_cpu);
+ if (want_sibling)
+ new_cpu = select_idle_sibling(p, prev_cpu, new_cpu);
return new_cpu;
}
--
2.53.0