cpufreq: interactive: fix policy locking
authorDmitry Torokhov <dtor@chromium.org>
Wed, 4 Feb 2015 21:54:48 +0000 (13:54 -0800)
committerAmit Pundir <amit.pundir@linaro.org>
Thu, 7 Apr 2016 11:20:07 +0000 (16:50 +0530)
cpufreq_interactive_speedchange_task() is running as a separate kernel
thread and is calling __cpufreq_driver_target(), which requires callers
to hold policy->rwsem for writing to prevent racing with other parts of
the kernel trying to adjust the frequency, for example kernel thermal
throttling. Let's change the code to take policy->rwsem and while at it
refactor the code a bit.

This was originally 2 changes reviewed at:
https://chromium-review.googlesource.com/246273
https://chromium-review.googlesource.com/256120

Change-Id: Icc2d97c6c1b929acd2ee32e8c81d81fd2af778ab
Signed-off-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Dmitry Torokhov <dtor@google.com>
drivers/cpufreq/cpufreq_interactive.c

index 0be66df4a6e6b5d47fb80768367fe45e7424d87c..5224e897d460d02abf52da104ebdc8d8d315fd86 100644 (file)
@@ -511,6 +511,58 @@ static void cpufreq_interactive_idle_end(void)
        up_read(&pcpu->enable_sem);
 }
 
+static void cpufreq_interactive_get_policy_info(struct cpufreq_policy *policy,
+                                               unsigned int *pmax_freq,
+                                               u64 *phvt, u64 *pfvt)
+{
+       struct cpufreq_interactive_cpuinfo *pcpu;
+       unsigned int max_freq = 0;
+       u64 hvt = ~0ULL, fvt = 0;
+       unsigned int i;
+
+       for_each_cpu(i, policy->cpus) {
+               pcpu = &per_cpu(cpuinfo, i);
+
+               fvt = max(fvt, pcpu->loc_floor_val_time);
+               if (pcpu->target_freq > max_freq) {
+                       max_freq = pcpu->target_freq;
+                       hvt = pcpu->loc_hispeed_val_time;
+               } else if (pcpu->target_freq == max_freq) {
+                       hvt = min(hvt, pcpu->loc_hispeed_val_time);
+               }
+       }
+
+       *pmax_freq = max_freq;
+       *phvt = hvt;
+       *pfvt = fvt;
+}
+
+static void cpufreq_interactive_adjust_cpu(unsigned int cpu,
+                                          struct cpufreq_policy *policy)
+{
+       struct cpufreq_interactive_cpuinfo *pcpu;
+       u64 hvt, fvt;
+       unsigned int max_freq;
+       int i;
+
+       cpufreq_interactive_get_policy_info(policy, &max_freq, &hvt, &fvt);
+
+       for_each_cpu(i, policy->cpus) {
+               pcpu = &per_cpu(cpuinfo, i);
+               pcpu->pol_floor_val_time = fvt;
+       }
+
+       if (max_freq != policy->cur) {
+               __cpufreq_driver_target(policy, max_freq, CPUFREQ_RELATION_H);
+               for_each_cpu(i, policy->cpus) {
+                       pcpu = &per_cpu(cpuinfo, i);
+                       pcpu->pol_hispeed_val_time = hvt;
+               }
+       }
+
+       trace_cpufreq_interactive_setspeed(cpu, max_freq, policy->cur);
+}
+
 static int cpufreq_interactive_speedchange_task(void *data)
 {
        unsigned int cpu;
@@ -539,49 +591,18 @@ static int cpufreq_interactive_speedchange_task(void *data)
                spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
 
                for_each_cpu(cpu, &tmp_mask) {
-                       unsigned int j;
-                       unsigned int max_freq = 0;
-                       struct cpufreq_interactive_cpuinfo *pjcpu;
-                       u64 hvt = ~0ULL, fvt = 0;
-
                        pcpu = &per_cpu(cpuinfo, cpu);
-                       if (!down_read_trylock(&pcpu->enable_sem))
-                               continue;
-                       if (!pcpu->governor_enabled) {
-                               up_read(&pcpu->enable_sem);
-                               continue;
-                       }
 
-                       for_each_cpu(j, pcpu->policy->cpus) {
-                               pjcpu = &per_cpu(cpuinfo, j);
+                       down_write(&pcpu->policy->rwsem);
 
-                               fvt = max(fvt, pjcpu->loc_floor_val_time);
-                               if (pjcpu->target_freq > max_freq) {
-                                       max_freq = pjcpu->target_freq;
-                                       hvt = pjcpu->loc_hispeed_val_time;
-                               } else if (pjcpu->target_freq == max_freq) {
-                                       hvt = min(hvt, pjcpu->loc_hispeed_val_time);
-                               }
-                       }
-                       for_each_cpu(j, pcpu->policy->cpus) {
-                               pjcpu = &per_cpu(cpuinfo, j);
-                               pjcpu->pol_floor_val_time = fvt;
-                       }
-
-                       if (max_freq != pcpu->policy->cur) {
-                               __cpufreq_driver_target(pcpu->policy,
-                                                       max_freq,
-                                                       CPUFREQ_RELATION_H);
-                               for_each_cpu(j, pcpu->policy->cpus) {
-                                       pjcpu = &per_cpu(cpuinfo, j);
-                                       pjcpu->pol_hispeed_val_time = hvt;
-                               }
+                       if (likely(down_read_trylock(&pcpu->enable_sem))) {
+                               if (likely(pcpu->governor_enabled))
+                                       cpufreq_interactive_adjust_cpu(cpu,
+                                                       pcpu->policy);
+                               up_read(&pcpu->enable_sem);
                        }
-                       trace_cpufreq_interactive_setspeed(cpu,
-                                                    pcpu->target_freq,
-                                                    pcpu->policy->cur);
 
-                       up_read(&pcpu->enable_sem);
+                       up_write(&pcpu->policy->rwsem);
                }
        }