mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases
@ 2025-02-28 13:39 Vincent Guittot
  2025-02-28 13:39 ` [PATCH 1/7 v3] sched/fair: Filter false overloaded_group case for EAS Vincent Guittot
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

The current Energy Aware Scheduler has some known limitations which have
became more and more visible with features like uclamp as an example. This
serie tries to fix some of those issues:
- tasks stacked on the same CPU of a PD
- tasks stuck on the wrong CPU.

Patch 1 fixes the case where a CPU is wrongly classified as overloaded
whereas it is capped to a lower compute capacity. This wrong classification
can prevent periodic load balancer to select a group_misfit_task CPU
because group_overloaded has higher priority.

Patch 2 creates a new EM interface that will be used by Patch 3

Patch 3 fixes the issue of tasks being stacked on same CPU of a PD whereas
others might be a better choice. feec() looks for the CPU with the highest
spare capacity in a PD assuming that it will be the best CPU from a energy
efficiency PoV because it will require the smallest increase of OPP.
This is often but not always true, this policy filters some others CPUs
which would be as efficients because of using the same OPP but with less
running tasks as an example.
In fact, we only care about the cost of the new OPP that will be
selected to handle the waking task. In many cases, several CPUs will end
up selecting the same OPP and as a result having the same energy cost. In
such cases, we can use other metrics to select the best CPU with the same
energy cost. Patch 3 rework feec() to look 1st for the lowest cost in a PD
and then the most performant CPU between CPUs. At now, this only tries to
evenly spread the number of runnable tasks on CPUs but this can be
improved with other metric like the sched slice duration in a follow up
series.

perf sched pipe on a dragonboard rb5 has been used to compare the overhead
of the new feec() vs current implementation.

9 iterations of perf bench sched pipe -T -l 80000
                ops/sec  stdev 
tip/sched/core  16634    (+/- 0.5%)
+ patches 1-3   17434    (+/- 1.2%)  +4.8%


Patch 4 removed the now unused em_cpu_energy()

Patch 5 solves another problem with tasks being stuck on a CPU forever
because it doesn't sleep anymore and as a result never wakeup and call
feec(). Such task can be detected by comparing util_avg or runnable_avg
with the compute capacity of the CPU. Once detected, we can call feec() to
check if there is a better CPU for the stuck task. The call can be done in
2 places:
- When the task is put back in the runnnable list after its running slice
  with the balance callback mecanism similarly to the rt/dl push callback.
- During cfs tick when there is only 1 running task stuck on the CPU in
  which case the balance callback can't be used.

This push callback mecanism with the new feec() algorithm ensures that
tasks always get a chance to migrate on the best suitable CPU and don't
stay stuck on a CPU which is no more the most suitable one. As examples:
- A task waking on a big CPU with a uclamp max preventing it to sleep and
  wake up, can migrate on a smaller CPU once it's more power efficient.
- The tasks are spread on CPUs in the PD when they target the same OPP.

Patch 6 adds task misfit migration case in the cfs tick and push callback
mecanism to prevent waking up an idle cpu unnecessarily.

Patch 7 removes the need of testing uclamp_min in cpu_overutilized to
trigger the active migration of a task on another CPU.

Compared to v2:
- Renamed the push and tick functions to ease understanding what they do.
  Both are kept in the same patch as they solve the same problem.
- Created some helper functions
- Fixing some typos and comments
- The task_stuck_on_cpu() condition remains unchanged. Pierre suggested to
  take into account the min capacity of the CPU but the is not directly
  available right now. It can trigger feec() when uclamp_max is very low
  compare to the min capacity of the CPU but the feec() should keep 
  returning the same CPU. This can be handled in a follow on patch

Compared to v1:
- The call to feec() even when overutilized has been removed
from this serie and will be adressed in a separate series. Only the case
of uclamp_min has been kept as it is now handled by push callback and
tick mecanism.
- The push mecanism has been cleanup, fixed and simplified.

This series implements some of the topics discussed at OSPM [1]. Other
topics will be part of an other serie

[1] https://youtu.be/PHEBAyxeM_M?si=ZApIOw3BS4SOLPwp

Vincent Guittot (7):
  sched/fair: Filter false overloaded_group case for EAS
  energy model: Add a get previous state function
  sched/fair: Rework feec() to use cost instead of spare capacity
  energy model: Remove unused em_cpu_energy()
  sched/fair: Add push task mechanism for EAS
  sched/fair: Add misfit case to push task mecanism for EAS
  sched/fair: Update overutilized detection

 include/linux/energy_model.h | 112 ++----
 kernel/sched/fair.c          | 717 ++++++++++++++++++++++++-----------
 kernel/sched/sched.h         |   2 +
 3 files changed, 515 insertions(+), 316 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/7 v3] sched/fair: Filter false overloaded_group case for EAS
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
@ 2025-02-28 13:39 ` Vincent Guittot
  2025-02-28 13:39 ` [PATCH 2/7 v3] energy model: Add a get previous state function Vincent Guittot
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

With EAS, a group should be set overloaded if at least 1 CPU in the group
is overutilized but it can happen that a CPU is fully utilized by tasks
because of clamping the compute capacity of the CPU. In such case, the CPU
is not overutilized and as a result should not be set overloaded as well.

group_overloaded being a higher priority than group_misfit, such group can
be selected as the busiest group instead of a group with a mistfit task
and prevents load_balance to select the CPU with the misfit task to pull
the latter on a fitting CPU.

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
Tested-by: Pierre Gondois <pierre.gondois@arm.com>
---
 kernel/sched/fair.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 857808da23d8..d3d1a2ba6b1a 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -9931,6 +9931,7 @@ struct sg_lb_stats {
 	unsigned int group_asym_packing;	/* Tasks should be moved to preferred CPU */
 	unsigned int group_smt_balance;		/* Task on busy SMT be moved */
 	unsigned long group_misfit_task_load;	/* A CPU has a task too big for its capacity */
+	unsigned int group_overutilized;	/* At least one CPU is overutilized in the group */
 #ifdef CONFIG_NUMA_BALANCING
 	unsigned int nr_numa_running;
 	unsigned int nr_preferred_running;
@@ -10163,6 +10164,13 @@ group_has_capacity(unsigned int imbalance_pct, struct sg_lb_stats *sgs)
 static inline bool
 group_is_overloaded(unsigned int imbalance_pct, struct sg_lb_stats *sgs)
 {
+	/*
+	 * With EAS and uclamp, 1 CPU in the group must be overutilized to
+	 * consider the group overloaded.
+	 */
+	if (sched_energy_enabled() && !sgs->group_overutilized)
+		return false;
+
 	if (sgs->sum_nr_running <= sgs->group_weight)
 		return false;
 
@@ -10374,8 +10382,10 @@ static inline void update_sg_lb_stats(struct lb_env *env,
 		nr_running = rq->nr_running;
 		sgs->sum_nr_running += nr_running;
 
-		if (cpu_overutilized(i))
+		if (cpu_overutilized(i)) {
 			*sg_overutilized = 1;
+			sgs->group_overutilized = 1;
+		}
 
 		/*
 		 * No need to call idle_cpu() if nr_running is not 0
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/7 v3] energy model: Add a get previous state function
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
  2025-02-28 13:39 ` [PATCH 1/7 v3] sched/fair: Filter false overloaded_group case for EAS Vincent Guittot
@ 2025-02-28 13:39 ` Vincent Guittot
  2025-02-28 13:39 ` [PATCH 3/7 v3] sched/fair: Rework feec() to use cost instead of spare capacity Vincent Guittot
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

Instead of parsing the entire EM table everytime, add a function to get the
previous state.

Will be used in the scheduler feec() function.

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
 include/linux/energy_model.h | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
index 78318d49276d..967650726619 100644
--- a/include/linux/energy_model.h
+++ b/include/linux/energy_model.h
@@ -216,6 +216,26 @@ em_pd_get_efficient_state(struct em_perf_state *table,
 	return max_ps;
 }
 
+static inline int
+em_pd_get_previous_state(struct em_perf_state *table,
+			 struct em_perf_domain *pd, int idx)
+{
+	unsigned long pd_flags = pd->flags;
+	int min_ps = pd->min_perf_state;
+	struct em_perf_state *ps;
+	int i;
+
+	for (i = idx - 1; i >= min_ps; i--) {
+		ps = &table[i];
+		if (pd_flags & EM_PERF_DOMAIN_SKIP_INEFFICIENCIES &&
+		    ps->flags & EM_PERF_STATE_INEFFICIENT)
+			continue;
+		return i;
+	}
+
+	return -1;
+}
+
 /**
  * em_cpu_energy() - Estimates the energy consumed by the CPUs of a
  *		performance domain
@@ -362,6 +382,19 @@ static inline struct em_perf_domain *em_pd_get(struct device *dev)
 {
 	return NULL;
 }
+static inline int
+em_pd_get_efficient_state(struct em_perf_state *table,
+			  struct em_perf_domain *pd, unsigned long max_util)
+{
+	return 0;
+}
+
+static inline int
+em_pd_get_previous_state(struct em_perf_state *table, int nr_perf_states,
+			  int idx, unsigned long pd_flags)
+{
+	return -1;
+}
 static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
 			unsigned long max_util, unsigned long sum_util,
 			unsigned long allowed_cpu_cap)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/7 v3] sched/fair: Rework feec() to use cost instead of spare capacity
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
  2025-02-28 13:39 ` [PATCH 1/7 v3] sched/fair: Filter false overloaded_group case for EAS Vincent Guittot
  2025-02-28 13:39 ` [PATCH 2/7 v3] energy model: Add a get previous state function Vincent Guittot
@ 2025-02-28 13:39 ` Vincent Guittot
  2025-02-28 13:39 ` [PATCH 4/7 v3] energy model: Remove unused em_cpu_energy() Vincent Guittot
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

feec() looks for the CPU with highest spare capacity in a PD assuming that
it will be the best CPU from a energy efficiency PoV because it will
require the smallest increase of OPP. Although this is true generally
speaking, this policy also filters some others CPUs which will be as
efficients because of using the same OPP.
In fact, we really care about the cost of the new OPP that will be
selected to handle the waking task. In many cases, several CPUs will end
up selecting the same OPP and as a result using the same energy cost. In
these cases, we can use other metrics to select the best CPU for the same
energy cost.

Rework feec() to look 1st for the lowest cost in a PD and then the most
performant CPU between CPUs. The cost of the OPP remains the only
comparison criteria between Performance Domains.

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
 kernel/sched/fair.c | 466 +++++++++++++++++++++++---------------------
 1 file changed, 246 insertions(+), 220 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d3d1a2ba6b1a..a9b97bbc085f 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -8193,29 +8193,37 @@ unsigned long sched_cpu_util(int cpu)
 }
 
 /*
- * energy_env - Utilization landscape for energy estimation.
- * @task_busy_time: Utilization contribution by the task for which we test the
- *                  placement. Given by eenv_task_busy_time().
- * @pd_busy_time:   Utilization of the whole perf domain without the task
- *                  contribution. Given by eenv_pd_busy_time().
- * @cpu_cap:        Maximum CPU capacity for the perf domain.
- * @pd_cap:         Entire perf domain capacity. (pd->nr_cpus * cpu_cap).
- */
-struct energy_env {
-	unsigned long task_busy_time;
-	unsigned long pd_busy_time;
-	unsigned long cpu_cap;
-	unsigned long pd_cap;
+ * energy_cpu_stat - Utilization landscape for energy estimation.
+ * @idx :        Index of the OPP in the performance domain
+ * @cost :       Cost of the OPP
+ * @max_perf :   Compute capacity of OPP
+ * @min_perf :   Compute capacity of the previous OPP
+ * @capa :       Capacity of the CPU
+ * @runnable :   runnable_avg of the CPU
+ * @nr_running : Number of cfs running task
+ * @fits :       Fits level of the CPU
+ * @cpu :        Current best CPU
+ */
+struct energy_cpu_stat {
+	unsigned long idx;
+	unsigned long cost;
+	unsigned long max_perf;
+	unsigned long min_perf;
+	unsigned long capa;
+	unsigned long util;
+	unsigned long runnable;
+	unsigned int nr_running;
+	int fits;
+	int cpu;
 };
 
 /*
- * Compute the task busy time for compute_energy(). This time cannot be
- * injected directly into effective_cpu_util() because of the IRQ scaling.
+ * Compute the task busy time for computing its energy impact. This time cannot
+ * be injected directly into effective_cpu_util() because of the IRQ scaling.
  * The latter only makes sense with the most recent CPUs where the task has
  * run.
  */
-static inline void eenv_task_busy_time(struct energy_env *eenv,
-				       struct task_struct *p, int prev_cpu)
+static inline unsigned long task_busy_time(struct task_struct *p, int prev_cpu)
 {
 	unsigned long busy_time, max_cap = arch_scale_cpu_capacity(prev_cpu);
 	unsigned long irq = cpu_util_irq(cpu_rq(prev_cpu));
@@ -8225,124 +8233,153 @@ static inline void eenv_task_busy_time(struct energy_env *eenv,
 	else
 		busy_time = scale_irq_capacity(task_util_est(p), irq, max_cap);
 
-	eenv->task_busy_time = busy_time;
+	return busy_time;
 }
 
-/*
- * Compute the perf_domain (PD) busy time for compute_energy(). Based on the
- * utilization for each @pd_cpus, it however doesn't take into account
- * clamping since the ratio (utilization / cpu_capacity) is already enough to
- * scale the EM reported power consumption at the (eventually clamped)
- * cpu_capacity.
- *
- * The contribution of the task @p for which we want to estimate the
- * energy cost is removed (by cpu_util()) and must be calculated
- * separately (see eenv_task_busy_time). This ensures:
- *
- *   - A stable PD utilization, no matter which CPU of that PD we want to place
- *     the task on.
- *
- *   - A fair comparison between CPUs as the task contribution (task_util())
- *     will always be the same no matter which CPU utilization we rely on
- *     (util_avg or util_est).
- *
- * Set @eenv busy time for the PD that spans @pd_cpus. This busy time can't
- * exceed @eenv->pd_cap.
- */
-static inline void eenv_pd_busy_time(struct energy_env *eenv,
-				     struct cpumask *pd_cpus,
-				     struct task_struct *p)
+/* Estimate the utilization of the CPU that is then used to select the OPP */
+static unsigned long find_cpu_max_util(int cpu, struct task_struct *p, int dst_cpu)
 {
-	unsigned long busy_time = 0;
-	int cpu;
+	unsigned long util = cpu_util(cpu, p, dst_cpu, 1);
+	unsigned long eff_util, min, max;
+
+	/*
+	 * Performance domain frequency: utilization clamping
+	 * must be considered since it affects the selection
+	 * of the performance domain frequency.
+	 */
+	eff_util = effective_cpu_util(cpu, util, &min, &max);
 
-	for_each_cpu(cpu, pd_cpus) {
-		unsigned long util = cpu_util(cpu, p, -1, 0);
+	/* Task's uclamp can modify min and max value */
+	if (uclamp_is_used() && cpu == dst_cpu) {
+		min = max(min, uclamp_eff_value(p, UCLAMP_MIN));
 
-		busy_time += effective_cpu_util(cpu, util, NULL, NULL);
+		/*
+		 * If there is no active max uclamp constraint,
+		 * directly use task's one, otherwise keep max.
+		 */
+		if (uclamp_rq_is_idle(cpu_rq(cpu)))
+			max = uclamp_eff_value(p, UCLAMP_MAX);
+		else
+			max = max(max, uclamp_eff_value(p, UCLAMP_MAX));
 	}
 
-	eenv->pd_busy_time = min(eenv->pd_cap, busy_time);
+	eff_util = sugov_effective_cpu_perf(cpu, eff_util, min, max);
+	return eff_util;
 }
 
-/*
- * Compute the maximum utilization for compute_energy() when the task @p
- * is placed on the cpu @dst_cpu.
- *
- * Returns the maximum utilization among @eenv->cpus. This utilization can't
- * exceed @eenv->cpu_cap.
- */
-static inline unsigned long
-eenv_pd_max_util(struct energy_env *eenv, struct cpumask *pd_cpus,
-		 struct task_struct *p, int dst_cpu)
+/* Estimate the utilization of the CPU without the task */
+static unsigned long find_cpu_actual_util(int cpu, struct task_struct *p)
 {
-	unsigned long max_util = 0;
-	int cpu;
+	unsigned long util = cpu_util(cpu, p, -1, 0);
+	unsigned long eff_util;
 
-	for_each_cpu(cpu, pd_cpus) {
-		struct task_struct *tsk = (cpu == dst_cpu) ? p : NULL;
-		unsigned long util = cpu_util(cpu, p, dst_cpu, 1);
-		unsigned long eff_util, min, max;
+	eff_util = effective_cpu_util(cpu, util, NULL, NULL);
 
-		/*
-		 * Performance domain frequency: utilization clamping
-		 * must be considered since it affects the selection
-		 * of the performance domain frequency.
-		 * NOTE: in case RT tasks are running, by default the min
-		 * utilization can be max OPP.
-		 */
-		eff_util = effective_cpu_util(cpu, util, &min, &max);
+	return eff_util;
+}
 
-		/* Task's uclamp can modify min and max value */
-		if (tsk && uclamp_is_used()) {
-			min = max(min, uclamp_eff_value(p, UCLAMP_MIN));
+/* Find the cost of a performance domain for the estimated utilization */
+static inline void find_pd_cost(struct em_perf_domain *pd,
+				unsigned long max_util,
+				struct energy_cpu_stat *stat)
+{
+	struct em_perf_table *em_table;
+	struct em_perf_state *ps;
+	int i;
 
-			/*
-			 * If there is no active max uclamp constraint,
-			 * directly use task's one, otherwise keep max.
-			 */
-			if (uclamp_rq_is_idle(cpu_rq(cpu)))
-				max = uclamp_eff_value(p, UCLAMP_MAX);
-			else
-				max = max(max, uclamp_eff_value(p, UCLAMP_MAX));
-		}
+	/*
+	 * Find the lowest performance state of the Energy Model above the
+	 * requested performance.
+	 */
+	em_table = rcu_dereference(pd->em_table);
+	i = em_pd_get_efficient_state(em_table->state, pd, max_util);
+	ps = &em_table->state[i];
 
-		eff_util = sugov_effective_cpu_perf(cpu, eff_util, min, max);
-		max_util = max(max_util, eff_util);
+	/* Save the cost and performance range of the OPP */
+	stat->max_perf = ps->performance;
+	stat->cost = ps->cost;
+	i = em_pd_get_previous_state(em_table->state, pd, i);
+	if (i < 0)
+		stat->min_perf = 0;
+	else {
+		ps = &em_table->state[i];
+		stat->min_perf = ps->performance;
 	}
+}
+
+/*Check if the CPU can handle the waking task */
+static int check_cpu_with_task(struct task_struct *p, int cpu)
+{
+	unsigned long p_util_min = uclamp_is_used() ? uclamp_eff_value(p, UCLAMP_MIN) : 0;
+	unsigned long p_util_max = uclamp_is_used() ? uclamp_eff_value(p, UCLAMP_MAX) : 1024;
+	unsigned long util_min = p_util_min;
+	unsigned long util_max = p_util_max;
+	unsigned long util = cpu_util(cpu, p, cpu, 0);
+	struct rq *rq = cpu_rq(cpu);
 
-	return min(max_util, eenv->cpu_cap);
+	/*
+	 * Skip CPUs that cannot satisfy the capacity request.
+	 * IOW, placing the task there would make the CPU
+	 * overutilized. Take uclamp into account to see how
+	 * much capacity we can get out of the CPU; this is
+	 * aligned with sched_cpu_util().
+	 */
+	if (uclamp_is_used() && !uclamp_rq_is_idle(rq)) {
+		unsigned long rq_util_min, rq_util_max;
+		/*
+		 * Open code uclamp_rq_util_with() except for
+		 * the clamp() part. I.e.: apply max aggregation
+		 * only. util_fits_cpu() logic requires to
+		 * operate on non clamped util but must use the
+		 * max-aggregated uclamp_{min, max}.
+		 */
+		rq_util_min = uclamp_rq_get(rq, UCLAMP_MIN);
+		rq_util_max = uclamp_rq_get(rq, UCLAMP_MAX);
+		util_min = max(rq_util_min, p_util_min);
+		util_max = max(rq_util_max, p_util_max);
+	}
+	return util_fits_cpu(util, util_min, util_max, cpu);
 }
 
 /*
- * compute_energy(): Use the Energy Model to estimate the energy that @pd would
- * consume for a given utilization landscape @eenv. When @dst_cpu < 0, the task
- * contribution is ignored.
+ * For the same cost, select the CPU that will povide best performance for the
+ * task.
  */
-static inline unsigned long
-compute_energy(struct energy_env *eenv, struct perf_domain *pd,
-	       struct cpumask *pd_cpus, struct task_struct *p, int dst_cpu)
+static bool update_best_cpu(struct energy_cpu_stat *target,
+			    struct energy_cpu_stat *min,
+			    int prev, struct sched_domain *sd)
 {
-	unsigned long max_util = eenv_pd_max_util(eenv, pd_cpus, p, dst_cpu);
-	unsigned long busy_time = eenv->pd_busy_time;
-	unsigned long energy;
-
-	if (dst_cpu >= 0)
-		busy_time = min(eenv->pd_cap, busy_time + eenv->task_busy_time);
+	/*  Select the one with the least number of running tasks */
+	if (target->nr_running < min->nr_running)
+		return true;
+	if (target->nr_running > min->nr_running)
+		return false;
 
-	energy = em_cpu_energy(pd->em_pd, max_util, busy_time, eenv->cpu_cap);
+	/* Favor previous CPU otherwise */
+	if (target->cpu == prev)
+		return true;
+	if (min->cpu == prev)
+		return false;
 
-	trace_sched_compute_energy_tp(p, dst_cpu, energy, max_util, busy_time);
+	/*
+	 * Choose CPU with lowest contention. One might want to consider load
+	 * instead of runnable but we are supposed to not be overutilized so
+	 * there is enough compute capacity for everybody.
+	 */
+	if ((target->runnable * min->capa * sd->imbalance_pct) >=
+			(min->runnable * target->capa * 100))
+		return false;
 
-	return energy;
+	return true;
 }
 
 /*
  * find_energy_efficient_cpu(): Find most energy-efficient target CPU for the
- * waking task. find_energy_efficient_cpu() looks for the CPU with maximum
- * spare capacity in each performance domain and uses it as a potential
- * candidate to execute the task. Then, it uses the Energy Model to figure
- * out which of the CPU candidates is the most energy-efficient.
+ * waking task. find_energy_efficient_cpu() looks for the CPU with the lowest
+ * power cost (usually with maximum spare capacity but not always) in each
+ * performance domain and uses it as a potential candidate to execute the task.
+ * Then, it uses the Energy Model to figure out which of the CPU candidates is
+ * the most energy-efficient.
  *
  * The rationale for this heuristic is as follows. In a performance domain,
  * all the most energy efficient CPU candidates (according to the Energy
@@ -8379,17 +8416,14 @@ compute_energy(struct energy_env *eenv, struct perf_domain *pd,
 static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
 {
 	struct cpumask *cpus = this_cpu_cpumask_var_ptr(select_rq_mask);
-	unsigned long prev_delta = ULONG_MAX, best_delta = ULONG_MAX;
-	unsigned long p_util_min = uclamp_is_used() ? uclamp_eff_value(p, UCLAMP_MIN) : 0;
-	unsigned long p_util_max = uclamp_is_used() ? uclamp_eff_value(p, UCLAMP_MAX) : 1024;
 	struct root_domain *rd = this_rq()->rd;
-	int cpu, best_energy_cpu, target = -1;
-	int prev_fits = -1, best_fits = -1;
-	unsigned long best_actual_cap = 0;
-	unsigned long prev_actual_cap = 0;
+	unsigned long best_nrg = ULONG_MAX;
+	unsigned long task_util;
 	struct sched_domain *sd;
 	struct perf_domain *pd;
-	struct energy_env eenv;
+	int cpu, target = -1;
+	int best_fits = -1;
+	int best_cpu = -1;
 
 	rcu_read_lock();
 	pd = rcu_dereference(rd->pd);
@@ -8409,19 +8443,19 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
 	target = prev_cpu;
 
 	sync_entity_load_avg(&p->se);
-	if (!task_util_est(p) && p_util_min == 0)
-		goto unlock;
-
-	eenv_task_busy_time(&eenv, p, prev_cpu);
+	task_util = task_busy_time(p, prev_cpu);
 
 	for (; pd; pd = pd->next) {
-		unsigned long util_min = p_util_min, util_max = p_util_max;
-		unsigned long cpu_cap, cpu_actual_cap, util;
-		long prev_spare_cap = -1, max_spare_cap = -1;
-		unsigned long rq_util_min, rq_util_max;
-		unsigned long cur_delta, base_energy;
-		int max_spare_cap_cpu = -1;
-		int fits, max_fits = -1;
+		unsigned long pd_actual_util = 0, delta_nrg = 0;
+		unsigned long cpu_actual_cap, max_cost = 0;
+		struct energy_cpu_stat target_stat;
+		struct energy_cpu_stat min_stat = {
+			.cost = ULONG_MAX,
+			.max_perf = ULONG_MAX,
+			.min_perf = ULONG_MAX,
+			.fits = -2,
+			.cpu = -1,
+		};
 
 		cpumask_and(cpus, perf_domain_span(pd), cpu_online_mask);
 
@@ -8432,13 +8466,9 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
 		cpu = cpumask_first(cpus);
 		cpu_actual_cap = get_actual_cpu_capacity(cpu);
 
-		eenv.cpu_cap = cpu_actual_cap;
-		eenv.pd_cap = 0;
-
+		/* In a PD, the CPU with the lowest cost will be the most efficient */
 		for_each_cpu(cpu, cpus) {
-			struct rq *rq = cpu_rq(cpu);
-
-			eenv.pd_cap += cpu_actual_cap;
+			unsigned long target_perf;
 
 			if (!cpumask_test_cpu(cpu, sched_domain_span(sd)))
 				continue;
@@ -8446,120 +8476,116 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
 			if (!cpumask_test_cpu(cpu, p->cpus_ptr))
 				continue;
 
-			util = cpu_util(cpu, p, cpu, 0);
-			cpu_cap = capacity_of(cpu);
+			target_stat.fits = check_cpu_with_task(p, cpu);
+
+			if (!target_stat.fits)
+				continue;
+
+			/* 1st select the CPU that fits best */
+			if (target_stat.fits < min_stat.fits)
+				continue;
+
+			/* Then select the CPU with lowest cost */
+
+			/* Get the performance of the CPU w/ the waking task */
+			target_perf = find_cpu_max_util(cpu, p, cpu);
+			target_perf = min(target_perf, cpu_actual_cap);
+
+			/* Needing a higher OPP means a higher cost */
+			if (target_perf > min_stat.max_perf)
+				continue;
 
 			/*
-			 * Skip CPUs that cannot satisfy the capacity request.
-			 * IOW, placing the task there would make the CPU
-			 * overutilized. Take uclamp into account to see how
-			 * much capacity we can get out of the CPU; this is
-			 * aligned with sched_cpu_util().
+			 * At this point, target's cost can be either equal or
+			 * lower than the current minimum cost.
 			 */
-			if (uclamp_is_used() && !uclamp_rq_is_idle(rq)) {
-				/*
-				 * Open code uclamp_rq_util_with() except for
-				 * the clamp() part. I.e.: apply max aggregation
-				 * only. util_fits_cpu() logic requires to
-				 * operate on non clamped util but must use the
-				 * max-aggregated uclamp_{min, max}.
-				 */
-				rq_util_min = uclamp_rq_get(rq, UCLAMP_MIN);
-				rq_util_max = uclamp_rq_get(rq, UCLAMP_MAX);
 
-				util_min = max(rq_util_min, p_util_min);
-				util_max = max(rq_util_max, p_util_max);
-			}
+			/* Gather more statistics */
+			target_stat.cpu = cpu;
+			target_stat.runnable = cpu_runnable(cpu_rq(cpu));
+			target_stat.capa = capacity_of(cpu);
+			target_stat.nr_running = cpu_rq(cpu)->cfs.h_nr_runnable;
 
-			fits = util_fits_cpu(util, util_min, util_max, cpu);
-			if (!fits)
+			/* If the target needs a lower OPP, then look up for
+			 * the corresponding OPP and its associated cost.
+			 * Otherwise at same cost level, select the CPU which
+			 * provides best performance.
+			 */
+			if (target_perf < min_stat.min_perf)
+				find_pd_cost(pd->em_pd, target_perf, &target_stat);
+			else if (!update_best_cpu(&target_stat, &min_stat, prev_cpu, sd))
 				continue;
 
-			lsub_positive(&cpu_cap, util);
-
-			if (cpu == prev_cpu) {
-				/* Always use prev_cpu as a candidate. */
-				prev_spare_cap = cpu_cap;
-				prev_fits = fits;
-			} else if ((fits > max_fits) ||
-				   ((fits == max_fits) && ((long)cpu_cap > max_spare_cap))) {
-				/*
-				 * Find the CPU with the maximum spare capacity
-				 * among the remaining CPUs in the performance
-				 * domain.
-				 */
-				max_spare_cap = cpu_cap;
-				max_spare_cap_cpu = cpu;
-				max_fits = fits;
-			}
+			/* Save the new most efficient CPU of the PD */
+			min_stat = target_stat;
 		}
 
-		if (max_spare_cap_cpu < 0 && prev_spare_cap < 0)
+		if (min_stat.cpu == -1)
 			continue;
 
-		eenv_pd_busy_time(&eenv, cpus, p);
-		/* Compute the 'base' energy of the pd, without @p */
-		base_energy = compute_energy(&eenv, pd, cpus, p, -1);
+		if (min_stat.fits < best_fits)
+			continue;
 
-		/* Evaluate the energy impact of using prev_cpu. */
-		if (prev_spare_cap > -1) {
-			prev_delta = compute_energy(&eenv, pd, cpus, p,
-						    prev_cpu);
-			/* CPU utilization has changed */
-			if (prev_delta < base_energy)
-				goto unlock;
-			prev_delta -= base_energy;
-			prev_actual_cap = cpu_actual_cap;
-			best_delta = min(best_delta, prev_delta);
-		}
+		/* Idle system costs nothing */
+		target_stat.max_perf = 0;
+		target_stat.cost = 0;
 
-		/* Evaluate the energy impact of using max_spare_cap_cpu. */
-		if (max_spare_cap_cpu >= 0 && max_spare_cap > prev_spare_cap) {
-			/* Current best energy cpu fits better */
-			if (max_fits < best_fits)
-				continue;
+		/* Estimate utilization and cost without p */
+		for_each_cpu(cpu, cpus) {
+			unsigned long target_util;
 
-			/*
-			 * Both don't fit performance hint (i.e. uclamp_min)
-			 * but best energy cpu has better capacity.
-			 */
-			if ((max_fits < 0) &&
-			    (cpu_actual_cap <= best_actual_cap))
-				continue;
+			/* Accumulate actual utilization w/o task p */
+			pd_actual_util += find_cpu_actual_util(cpu, p);
 
-			cur_delta = compute_energy(&eenv, pd, cpus, p,
-						   max_spare_cap_cpu);
-			/* CPU utilization has changed */
-			if (cur_delta < base_energy)
-				goto unlock;
-			cur_delta -= base_energy;
+			/* Get the max utilization of the CPU w/o task p */
+			target_util = find_cpu_max_util(cpu, p, -1);
+			target_util = min(target_util, cpu_actual_cap);
 
-			/*
-			 * Both fit for the task but best energy cpu has lower
-			 * energy impact.
-			 */
-			if ((max_fits > 0) && (best_fits > 0) &&
-			    (cur_delta >= best_delta))
+			/* Current OPP is enough */
+			if (target_util <= target_stat.max_perf)
 				continue;
 
-			best_delta = cur_delta;
-			best_energy_cpu = max_spare_cap_cpu;
-			best_fits = max_fits;
-			best_actual_cap = cpu_actual_cap;
+			/* Compute and save the cost of the OPP */
+			find_pd_cost(pd->em_pd, target_util, &target_stat);
+			max_cost = target_stat.cost;
 		}
-	}
-	rcu_read_unlock();
 
-	if ((best_fits > prev_fits) ||
-	    ((best_fits > 0) && (best_delta < prev_delta)) ||
-	    ((best_fits < 0) && (best_actual_cap > prev_actual_cap)))
-		target = best_energy_cpu;
+		/* Add the energy cost of p */
+		delta_nrg = task_util * min_stat.cost;
 
-	return target;
+		/*
+		 * Compute the energy cost of others running at higher OPP
+		 * because of p.
+		 */
+		if (min_stat.cost > max_cost)
+			delta_nrg += pd_actual_util * (min_stat.cost - max_cost);
+
+		/* Delta energy with p */
+		trace_sched_compute_energy_tp(p, min_stat.cpu, delta_nrg,
+				min_stat.max_perf, pd_actual_util + task_util);
+
+		/*
+		 * The probability that delta energies are equals is almost
+		 * null. PDs being sorted by max capacity, keep the one with
+		 * highest max capacity if this happens.
+		 * TODO: add a margin in energy cost and take into account
+		 * other stats.
+		 */
+		if ((min_stat.fits == best_fits) &&
+		    (delta_nrg >= best_nrg))
+			continue;
+
+		best_fits = min_stat.fits;
+		best_nrg = delta_nrg;
+		best_cpu = min_stat.cpu;
+	}
 
 unlock:
 	rcu_read_unlock();
 
+	if (best_cpu >= 0)
+		target = best_cpu;
+
 	return target;
 }
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/7 v3] energy model: Remove unused em_cpu_energy()
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
                   ` (2 preceding siblings ...)
  2025-02-28 13:39 ` [PATCH 3/7 v3] sched/fair: Rework feec() to use cost instead of spare capacity Vincent Guittot
@ 2025-02-28 13:39 ` Vincent Guittot
  2025-02-28 13:39 ` [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS Vincent Guittot
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

Remove the unused function em_cpu_energy()

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
 include/linux/energy_model.h | 99 ------------------------------------
 1 file changed, 99 deletions(-)

diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
index 967650726619..441100686f1b 100644
--- a/include/linux/energy_model.h
+++ b/include/linux/energy_model.h
@@ -236,99 +236,6 @@ em_pd_get_previous_state(struct em_perf_state *table,
 	return -1;
 }
 
-/**
- * em_cpu_energy() - Estimates the energy consumed by the CPUs of a
- *		performance domain
- * @pd		: performance domain for which energy has to be estimated
- * @max_util	: highest utilization among CPUs of the domain
- * @sum_util	: sum of the utilization of all CPUs in the domain
- * @allowed_cpu_cap	: maximum allowed CPU capacity for the @pd, which
- *			  might reflect reduced frequency (due to thermal)
- *
- * This function must be used only for CPU devices. There is no validation,
- * i.e. if the EM is a CPU type and has cpumask allocated. It is called from
- * the scheduler code quite frequently and that is why there is not checks.
- *
- * Return: the sum of the energy consumed by the CPUs of the domain assuming
- * a capacity state satisfying the max utilization of the domain.
- */
-static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
-				unsigned long max_util, unsigned long sum_util,
-				unsigned long allowed_cpu_cap)
-{
-	struct em_perf_table *em_table;
-	struct em_perf_state *ps;
-	int i;
-
-#ifdef CONFIG_SCHED_DEBUG
-	WARN_ONCE(!rcu_read_lock_held(), "EM: rcu read lock needed\n");
-#endif
-
-	if (!sum_util)
-		return 0;
-
-	/*
-	 * In order to predict the performance state, map the utilization of
-	 * the most utilized CPU of the performance domain to a requested
-	 * performance, like schedutil. Take also into account that the real
-	 * performance might be set lower (due to thermal capping). Thus, clamp
-	 * max utilization to the allowed CPU capacity before calculating
-	 * effective performance.
-	 */
-	max_util = min(max_util, allowed_cpu_cap);
-
-	/*
-	 * Find the lowest performance state of the Energy Model above the
-	 * requested performance.
-	 */
-	em_table = rcu_dereference(pd->em_table);
-	i = em_pd_get_efficient_state(em_table->state, pd, max_util);
-	ps = &em_table->state[i];
-
-	/*
-	 * The performance (capacity) of a CPU in the domain at the performance
-	 * state (ps) can be computed as:
-	 *
-	 *                     ps->freq * scale_cpu
-	 *   ps->performance = --------------------                  (1)
-	 *                         cpu_max_freq
-	 *
-	 * So, ignoring the costs of idle states (which are not available in
-	 * the EM), the energy consumed by this CPU at that performance state
-	 * is estimated as:
-	 *
-	 *             ps->power * cpu_util
-	 *   cpu_nrg = --------------------                          (2)
-	 *               ps->performance
-	 *
-	 * since 'cpu_util / ps->performance' represents its percentage of busy
-	 * time.
-	 *
-	 *   NOTE: Although the result of this computation actually is in
-	 *         units of power, it can be manipulated as an energy value
-	 *         over a scheduling period, since it is assumed to be
-	 *         constant during that interval.
-	 *
-	 * By injecting (1) in (2), 'cpu_nrg' can be re-expressed as a product
-	 * of two terms:
-	 *
-	 *             ps->power * cpu_max_freq
-	 *   cpu_nrg = ------------------------ * cpu_util           (3)
-	 *               ps->freq * scale_cpu
-	 *
-	 * The first term is static, and is stored in the em_perf_state struct
-	 * as 'ps->cost'.
-	 *
-	 * Since all CPUs of the domain have the same micro-architecture, they
-	 * share the same 'ps->cost', and the same CPU capacity. Hence, the
-	 * total energy of the domain (which is the simple sum of the energy of
-	 * all of its CPUs) can be factorized as:
-	 *
-	 *   pd_nrg = ps->cost * \Sum cpu_util                       (4)
-	 */
-	return ps->cost * sum_util;
-}
-
 /**
  * em_pd_nr_perf_states() - Get the number of performance states of a perf.
  *				domain
@@ -395,12 +302,6 @@ em_pd_get_previous_state(struct em_perf_state *table, int nr_perf_states,
 {
 	return -1;
 }
-static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
-			unsigned long max_util, unsigned long sum_util,
-			unsigned long allowed_cpu_cap)
-{
-	return 0;
-}
 static inline int em_pd_nr_perf_states(struct em_perf_domain *pd)
 {
 	return 0;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
                   ` (3 preceding siblings ...)
  2025-02-28 13:39 ` [PATCH 4/7 v3] energy model: Remove unused em_cpu_energy() Vincent Guittot
@ 2025-02-28 13:39 ` Vincent Guittot
  2025-03-01 15:33   ` kernel test robot
  2025-03-01 15:33   ` kernel test robot
  2025-02-28 13:39 ` [PATCH 6/7 v3] sched/fair: Add misfit case to push task mecanism " Vincent Guittot
  2025-02-28 13:40 ` [PATCH 7/7 v3] sched/fair: Update overutilized detection Vincent Guittot
  6 siblings, 2 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

EAS is based on wakeup events to efficiently place tasks on the system, but
there are cases where a task doesn't have wakeup events anymore or at a far
too low pace. For such situation, we can take advantage of the task being
put back in the enqueued list to check if it should be pushed on another
CPU. When the task is alone on the CPU, it's never put back in the enqueued
list; In this special case, we use the tick to run the check.

Wake up events remain the main way to migrate tasks but we now detect
situation where a task is stuck on a CPU by checking that its utilization
is larger than the max available compute capacity (max cpu capacity or
uclamp max setting)

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
 kernel/sched/fair.c  | 220 +++++++++++++++++++++++++++++++++++++++++++
 kernel/sched/sched.h |   2 +
 2 files changed, 222 insertions(+)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index a9b97bbc085f..5b2f88dec70e 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7051,6 +7051,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
 	hrtick_update(rq);
 }
 
+static void fair_remove_pushable_task(struct rq *rq, struct task_struct *p);
 static void set_next_buddy(struct sched_entity *se);
 
 /*
@@ -7081,6 +7082,8 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags)
 		h_nr_idle = task_has_idle_policy(p);
 		if (task_sleep || task_delayed || !se->sched_delayed)
 			h_nr_runnable = 1;
+
+		fair_remove_pushable_task(rq, p);
 	} else {
 		cfs_rq = group_cfs_rq(se);
 		slice = cfs_rq_min_slice(cfs_rq);
@@ -8589,6 +8592,197 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
 	return target;
 }
 
+static inline bool task_stuck_on_cpu(struct task_struct *p, int cpu)
+{
+	unsigned long max_capa, util;
+
+	max_capa = min(get_actual_cpu_capacity(cpu),
+		       uclamp_eff_value(p, UCLAMP_MAX));
+	util = max(task_util_est(p), task_runnable(p));
+
+	/*
+	 * Return true only if the task might not sleep/wakeup because of a low
+	 * compute capacity. Tasks, which wake up regularly, will be handled by
+	 * feec().
+	 */
+	return (util > max_capa);
+}
+
+static inline bool sched_energy_push_task(struct task_struct *p, struct rq *rq)
+{
+	if (p->nr_cpus_allowed == 1)
+		return false;
+
+	if (is_rd_overutilized(rq->rd))
+		return false;
+
+	if (task_stuck_on_cpu(p, cpu_of(rq)))
+		return true;
+
+	return false;
+}
+
+static int active_load_balance_cpu_stop(void *data);
+
+static inline void check_pushable_task(struct task_struct *p, struct rq *rq)
+{
+	int new_cpu, cpu = cpu_of(rq);
+
+	if (!sched_energy_enabled())
+		return;
+
+	if (WARN_ON(!p))
+		return;
+
+	if (WARN_ON(!task_current(rq, p)))
+		return;
+
+	if (is_migration_disabled(p))
+		return;
+
+	/* If there are several task, wait for being put back */
+	if (rq->nr_running > 1)
+		return;
+
+	if (!sched_energy_push_task(p, rq))
+		return;
+
+	new_cpu = find_energy_efficient_cpu(p, cpu);
+
+	if (new_cpu == cpu)
+		return;
+
+	/*
+	 * ->active_balance synchronizes accesses to
+	 * ->active_balance_work.  Once set, it's cleared
+	 * only after active load balance is finished.
+	 */
+	if (!rq->active_balance) {
+		rq->active_balance = 1;
+		rq->push_cpu = new_cpu;
+	} else
+		return;
+
+	raw_spin_rq_unlock(rq);
+	stop_one_cpu_nowait(cpu,
+		active_load_balance_cpu_stop, rq,
+		&rq->active_balance_work);
+	raw_spin_rq_lock(rq);
+}
+
+static inline int has_pushable_tasks(struct rq *rq)
+{
+	return !plist_head_empty(&rq->cfs.pushable_tasks);
+}
+
+static struct task_struct *pick_next_pushable_fair_task(struct rq *rq)
+{
+	struct task_struct *p;
+
+	if (!has_pushable_tasks(rq))
+		return NULL;
+
+	p = plist_first_entry(&rq->cfs.pushable_tasks,
+			      struct task_struct, pushable_tasks);
+
+	WARN_ON_ONCE(rq->cpu != task_cpu(p));
+	WARN_ON_ONCE(task_current(rq, p));
+	WARN_ON_ONCE(p->nr_cpus_allowed <= 1);
+	WARN_ON_ONCE(!task_on_rq_queued(p));
+
+	/*
+	 * Remove task from the pushable list as we try only once after that
+	 * the task has been put back in enqueued list.
+	 */
+	plist_del(&p->pushable_tasks, &rq->cfs.pushable_tasks);
+
+	return p;
+}
+
+/*
+ * See if the non running fair tasks on this rq can be sent on other CPUs
+ * that fits better with their profile.
+ */
+static bool push_fair_task(struct rq *rq)
+{
+	struct task_struct *next_task;
+	int prev_cpu, new_cpu;
+	struct rq *new_rq;
+
+	next_task = pick_next_pushable_fair_task(rq);
+	if (!next_task)
+		return false;
+
+	if (is_migration_disabled(next_task))
+		return true;
+
+	/* We might release rq lock */
+	get_task_struct(next_task);
+
+	prev_cpu = rq->cpu;
+
+	new_cpu = find_energy_efficient_cpu(next_task, prev_cpu);
+
+	if (new_cpu == prev_cpu)
+		goto out;
+
+	new_rq = cpu_rq(new_cpu);
+
+	if (double_lock_balance(rq, new_rq)) {
+		/* The task has already migrated in between */
+		if (task_cpu(next_task) != rq->cpu) {
+			double_unlock_balance(rq, new_rq);
+			goto out;
+		}
+
+		deactivate_task(rq, next_task, 0);
+		set_task_cpu(next_task, new_cpu);
+		activate_task(new_rq, next_task, 0);
+
+		resched_curr(new_rq);
+
+		double_unlock_balance(rq, new_rq);
+	}
+
+out:
+	put_task_struct(next_task);
+
+	return true;
+}
+
+static void push_fair_tasks(struct rq *rq)
+{
+	/* push_fair_task() will return true if it moved a fair task */
+	while (push_fair_task(rq))
+		;
+}
+
+static DEFINE_PER_CPU(struct balance_callback, fair_push_head);
+
+static inline void fair_queue_pushable_tasks(struct rq *rq)
+{
+	if (!sched_energy_enabled() || !has_pushable_tasks(rq))
+		return;
+
+	queue_balance_callback(rq, &per_cpu(fair_push_head, rq->cpu), push_fair_tasks);
+}
+static void fair_remove_pushable_task(struct rq *rq, struct task_struct *p)
+{
+	if (sched_energy_enabled())
+		plist_del(&p->pushable_tasks, &rq->cfs.pushable_tasks);
+}
+
+static void fair_add_pushable_task(struct rq *rq, struct task_struct *p)
+{
+	if (sched_energy_enabled() && task_on_rq_queued(p) && !p->se.sched_delayed) {
+		if (sched_energy_push_task(p, rq)) {
+			plist_del(&p->pushable_tasks, &rq->cfs.pushable_tasks);
+			plist_node_init(&p->pushable_tasks, p->prio);
+			plist_add(&p->pushable_tasks, &rq->cfs.pushable_tasks);
+		}
+	}
+}
+
 /*
  * 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,
@@ -8758,6 +8952,10 @@ balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
 	return sched_balance_newidle(rq, rf) != 0;
 }
 #else
+static inline void check_pushable_task(struct task_struct *p, struct rq *rq) {}
+static inline void fair_queue_pushable_tasks(struct rq *rq) {}
+static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
+static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
 static inline void set_task_max_allowed_capacity(struct task_struct *p) {}
 #endif /* CONFIG_SMP */
 
@@ -8947,6 +9145,12 @@ pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf
 		put_prev_entity(cfs_rq, pse);
 		set_next_entity(cfs_rq, se);
 
+		/*
+		 * The previous task might be eligible for being pushed on
+		 * another cpu if it is still active.
+		 */
+		fair_add_pushable_task(rq, prev);
+
 		__set_next_task_fair(rq, p, true);
 	}
 
@@ -9019,6 +9223,13 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct t
 		cfs_rq = cfs_rq_of(se);
 		put_prev_entity(cfs_rq, se);
 	}
+
+	/*
+	 * The previous task might be eligible for being pushed on another cpu
+	 * if it is still active.
+	 */
+	fair_add_pushable_task(rq, prev);
+
 }
 
 /*
@@ -13151,6 +13362,7 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
 	if (static_branch_unlikely(&sched_numa_balancing))
 		task_tick_numa(rq, curr);
 
+	check_pushable_task(curr, rq);
 	update_misfit_status(curr, rq);
 	check_update_overutilized_status(task_rq(curr));
 
@@ -13303,6 +13515,8 @@ static void __set_next_task_fair(struct rq *rq, struct task_struct *p, bool firs
 {
 	struct sched_entity *se = &p->se;
 
+	fair_remove_pushable_task(rq, p);
+
 #ifdef CONFIG_SMP
 	if (task_on_rq_queued(p)) {
 		/*
@@ -13320,6 +13534,11 @@ static void __set_next_task_fair(struct rq *rq, struct task_struct *p, bool firs
 	if (hrtick_enabled_fair(rq))
 		hrtick_start_fair(rq, p);
 
+	/*
+	 * Try to push prev task before checking misfit for next task as
+	 * the migration of prev can make next fitting the CPU
+	 */
+	fair_queue_pushable_tasks(rq);
 	update_misfit_status(p, rq);
 	sched_fair_update_stop_tick(rq, p);
 }
@@ -13350,6 +13569,7 @@ void init_cfs_rq(struct cfs_rq *cfs_rq)
 	cfs_rq->tasks_timeline = RB_ROOT_CACHED;
 	cfs_rq->min_vruntime = (u64)(-(1LL << 20));
 #ifdef CONFIG_SMP
+	plist_head_init(&cfs_rq->pushable_tasks);
 	raw_spin_lock_init(&cfs_rq->removed.lock);
 #endif
 }
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index ab16d3d0e51c..2db198dccf21 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -722,6 +722,8 @@ struct cfs_rq {
 	struct list_head	leaf_cfs_rq_list;
 	struct task_group	*tg;	/* group that "owns" this runqueue */
 
+	struct plist_head	pushable_tasks;
+
 	/* Locally cached copy of our task_group's idle value */
 	int			idle;
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 6/7 v3] sched/fair: Add misfit case to push task mecanism for EAS
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
                   ` (4 preceding siblings ...)
  2025-02-28 13:39 ` [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS Vincent Guittot
@ 2025-02-28 13:39 ` Vincent Guittot
  2025-02-28 13:40 ` [PATCH 7/7 v3] sched/fair: Update overutilized detection Vincent Guittot
  6 siblings, 0 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:39 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

Some task misfit cases can be handled directly by the push mecanism
instead of triggering an idle load balance to pull the task on a better
CPU.

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
 kernel/sched/fair.c | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 5b2f88dec70e..87bf054cf36b 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -8508,6 +8508,8 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
 			target_stat.runnable = cpu_runnable(cpu_rq(cpu));
 			target_stat.capa = capacity_of(cpu);
 			target_stat.nr_running = cpu_rq(cpu)->cfs.h_nr_runnable;
+			if ((p->on_rq) && (!p->se.sched_delayed) && (cpu == prev_cpu))
+				target_stat.nr_running--;
 
 			/* If the target needs a lower OPP, then look up for
 			 * the corresponding OPP and its associated cost.
@@ -8613,6 +8615,9 @@ static inline bool sched_energy_push_task(struct task_struct *p, struct rq *rq)
 	if (p->nr_cpus_allowed == 1)
 		return false;
 
+	if (!task_fits_cpu(p, cpu_of(rq)))
+		return true;
+
 	if (is_rd_overutilized(rq->rd))
 		return false;
 
@@ -8624,33 +8629,33 @@ static inline bool sched_energy_push_task(struct task_struct *p, struct rq *rq)
 
 static int active_load_balance_cpu_stop(void *data);
 
-static inline void check_pushable_task(struct task_struct *p, struct rq *rq)
+static inline bool check_pushable_task(struct task_struct *p, struct rq *rq)
 {
 	int new_cpu, cpu = cpu_of(rq);
 
 	if (!sched_energy_enabled())
-		return;
+		return false;
 
 	if (WARN_ON(!p))
-		return;
+		return false;
 
 	if (WARN_ON(!task_current(rq, p)))
-		return;
+		return false;
 
 	if (is_migration_disabled(p))
-		return;
+		return false;
 
 	/* If there are several task, wait for being put back */
 	if (rq->nr_running > 1)
-		return;
+		return false;
 
 	if (!sched_energy_push_task(p, rq))
-		return;
+		return false;
 
 	new_cpu = find_energy_efficient_cpu(p, cpu);
 
 	if (new_cpu == cpu)
-		return;
+		return false;
 
 	/*
 	 * ->active_balance synchronizes accesses to
@@ -8661,13 +8666,15 @@ static inline void check_pushable_task(struct task_struct *p, struct rq *rq)
 		rq->active_balance = 1;
 		rq->push_cpu = new_cpu;
 	} else
-		return;
+		return false;
 
 	raw_spin_rq_unlock(rq);
 	stop_one_cpu_nowait(cpu,
 		active_load_balance_cpu_stop, rq,
 		&rq->active_balance_work);
 	raw_spin_rq_lock(rq);
+
+	return true;
 }
 
 static inline int has_pushable_tasks(struct rq *rq)
@@ -8952,7 +8959,7 @@ balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
 	return sched_balance_newidle(rq, rf) != 0;
 }
 #else
-static inline void check_pushable_task(struct task_struct *p, struct rq *rq) {}
+static inline bool check_pushable_task(struct task_struct *p, struct rq *rq) {}
 static inline void fair_queue_pushable_tasks(struct rq *rq) {}
 static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
 static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
@@ -13362,9 +13369,10 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
 	if (static_branch_unlikely(&sched_numa_balancing))
 		task_tick_numa(rq, curr);
 
-	check_pushable_task(curr, rq);
-	update_misfit_status(curr, rq);
-	check_update_overutilized_status(task_rq(curr));
+	if (!check_pushable_task(curr, rq)) {
+		update_misfit_status(curr, rq);
+		check_update_overutilized_status(task_rq(curr));
+	}
 
 	task_tick_core(rq, curr);
 }
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 7/7 v3] sched/fair: Update overutilized detection
  2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
                   ` (5 preceding siblings ...)
  2025-02-28 13:39 ` [PATCH 6/7 v3] sched/fair: Add misfit case to push task mecanism " Vincent Guittot
@ 2025-02-28 13:40 ` Vincent Guittot
  6 siblings, 0 replies; 10+ messages in thread
From: Vincent Guittot @ 2025-02-28 13:40 UTC (permalink / raw)
  To: mingo, peterz, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, lukasz.luba, rafael.j.wysocki, pierre.gondois,
	linux-kernel
  Cc: qyousef, hongyan.xia2, christian.loehle, luis.machado, qperret,
	Vincent Guittot

Checking uclamp_min is useless and counterproductive for overutilized state
as misfit can now happen without being in overutilized state

Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
 kernel/sched/fair.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 87bf054cf36b..2219db9636c2 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6831,16 +6831,15 @@ static inline void hrtick_update(struct rq *rq)
 #ifdef CONFIG_SMP
 static inline bool cpu_overutilized(int cpu)
 {
-	unsigned long  rq_util_min, rq_util_max;
+	unsigned long rq_util_max;
 
 	if (!sched_energy_enabled())
 		return false;
 
-	rq_util_min = uclamp_rq_get(cpu_rq(cpu), UCLAMP_MIN);
 	rq_util_max = uclamp_rq_get(cpu_rq(cpu), UCLAMP_MAX);
 
 	/* Return true only if the utilization doesn't fit CPU's capacity */
-	return !util_fits_cpu(cpu_util_cfs(cpu), rq_util_min, rq_util_max, cpu);
+	return !util_fits_cpu(cpu_util_cfs(cpu), 0, rq_util_max, cpu);
 }
 
 /*
-- 
2.43.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS
  2025-02-28 13:39 ` [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS Vincent Guittot
@ 2025-03-01 15:33   ` kernel test robot
  2025-03-01 15:33   ` kernel test robot
  1 sibling, 0 replies; 10+ messages in thread
From: kernel test robot @ 2025-03-01 15:33 UTC (permalink / raw)
  To: Vincent Guittot, mingo, peterz, juri.lelli, dietmar.eggemann,
	rostedt, bsegall, mgorman, vschneid, lukasz.luba,
	rafael.j.wysocki, pierre.gondois, linux-kernel
  Cc: oe-kbuild-all, qyousef, hongyan.xia2, christian.loehle,
	luis.machado, qperret, Vincent Guittot

Hi Vincent,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/sched/core]
[also build test ERROR on peterz-queue/sched/core linus/master v6.14-rc4 next-20250228]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vincent-Guittot/sched-fair-Filter-false-overloaded_group-case-for-EAS/20250228-214408
base:   tip/sched/core
patch link:    https://lore.kernel.org/r/20250228134000.1226665-6-vincent.guittot%40linaro.org
patch subject: [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS
config: arc-randconfig-002-20250301 (https://download.01.org/0day-ci/archive/20250301/202503012314.oQzjTBLS-lkp@intel.com/config)
compiler: arceb-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250301/202503012314.oQzjTBLS-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503012314.oQzjTBLS-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

>> kernel/sched/fair.c:8957:13: error: conflicting types for 'fair_remove_pushable_task'; have 'void(struct cfs_rq *, struct task_struct *)'
    8957 | static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/sched/fair.c:7054:13: note: previous declaration of 'fair_remove_pushable_task' with type 'void(struct rq *, struct task_struct *)'
    7054 | static void fair_remove_pushable_task(struct rq *rq, struct task_struct *p);
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/sched/fair.c: In function 'pick_next_task_fair':
>> kernel/sched/fair.c:9152:40: error: passing argument 1 of 'fair_add_pushable_task' from incompatible pointer type [-Werror=incompatible-pointer-types]
    9152 |                 fair_add_pushable_task(rq, prev);
         |                                        ^~
         |                                        |
         |                                        struct rq *
   kernel/sched/fair.c:8958:58: note: expected 'struct cfs_rq *' but argument is of type 'struct rq *'
    8958 | static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |                                           ~~~~~~~~~~~~~~~^~~~~~
   kernel/sched/fair.c: In function 'put_prev_task_fair':
   kernel/sched/fair.c:9231:32: error: passing argument 1 of 'fair_add_pushable_task' from incompatible pointer type [-Werror=incompatible-pointer-types]
    9231 |         fair_add_pushable_task(rq, prev);
         |                                ^~
         |                                |
         |                                struct rq *
   kernel/sched/fair.c:8958:58: note: expected 'struct cfs_rq *' but argument is of type 'struct rq *'
    8958 | static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |                                           ~~~~~~~~~~~~~~~^~~~~~
   kernel/sched/fair.c: In function '__set_next_task_fair':
>> kernel/sched/fair.c:13518:35: error: passing argument 1 of 'fair_remove_pushable_task' from incompatible pointer type [-Werror=incompatible-pointer-types]
   13518 |         fair_remove_pushable_task(rq, p);
         |                                   ^~
         |                                   |
         |                                   struct rq *
   kernel/sched/fair.c:8957:54: note: expected 'struct cfs_rq *' but argument is of type 'struct rq *'
    8957 | static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |                                       ~~~~~~~~~~~~~~~^~~~~~
   kernel/sched/fair.c: At top level:
>> kernel/sched/fair.c:7054:13: warning: 'fair_remove_pushable_task' used but never defined
    7054 | static void fair_remove_pushable_task(struct rq *rq, struct task_struct *p);
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +8957 kernel/sched/fair.c

  8945	
  8946	static int
  8947	balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
  8948	{
  8949		if (sched_fair_runnable(rq))
  8950			return 1;
  8951	
  8952		return sched_balance_newidle(rq, rf) != 0;
  8953	}
  8954	#else
  8955	static inline void check_pushable_task(struct task_struct *p, struct rq *rq) {}
  8956	static inline void fair_queue_pushable_tasks(struct rq *rq) {}
> 8957	static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
  8958	static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
  8959	static inline void set_task_max_allowed_capacity(struct task_struct *p) {}
  8960	#endif /* CONFIG_SMP */
  8961	
  8962	static void set_next_buddy(struct sched_entity *se)
  8963	{
  8964		for_each_sched_entity(se) {
  8965			if (SCHED_WARN_ON(!se->on_rq))
  8966				return;
  8967			if (se_is_idle(se))
  8968				return;
  8969			cfs_rq_of(se)->next = se;
  8970		}
  8971	}
  8972	
  8973	/*
  8974	 * Preempt the current task with a newly woken task if needed:
  8975	 */
  8976	static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int wake_flags)
  8977	{
  8978		struct task_struct *donor = rq->donor;
  8979		struct sched_entity *se = &donor->se, *pse = &p->se;
  8980		struct cfs_rq *cfs_rq = task_cfs_rq(donor);
  8981		int cse_is_idle, pse_is_idle;
  8982	
  8983		if (unlikely(se == pse))
  8984			return;
  8985	
  8986		/*
  8987		 * This is possible from callers such as attach_tasks(), in which we
  8988		 * unconditionally wakeup_preempt() after an enqueue (which may have
  8989		 * lead to a throttle).  This both saves work and prevents false
  8990		 * next-buddy nomination below.
  8991		 */
  8992		if (unlikely(throttled_hierarchy(cfs_rq_of(pse))))
  8993			return;
  8994	
  8995		if (sched_feat(NEXT_BUDDY) && !(wake_flags & WF_FORK) && !pse->sched_delayed) {
  8996			set_next_buddy(pse);
  8997		}
  8998	
  8999		/*
  9000		 * We can come here with TIF_NEED_RESCHED already set from new task
  9001		 * wake up path.
  9002		 *
  9003		 * Note: this also catches the edge-case of curr being in a throttled
  9004		 * group (e.g. via set_curr_task), since update_curr() (in the
  9005		 * enqueue of curr) will have resulted in resched being set.  This
  9006		 * prevents us from potentially nominating it as a false LAST_BUDDY
  9007		 * below.
  9008		 */
  9009		if (test_tsk_need_resched(rq->curr))
  9010			return;
  9011	
  9012		if (!sched_feat(WAKEUP_PREEMPTION))
  9013			return;
  9014	
  9015		find_matching_se(&se, &pse);
  9016		WARN_ON_ONCE(!pse);
  9017	
  9018		cse_is_idle = se_is_idle(se);
  9019		pse_is_idle = se_is_idle(pse);
  9020	
  9021		/*
  9022		 * Preempt an idle entity in favor of a non-idle entity (and don't preempt
  9023		 * in the inverse case).
  9024		 */
  9025		if (cse_is_idle && !pse_is_idle) {
  9026			/*
  9027			 * When non-idle entity preempt an idle entity,
  9028			 * don't give idle entity slice protection.
  9029			 */
  9030			cancel_protect_slice(se);
  9031			goto preempt;
  9032		}
  9033	
  9034		if (cse_is_idle != pse_is_idle)
  9035			return;
  9036	
  9037		/*
  9038		 * BATCH and IDLE tasks do not preempt others.
  9039		 */
  9040		if (unlikely(!normal_policy(p->policy)))
  9041			return;
  9042	
  9043		cfs_rq = cfs_rq_of(se);
  9044		update_curr(cfs_rq);
  9045		/*
  9046		 * If @p has a shorter slice than current and @p is eligible, override
  9047		 * current's slice protection in order to allow preemption.
  9048		 *
  9049		 * Note that even if @p does not turn out to be the most eligible
  9050		 * task at this moment, current's slice protection will be lost.
  9051		 */
  9052		if (do_preempt_short(cfs_rq, pse, se))
  9053			cancel_protect_slice(se);
  9054	
  9055		/*
  9056		 * If @p has become the most eligible task, force preemption.
  9057		 */
  9058		if (pick_eevdf(cfs_rq) == pse)
  9059			goto preempt;
  9060	
  9061		return;
  9062	
  9063	preempt:
  9064		resched_curr_lazy(rq);
  9065	}
  9066	
  9067	static struct task_struct *pick_task_fair(struct rq *rq)
  9068	{
  9069		struct sched_entity *se;
  9070		struct cfs_rq *cfs_rq;
  9071	
  9072	again:
  9073		cfs_rq = &rq->cfs;
  9074		if (!cfs_rq->nr_queued)
  9075			return NULL;
  9076	
  9077		do {
  9078			/* Might not have done put_prev_entity() */
  9079			if (cfs_rq->curr && cfs_rq->curr->on_rq)
  9080				update_curr(cfs_rq);
  9081	
  9082			if (unlikely(check_cfs_rq_runtime(cfs_rq)))
  9083				goto again;
  9084	
  9085			se = pick_next_entity(rq, cfs_rq);
  9086			if (!se)
  9087				goto again;
  9088			cfs_rq = group_cfs_rq(se);
  9089		} while (cfs_rq);
  9090	
  9091		return task_of(se);
  9092	}
  9093	
  9094	static void __set_next_task_fair(struct rq *rq, struct task_struct *p, bool first);
  9095	static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first);
  9096	
  9097	struct task_struct *
  9098	pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
  9099	{
  9100		struct sched_entity *se;
  9101		struct task_struct *p;
  9102		int new_tasks;
  9103	
  9104	again:
  9105		p = pick_task_fair(rq);
  9106		if (!p)
  9107			goto idle;
  9108		se = &p->se;
  9109	
  9110	#ifdef CONFIG_FAIR_GROUP_SCHED
  9111		if (prev->sched_class != &fair_sched_class)
  9112			goto simple;
  9113	
  9114		__put_prev_set_next_dl_server(rq, prev, p);
  9115	
  9116		/*
  9117		 * Because of the set_next_buddy() in dequeue_task_fair() it is rather
  9118		 * likely that a next task is from the same cgroup as the current.
  9119		 *
  9120		 * Therefore attempt to avoid putting and setting the entire cgroup
  9121		 * hierarchy, only change the part that actually changes.
  9122		 *
  9123		 * Since we haven't yet done put_prev_entity and if the selected task
  9124		 * is a different task than we started out with, try and touch the
  9125		 * least amount of cfs_rqs.
  9126		 */
  9127		if (prev != p) {
  9128			struct sched_entity *pse = &prev->se;
  9129			struct cfs_rq *cfs_rq;
  9130	
  9131			while (!(cfs_rq = is_same_group(se, pse))) {
  9132				int se_depth = se->depth;
  9133				int pse_depth = pse->depth;
  9134	
  9135				if (se_depth <= pse_depth) {
  9136					put_prev_entity(cfs_rq_of(pse), pse);
  9137					pse = parent_entity(pse);
  9138				}
  9139				if (se_depth >= pse_depth) {
  9140					set_next_entity(cfs_rq_of(se), se);
  9141					se = parent_entity(se);
  9142				}
  9143			}
  9144	
  9145			put_prev_entity(cfs_rq, pse);
  9146			set_next_entity(cfs_rq, se);
  9147	
  9148			/*
  9149			 * The previous task might be eligible for being pushed on
  9150			 * another cpu if it is still active.
  9151			 */
> 9152			fair_add_pushable_task(rq, prev);
  9153	
  9154			__set_next_task_fair(rq, p, true);
  9155		}
  9156	
  9157		return p;
  9158	
  9159	simple:
  9160	#endif
  9161		put_prev_set_next_task(rq, prev, p);
  9162		return p;
  9163	
  9164	idle:
  9165		if (!rf)
  9166			return NULL;
  9167	
  9168		new_tasks = sched_balance_newidle(rq, rf);
  9169	
  9170		/*
  9171		 * Because sched_balance_newidle() releases (and re-acquires) rq->lock, it is
  9172		 * possible for any higher priority task to appear. In that case we
  9173		 * must re-start the pick_next_entity() loop.
  9174		 */
  9175		if (new_tasks < 0)
  9176			return RETRY_TASK;
  9177	
  9178		if (new_tasks > 0)
  9179			goto again;
  9180	
  9181		/*
  9182		 * rq is about to be idle, check if we need to update the
  9183		 * lost_idle_time of clock_pelt
  9184		 */
  9185		update_idle_rq_clock_pelt(rq);
  9186	
  9187		return NULL;
  9188	}
  9189	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS
  2025-02-28 13:39 ` [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS Vincent Guittot
  2025-03-01 15:33   ` kernel test robot
@ 2025-03-01 15:33   ` kernel test robot
  1 sibling, 0 replies; 10+ messages in thread
From: kernel test robot @ 2025-03-01 15:33 UTC (permalink / raw)
  To: Vincent Guittot, mingo, peterz, juri.lelli, dietmar.eggemann,
	rostedt, bsegall, mgorman, vschneid, lukasz.luba,
	rafael.j.wysocki, pierre.gondois, linux-kernel
  Cc: llvm, oe-kbuild-all, qyousef, hongyan.xia2, christian.loehle,
	luis.machado, qperret, Vincent Guittot

Hi Vincent,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/sched/core]
[also build test ERROR on peterz-queue/sched/core linus/master v6.14-rc4 next-20250228]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vincent-Guittot/sched-fair-Filter-false-overloaded_group-case-for-EAS/20250228-214408
base:   tip/sched/core
patch link:    https://lore.kernel.org/r/20250228134000.1226665-6-vincent.guittot%40linaro.org
patch subject: [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS
config: i386-buildonly-randconfig-001-20250301 (https://download.01.org/0day-ci/archive/20250301/202503012344.WKL9UWX1-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250301/202503012344.WKL9UWX1-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503012344.WKL9UWX1-lkp@intel.com/

All errors (new ones prefixed by >>):

>> kernel/sched/fair.c:8957:13: error: conflicting types for 'fair_remove_pushable_task'
    8957 | static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |             ^
   kernel/sched/fair.c:7054:13: note: previous declaration is here
    7054 | static void fair_remove_pushable_task(struct rq *rq, struct task_struct *p);
         |             ^
>> kernel/sched/fair.c:9152:26: error: incompatible pointer types passing 'struct rq *' to parameter of type 'struct cfs_rq *' [-Werror,-Wincompatible-pointer-types]
    9152 |                 fair_add_pushable_task(rq, prev);
         |                                        ^~
   kernel/sched/fair.c:8958:58: note: passing argument to parameter 'cfs_rq' here
    8958 | static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |                                                          ^
   kernel/sched/fair.c:9231:25: error: incompatible pointer types passing 'struct rq *' to parameter of type 'struct cfs_rq *' [-Werror,-Wincompatible-pointer-types]
    9231 |         fair_add_pushable_task(rq, prev);
         |                                ^~
   kernel/sched/fair.c:8958:58: note: passing argument to parameter 'cfs_rq' here
    8958 | static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
         |                                                          ^
   3 errors generated.


vim +/fair_remove_pushable_task +8957 kernel/sched/fair.c

  8945	
  8946	static int
  8947	balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
  8948	{
  8949		if (sched_fair_runnable(rq))
  8950			return 1;
  8951	
  8952		return sched_balance_newidle(rq, rf) != 0;
  8953	}
  8954	#else
  8955	static inline void check_pushable_task(struct task_struct *p, struct rq *rq) {}
  8956	static inline void fair_queue_pushable_tasks(struct rq *rq) {}
> 8957	static void fair_remove_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
  8958	static inline void fair_add_pushable_task(struct cfs_rq *cfs_rq, struct task_struct *p) {}
  8959	static inline void set_task_max_allowed_capacity(struct task_struct *p) {}
  8960	#endif /* CONFIG_SMP */
  8961	
  8962	static void set_next_buddy(struct sched_entity *se)
  8963	{
  8964		for_each_sched_entity(se) {
  8965			if (SCHED_WARN_ON(!se->on_rq))
  8966				return;
  8967			if (se_is_idle(se))
  8968				return;
  8969			cfs_rq_of(se)->next = se;
  8970		}
  8971	}
  8972	
  8973	/*
  8974	 * Preempt the current task with a newly woken task if needed:
  8975	 */
  8976	static void check_preempt_wakeup_fair(struct rq *rq, struct task_struct *p, int wake_flags)
  8977	{
  8978		struct task_struct *donor = rq->donor;
  8979		struct sched_entity *se = &donor->se, *pse = &p->se;
  8980		struct cfs_rq *cfs_rq = task_cfs_rq(donor);
  8981		int cse_is_idle, pse_is_idle;
  8982	
  8983		if (unlikely(se == pse))
  8984			return;
  8985	
  8986		/*
  8987		 * This is possible from callers such as attach_tasks(), in which we
  8988		 * unconditionally wakeup_preempt() after an enqueue (which may have
  8989		 * lead to a throttle).  This both saves work and prevents false
  8990		 * next-buddy nomination below.
  8991		 */
  8992		if (unlikely(throttled_hierarchy(cfs_rq_of(pse))))
  8993			return;
  8994	
  8995		if (sched_feat(NEXT_BUDDY) && !(wake_flags & WF_FORK) && !pse->sched_delayed) {
  8996			set_next_buddy(pse);
  8997		}
  8998	
  8999		/*
  9000		 * We can come here with TIF_NEED_RESCHED already set from new task
  9001		 * wake up path.
  9002		 *
  9003		 * Note: this also catches the edge-case of curr being in a throttled
  9004		 * group (e.g. via set_curr_task), since update_curr() (in the
  9005		 * enqueue of curr) will have resulted in resched being set.  This
  9006		 * prevents us from potentially nominating it as a false LAST_BUDDY
  9007		 * below.
  9008		 */
  9009		if (test_tsk_need_resched(rq->curr))
  9010			return;
  9011	
  9012		if (!sched_feat(WAKEUP_PREEMPTION))
  9013			return;
  9014	
  9015		find_matching_se(&se, &pse);
  9016		WARN_ON_ONCE(!pse);
  9017	
  9018		cse_is_idle = se_is_idle(se);
  9019		pse_is_idle = se_is_idle(pse);
  9020	
  9021		/*
  9022		 * Preempt an idle entity in favor of a non-idle entity (and don't preempt
  9023		 * in the inverse case).
  9024		 */
  9025		if (cse_is_idle && !pse_is_idle) {
  9026			/*
  9027			 * When non-idle entity preempt an idle entity,
  9028			 * don't give idle entity slice protection.
  9029			 */
  9030			cancel_protect_slice(se);
  9031			goto preempt;
  9032		}
  9033	
  9034		if (cse_is_idle != pse_is_idle)
  9035			return;
  9036	
  9037		/*
  9038		 * BATCH and IDLE tasks do not preempt others.
  9039		 */
  9040		if (unlikely(!normal_policy(p->policy)))
  9041			return;
  9042	
  9043		cfs_rq = cfs_rq_of(se);
  9044		update_curr(cfs_rq);
  9045		/*
  9046		 * If @p has a shorter slice than current and @p is eligible, override
  9047		 * current's slice protection in order to allow preemption.
  9048		 *
  9049		 * Note that even if @p does not turn out to be the most eligible
  9050		 * task at this moment, current's slice protection will be lost.
  9051		 */
  9052		if (do_preempt_short(cfs_rq, pse, se))
  9053			cancel_protect_slice(se);
  9054	
  9055		/*
  9056		 * If @p has become the most eligible task, force preemption.
  9057		 */
  9058		if (pick_eevdf(cfs_rq) == pse)
  9059			goto preempt;
  9060	
  9061		return;
  9062	
  9063	preempt:
  9064		resched_curr_lazy(rq);
  9065	}
  9066	
  9067	static struct task_struct *pick_task_fair(struct rq *rq)
  9068	{
  9069		struct sched_entity *se;
  9070		struct cfs_rq *cfs_rq;
  9071	
  9072	again:
  9073		cfs_rq = &rq->cfs;
  9074		if (!cfs_rq->nr_queued)
  9075			return NULL;
  9076	
  9077		do {
  9078			/* Might not have done put_prev_entity() */
  9079			if (cfs_rq->curr && cfs_rq->curr->on_rq)
  9080				update_curr(cfs_rq);
  9081	
  9082			if (unlikely(check_cfs_rq_runtime(cfs_rq)))
  9083				goto again;
  9084	
  9085			se = pick_next_entity(rq, cfs_rq);
  9086			if (!se)
  9087				goto again;
  9088			cfs_rq = group_cfs_rq(se);
  9089		} while (cfs_rq);
  9090	
  9091		return task_of(se);
  9092	}
  9093	
  9094	static void __set_next_task_fair(struct rq *rq, struct task_struct *p, bool first);
  9095	static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first);
  9096	
  9097	struct task_struct *
  9098	pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
  9099	{
  9100		struct sched_entity *se;
  9101		struct task_struct *p;
  9102		int new_tasks;
  9103	
  9104	again:
  9105		p = pick_task_fair(rq);
  9106		if (!p)
  9107			goto idle;
  9108		se = &p->se;
  9109	
  9110	#ifdef CONFIG_FAIR_GROUP_SCHED
  9111		if (prev->sched_class != &fair_sched_class)
  9112			goto simple;
  9113	
  9114		__put_prev_set_next_dl_server(rq, prev, p);
  9115	
  9116		/*
  9117		 * Because of the set_next_buddy() in dequeue_task_fair() it is rather
  9118		 * likely that a next task is from the same cgroup as the current.
  9119		 *
  9120		 * Therefore attempt to avoid putting and setting the entire cgroup
  9121		 * hierarchy, only change the part that actually changes.
  9122		 *
  9123		 * Since we haven't yet done put_prev_entity and if the selected task
  9124		 * is a different task than we started out with, try and touch the
  9125		 * least amount of cfs_rqs.
  9126		 */
  9127		if (prev != p) {
  9128			struct sched_entity *pse = &prev->se;
  9129			struct cfs_rq *cfs_rq;
  9130	
  9131			while (!(cfs_rq = is_same_group(se, pse))) {
  9132				int se_depth = se->depth;
  9133				int pse_depth = pse->depth;
  9134	
  9135				if (se_depth <= pse_depth) {
  9136					put_prev_entity(cfs_rq_of(pse), pse);
  9137					pse = parent_entity(pse);
  9138				}
  9139				if (se_depth >= pse_depth) {
  9140					set_next_entity(cfs_rq_of(se), se);
  9141					se = parent_entity(se);
  9142				}
  9143			}
  9144	
  9145			put_prev_entity(cfs_rq, pse);
  9146			set_next_entity(cfs_rq, se);
  9147	
  9148			/*
  9149			 * The previous task might be eligible for being pushed on
  9150			 * another cpu if it is still active.
  9151			 */
> 9152			fair_add_pushable_task(rq, prev);
  9153	
  9154			__set_next_task_fair(rq, p, true);
  9155		}
  9156	
  9157		return p;
  9158	
  9159	simple:
  9160	#endif
  9161		put_prev_set_next_task(rq, prev, p);
  9162		return p;
  9163	
  9164	idle:
  9165		if (!rf)
  9166			return NULL;
  9167	
  9168		new_tasks = sched_balance_newidle(rq, rf);
  9169	
  9170		/*
  9171		 * Because sched_balance_newidle() releases (and re-acquires) rq->lock, it is
  9172		 * possible for any higher priority task to appear. In that case we
  9173		 * must re-start the pick_next_entity() loop.
  9174		 */
  9175		if (new_tasks < 0)
  9176			return RETRY_TASK;
  9177	
  9178		if (new_tasks > 0)
  9179			goto again;
  9180	
  9181		/*
  9182		 * rq is about to be idle, check if we need to update the
  9183		 * lost_idle_time of clock_pelt
  9184		 */
  9185		update_idle_rq_clock_pelt(rq);
  9186	
  9187		return NULL;
  9188	}
  9189	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-03-01 15:33 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-02-28 13:39 [PATCH 0/7 v3] sched/fair: Rework EAS to handle more cases Vincent Guittot
2025-02-28 13:39 ` [PATCH 1/7 v3] sched/fair: Filter false overloaded_group case for EAS Vincent Guittot
2025-02-28 13:39 ` [PATCH 2/7 v3] energy model: Add a get previous state function Vincent Guittot
2025-02-28 13:39 ` [PATCH 3/7 v3] sched/fair: Rework feec() to use cost instead of spare capacity Vincent Guittot
2025-02-28 13:39 ` [PATCH 4/7 v3] energy model: Remove unused em_cpu_energy() Vincent Guittot
2025-02-28 13:39 ` [PATCH 5/7 v3] sched/fair: Add push task mechanism for EAS Vincent Guittot
2025-03-01 15:33   ` kernel test robot
2025-03-01 15:33   ` kernel test robot
2025-02-28 13:39 ` [PATCH 6/7 v3] sched/fair: Add misfit case to push task mecanism " Vincent Guittot
2025-02-28 13:40 ` [PATCH 7/7 v3] sched/fair: Update overutilized detection Vincent Guittot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®