cpufreq: Max freq invariant scheduler load-tracking and cpu capacity support
authorDietmar Eggemann <dietmar.eggemann@arm.com>
Tue, 22 Sep 2015 15:47:48 +0000 (16:47 +0100)
committerLeo Yan <leo.yan@linaro.org>
Tue, 10 May 2016 08:49:54 +0000 (16:49 +0800)
Implements cpufreq_scale_max_freq_capacity() to provide the scheduler
with a maximum frequency scaling correction factor for more accurate
load-tracking and cpu capacity handling by being able to deal with
frequency capping.

This scaling factor describes the influence of running a cpu with a
current maximum frequency lower than the absolute possible maximum
frequency on load tracking and cpu capacity.

The factor is:

current_max_freq(cpu) << SCHED_CAPACITY_SHIFT / max_freq(cpu)

In fact, max_freq_scale should be a struct cpufreq_policy data member.
But this would require that the scheduler hot path (__update_load_avg())
would have to grab the cpufreq lock. This can be avoided by using per-cpu
data initialized to SCHED_CAPACITY_SCALE for max_freq_scale.

Signed-off-by: Dietmar Eggemann <dietmar.eggemann@arm.com>
drivers/cpufreq/cpufreq.c
include/linux/cpufreq.h

index 46a7d62c817431d507960ce0d7100075d4d79407..96f1a88608191d72127d9279b704103b709c4cb0 100644 (file)
@@ -352,12 +352,14 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
  *********************************************************************/
 
 static DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
+static DEFINE_PER_CPU(unsigned long, max_freq_scale) = SCHED_CAPACITY_SCALE;
 
 static void
 scale_freq_capacity(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs)
 {
        unsigned long cur = freqs ? freqs->new : policy->cur;
        unsigned long scale = (cur << SCHED_CAPACITY_SHIFT) / policy->max;
+       struct cpufreq_cpuinfo *cpuinfo = &policy->cpuinfo;
        int cpu;
 
        pr_debug("cpus %*pbl cur/cur max freq %lu/%u kHz freq scale %lu\n",
@@ -365,6 +367,18 @@ scale_freq_capacity(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs)
 
        for_each_cpu(cpu, policy->cpus)
                per_cpu(freq_scale, cpu) = scale;
+
+       if (freqs)
+               return;
+
+       scale = (policy->max << SCHED_CAPACITY_SHIFT) / cpuinfo->max_freq;
+
+       pr_debug("cpus %*pbl cur max/max freq %u/%u kHz max freq scale %lu\n",
+                cpumask_pr_args(policy->cpus), policy->max, cpuinfo->max_freq,
+                scale);
+
+       for_each_cpu(cpu, policy->cpus)
+               per_cpu(max_freq_scale, cpu) = scale;
 }
 
 unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu)
@@ -372,6 +386,11 @@ unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu)
        return per_cpu(freq_scale, cpu);
 }
 
+unsigned long cpufreq_scale_max_freq_capacity(int cpu)
+{
+       return per_cpu(max_freq_scale, cpu);
+}
+
 static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
                struct cpufreq_freqs *freqs, unsigned int state)
 {
index 11c65f536b6cbb74303978ab590efeb2a8c7d915..9b1d61e1f1d71571a582a114f1aa3da66621c0bf 100644 (file)
@@ -619,4 +619,5 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
 
 struct sched_domain;
 unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu);
+unsigned long cpufreq_scale_max_freq_capacity(int cpu);
 #endif /* _LINUX_CPUFREQ_H */