Commit 8f1b971b authored by Lukasz Luba's avatar Lukasz Luba Committed by Peter Zijlstra

sched/cpufreq: Consider reduced CPU capacity in energy calculation

Energy Aware Scheduling (EAS) needs to predict the decisions made by
SchedUtil. The map_util_freq() exists to do that.

There are corner cases where the max allowed frequency might be reduced
(due to thermal). SchedUtil as a CPUFreq governor, is aware of that
but EAS is not. This patch aims to address it.

SchedUtil stores the maximum allowed frequency in
'sugov_policy::next_freq' field. EAS has to predict that value, which is
the real used frequency. That value is made after a call to
cpufreq_driver_resolve_freq() which clamps to the CPUFreq policy limits.
In the existing code EAS is not able to predict that real frequency.
This leads to energy estimation errors.

To avoid wrong energy estimation in EAS (due to frequency miss prediction)
make sure that the step which calculates Performance Domain frequency,
is also aware of the allowed CPU capacity.

Furthermore, modify map_util_freq() to not extend the frequency value.
Instead, use map_util_perf() to extend the util value in both places:
SchedUtil and EAS, but for EAS clamp it to max allowed CPU capacity.
In the end, we achieve the same desirable behavior for both subsystems
and alignment in regards to the real CPU frequency.
Signed-off-by: default avatarLukasz Luba <lukasz.luba@arm.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> (For the schedutil part)
Link: https://lore.kernel.org/r/20210614191238.23224-1-lukasz.luba@arm.com
parent 489f1645
...@@ -91,6 +91,8 @@ void em_dev_unregister_perf_domain(struct device *dev); ...@@ -91,6 +91,8 @@ void em_dev_unregister_perf_domain(struct device *dev);
* @pd : performance domain for which energy has to be estimated * @pd : performance domain for which energy has to be estimated
* @max_util : highest utilization among CPUs of the domain * @max_util : highest utilization among CPUs of the domain
* @sum_util : sum of the utilization of all CPUs in 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, * 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 * i.e. if the EM is a CPU type and has cpumask allocated. It is called from
...@@ -100,7 +102,8 @@ void em_dev_unregister_perf_domain(struct device *dev); ...@@ -100,7 +102,8 @@ void em_dev_unregister_perf_domain(struct device *dev);
* a capacity state satisfying the max utilization of the domain. * a capacity state satisfying the max utilization of the domain.
*/ */
static inline unsigned long em_cpu_energy(struct em_perf_domain *pd, static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
unsigned long max_util, unsigned long sum_util) unsigned long max_util, unsigned long sum_util,
unsigned long allowed_cpu_cap)
{ {
unsigned long freq, scale_cpu; unsigned long freq, scale_cpu;
struct em_perf_state *ps; struct em_perf_state *ps;
...@@ -112,11 +115,17 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd, ...@@ -112,11 +115,17 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
/* /*
* In order to predict the performance state, map the utilization of * In order to predict the performance state, map the utilization of
* the most utilized CPU of the performance domain to a requested * the most utilized CPU of the performance domain to a requested
* frequency, like schedutil. * frequency, like schedutil. Take also into account that the real
* frequency might be set lower (due to thermal capping). Thus, clamp
* max utilization to the allowed CPU capacity before calculating
* effective frequency.
*/ */
cpu = cpumask_first(to_cpumask(pd->cpus)); cpu = cpumask_first(to_cpumask(pd->cpus));
scale_cpu = arch_scale_cpu_capacity(cpu); scale_cpu = arch_scale_cpu_capacity(cpu);
ps = &pd->table[pd->nr_perf_states - 1]; ps = &pd->table[pd->nr_perf_states - 1];
max_util = map_util_perf(max_util);
max_util = min(max_util, allowed_cpu_cap);
freq = map_util_freq(max_util, ps->frequency, scale_cpu); freq = map_util_freq(max_util, ps->frequency, scale_cpu);
/* /*
...@@ -209,7 +218,8 @@ static inline struct em_perf_domain *em_pd_get(struct device *dev) ...@@ -209,7 +218,8 @@ static inline struct em_perf_domain *em_pd_get(struct device *dev)
return NULL; return NULL;
} }
static inline unsigned long em_cpu_energy(struct em_perf_domain *pd, static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
unsigned long max_util, unsigned long sum_util) unsigned long max_util, unsigned long sum_util,
unsigned long allowed_cpu_cap)
{ {
return 0; return 0;
} }
......
...@@ -26,7 +26,7 @@ bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy); ...@@ -26,7 +26,7 @@ bool cpufreq_this_cpu_can_update(struct cpufreq_policy *policy);
static inline unsigned long map_util_freq(unsigned long util, static inline unsigned long map_util_freq(unsigned long util,
unsigned long freq, unsigned long cap) unsigned long freq, unsigned long cap)
{ {
return (freq + (freq >> 2)) * util / cap; return freq * util / cap;
} }
static inline unsigned long map_util_perf(unsigned long util) static inline unsigned long map_util_perf(unsigned long util)
......
...@@ -151,6 +151,7 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, ...@@ -151,6 +151,7 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy,
unsigned int freq = arch_scale_freq_invariant() ? unsigned int freq = arch_scale_freq_invariant() ?
policy->cpuinfo.max_freq : policy->cur; policy->cpuinfo.max_freq : policy->cur;
util = map_util_perf(util);
freq = map_util_freq(util, freq, max); freq = map_util_freq(util, freq, max);
if (freq == sg_policy->cached_raw_freq && !sg_policy->need_freq_update) if (freq == sg_policy->cached_raw_freq && !sg_policy->need_freq_update)
......
...@@ -6592,7 +6592,7 @@ compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd) ...@@ -6592,7 +6592,7 @@ compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd)
max_util = max(max_util, min(cpu_util, _cpu_cap)); max_util = max(max_util, min(cpu_util, _cpu_cap));
} }
return em_cpu_energy(pd->em_pd, max_util, sum_util); return em_cpu_energy(pd->em_pd, max_util, sum_util, _cpu_cap);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment