cpufreq: Frequency invariant scheduler load-tracking support
authorDietmar Eggemann <dietmar.eggemann@arm.com>
Thu, 17 Sep 2015 15:10:56 +0000 (16:10 +0100)
committerAmit Pundir <amit.pundir@linaro.org>
Wed, 14 Sep 2016 09:18:50 +0000 (14:48 +0530)
Implements cpufreq_scale_freq_capacity() to provide the scheduler with a
frequency scaling correction factor for more accurate load-tracking.

The factor is:

current_freq(cpu) << SCHED_CAPACITY_SHIFT / max_freq(cpu)

In fact, 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 freq_scale.

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

index e49512718325b73d59fa52d5eaf225560cce91e0..0d494c9379209fa46b1f2fca793191b90c437ca8 100644 (file)
@@ -347,6 +347,31 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
 #endif
 }
 
+/*********************************************************************
+ *               FREQUENCY INVARIANT CPU CAPACITY                    *
+ *********************************************************************/
+
+static DEFINE_PER_CPU(unsigned long, 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;
+       int cpu;
+
+       pr_debug("cpus %*pbl cur/cur max freq %lu/%u kHz freq scale %lu\n",
+                cpumask_pr_args(policy->cpus), cur, policy->max, scale);
+
+       for_each_cpu(cpu, policy->cpus)
+               per_cpu(freq_scale, cpu) = scale;
+}
+
+unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu)
+{
+       return per_cpu(freq_scale, cpu);
+}
+
 static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
                struct cpufreq_freqs *freqs, unsigned int state)
 {
@@ -450,6 +475,8 @@ wait:
 
        spin_unlock(&policy->transition_lock);
 
+       scale_freq_capacity(policy, freqs);
+
        cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE);
 }
 EXPORT_SYMBOL_GPL(cpufreq_freq_transition_begin);
@@ -2126,6 +2153,8 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
        blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
                        CPUFREQ_NOTIFY, new_policy);
 
+       scale_freq_capacity(new_policy, NULL);
+
        policy->min = new_policy->min;
        policy->max = new_policy->max;
        trace_cpu_frequency_limits(policy->max, policy->min, policy->cpu);
index 3ac01621cd1fa0fc654a2177f195efb7d5329aed..5f1e66e544f5d54e12df0a27435630dd23586a65 100644 (file)
@@ -619,4 +619,7 @@ unsigned int cpufreq_generic_get(unsigned int cpu);
 int cpufreq_generic_init(struct cpufreq_policy *policy,
                struct cpufreq_frequency_table *table,
                unsigned int transition_latency);
+
+struct sched_domain;
+unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu);
 #endif /* _LINUX_CPUFREQ_H */