Re: [PATCH] sched/topology: Allow EAS without schedutil for artificial Energy Models
From: Rafael J. Wysocki
Date: Mon Jun 29 2026 - 15:06:32 EST
On Monday, June 29, 2026 5:16:17 PM CEST Rafael J. Wysocki (Intel) wrote:
> On Mon, Jun 29, 2026 at 10:36 AM Lucas de Lima Nóbrega
> <lucaslnobrega38@xxxxxxxxx> wrote:
> >
> > EAS currently refuses to enable energy-aware scheduling on a root
> > domain unless schedutil is the active CPUFreq governor for all of its
> > CPUs (cpufreq_ready_for_eas()). This requirement exists to protect the
> > accuracy of the energy estimate: EAS predicts the OPP a CPU will run
> > at from its utilization, which is only meaningful if the active
> > governor actually requests OPPs that way, and schedutil is the only
> > one that does.
> >
> > That requirement does not apply to artificial Energy Models
> > (EM_PERF_DOMAIN_ARTIFICIAL). An artificial EM is built from a
> > get_cost() callback instead of real power numbers, and only encodes a
> > cost ranking between CPUs (e.g. P-cores cost more than E-cores at a
> > given utilization). It never claims to predict real energy use at any
> > specific OPP, so there is no per-OPP accuracy for the governor
> > requirement to protect, regardless of which governor is in control or
> > whether it tracks utilization at all.
>
> But it is still about comparing the cost of running on different CPUs
> at different performance levels.
>
> For instance, say the scale-invariant utilization of a task is 256 and
> it can run either by itself on a P-core, or with another task whose
> utilization is 128 on an E-core, and say the P-core's and E-core's
> capacity is 1024 and 512, respectively.
>
> Say the cost function tells EAS that running a P-core at 1/4 of the
> capacity is cheaper than running an E-core at 3/4 capacity, so it will
> pick up the P-core to run that task, but if cpufreq ramps up the
> frequency of the P-core to the max when the task gets to it, it may
> actually turn out to be more expensive.
>
> This means that EAS still has an expectation regarding cpufreq which
> is that it will generally tend to run tasks at the performance level
> corresponding to the sum of their scale-invariant utilization at least
> roughly.
>
> IIUC this actually has nothing to do with whether or not the energy
> model used by EAS is artificial. The schedutil requirement is about
> choosing a performance level proportional to the utilization (which
> schedutil generally tends to do by design).
>
> > intel_pstate registers exactly this kind of artificial EM for hybrid
> > (P/E-core) systems without SMT, regardless of whether it operates in
> > active or passive mode. In active mode it never uses schedutil, since
> > HWP picks frequency autonomously, so on these systems EAS never
> > engages even though SD_ASYM_CPUCAPACITY, frequency invariance and the
> > EM are all in place: find_energy_efficient_cpu() is never reached
> > because is_rd_overutilized() is hardcoded to true whenever
> > sched_energy_enabled() is false. cppc_cpufreq registers the same kind
> > of ranking-only artificial EM and is affected the same way with any
> > non-schedutil governor.
> >
> > Allow EAS to be enabled when every CPU's EM in the root domain is
> > artificial, even when schedutil is not the active governor.
> >
> > Tested on a Raptor Lake-P laptop with nosmt=force and intel_pstate in
> > active/HWP mode: find_energy_efficient_cpu() was never called before
> > this change (confirmed via the sched_overutilized_tp tracepoint and
> > ftrace) and is exercised as expected afterwards.
>
> If this is about allowing EAS to work with intel_pstate running in the
> active mode, you may argue that what the processor firmware is doing
> when intel_pstate runs in the active mode is not much different from
> what schedutil would do. So a driver implementing an internal
> governor (that is, using the .set_policy() callback) would need to
> declare that its internal governor is as good as schedutil from EAS'
> perspective and so it will pass the "cpufreq readiness" check.
And I have a prototype patch (on top of 7.2-rc1) doing this which is
appended.
I wonder if it works for you (that is, if it allows intel_pstate and EAS to
work together both with schedutil and when intel_pstate operates in the
active mode with the "powersave" policy on your system).
Also I wonder why exactly you want intel_pstate in the active mode to
work with EAS. Do you see any significant improvement in that case?
---
drivers/cpufreq/cpufreq.c | 2 +-
drivers/cpufreq/intel_pstate.c | 11 +++++++++++
include/linux/cpufreq.h | 16 +++++++---------
kernel/sched/cpufreq_schedutil.c | 7 ++-----
4 files changed, 21 insertions(+), 15 deletions(-)
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -3058,7 +3058,7 @@ static bool cpufreq_policy_is_good_for_e
return false;
}
- return sugov_is_governor(policy);
+ return policy->eas_compatible;
}
bool cpufreq_ready_for_eas(const struct cpumask *cpu_mask)
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -2921,6 +2921,9 @@ static int intel_pstate_set_policy(struc
if (!hwp_boost)
intel_pstate_clear_update_util_hook(policy->cpu);
intel_pstate_hwp_set(policy->cpu);
+
+ policy->eas_compatible = hwp_is_hybrid &&
+ cpu->policy != CPUFREQ_POLICY_PERFORMANCE;
}
/*
* policy->cur is never updated with the intel_pstate driver, but it
@@ -2930,6 +2933,9 @@ static int intel_pstate_set_policy(struc
mutex_unlock(&intel_pstate_limits_lock);
+ if (policy->eas_compatible)
+ em_rebuild_sched_domains();
+
return 0;
}
@@ -3030,6 +3036,11 @@ static void intel_pstate_cpu_exit(struct
pr_debug("CPU %d exiting\n", policy->cpu);
policy->fast_switch_possible = false;
+
+ if (policy->eas_compatible) {
+ policy->eas_compatible = false;
+ em_rebuild_sched_domains();
+ }
}
static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -118,6 +118,13 @@ struct cpufreq_policy {
bool strict_target;
/*
+ * Set if the current governor meets EAS' expectations regarding
+ * performance scaling (that is, it selects performance levels
+ * proportional to CPU utilization at least roughly).
+ */
+ bool eas_compatible;
+
+ /*
* Set if inefficient frequencies were found in the frequency table.
* This indicates if the relation flag CPUFREQ_RELATION_E can be
* honored.
@@ -657,15 +664,6 @@ module_exit(__governor##_exit)
struct cpufreq_governor *cpufreq_default_governor(void);
struct cpufreq_governor *cpufreq_fallback_governor(void);
-#ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL
-bool sugov_is_governor(struct cpufreq_policy *policy);
-#else
-static inline bool sugov_is_governor(struct cpufreq_policy *policy)
-{
- return false;
-}
-#endif
-
static inline void cpufreq_policy_apply_limits(struct cpufreq_policy *policy)
{
if (policy->max < policy->cur)
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -797,6 +797,7 @@ out:
* Schedutil is the preferred governor for EAS, so rebuild sched domains
* on governor changes to make sure the scheduler knows about them.
*/
+ policy->eas_compatible = true;
em_rebuild_sched_domains();
mutex_unlock(&global_tunables_lock);
return 0;
@@ -839,6 +840,7 @@ static void sugov_exit(struct cpufreq_po
sugov_policy_free(sg_policy);
cpufreq_disable_fast_switch(policy);
+ policy->eas_compatible = false;
em_rebuild_sched_domains();
}
@@ -931,9 +933,4 @@ struct cpufreq_governor *cpufreq_default
}
#endif
-bool sugov_is_governor(struct cpufreq_policy *policy)
-{
- return policy->governor == &schedutil_gov;
-}
-
cpufreq_governor_init(schedutil_gov);