cpufreq: handle SW coordinated CPUs
authorRickard Andersson <rickard.andersson@stericsson.com>
Thu, 27 Dec 2012 14:55:38 +0000 (14:55 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 1 Feb 2013 23:01:13 +0000 (00:01 +0100)
This patch fixes a bug that occurred when we had load on a secondary CPU
and the primary CPU was sleeping. Only one sampling timer was spawned
and it was spawned as a deferred timer on the primary CPU, so when a
secondary CPU had a change in load this was not detected by the cpufreq
governor (both ondemand and conservative).

This patch make sure that deferred timers are run on all CPUs in the
case of software controlled CPUs that run on the same frequency.

Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/cpufreq/cpufreq_conservative.c
drivers/cpufreq/cpufreq_governor.c
drivers/cpufreq/cpufreq_governor.h
drivers/cpufreq/cpufreq_ondemand.c

index 64ef737e7e72979aa5d29a865d8ff4b9db4206cb..b9d7f14d7d3d683bb1dd9cbd0478f8200de43105 100644 (file)
@@ -122,7 +122,8 @@ static void cs_dbs_timer(struct work_struct *work)
 
        dbs_check_cpu(&cs_dbs_data, cpu);
 
-       schedule_delayed_work_on(cpu, &dbs_info->cdbs.work, delay);
+       schedule_delayed_work_on(smp_processor_id(), &dbs_info->cdbs.work,
+                       delay);
        mutex_unlock(&dbs_info->cdbs.timer_mutex);
 }
 
index 6c5f1d383cdce86b783fc37077798a8a980c4116..b0e4506f2cae126dde8bf3cfef255f66caa61add 100644 (file)
@@ -161,13 +161,23 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
 }
 EXPORT_SYMBOL_GPL(dbs_check_cpu);
 
+bool dbs_sw_coordinated_cpus(struct cpu_dbs_common_info *cdbs)
+{
+       struct cpufreq_policy *policy = cdbs->cur_policy;
+
+       return cpumask_weight(policy->cpus) > 1;
+}
+EXPORT_SYMBOL_GPL(dbs_sw_coordinated_cpus);
+
 static inline void dbs_timer_init(struct dbs_data *dbs_data,
-               struct cpu_dbs_common_info *cdbs, unsigned int sampling_rate)
+                                 struct cpu_dbs_common_info *cdbs,
+                                 unsigned int sampling_rate,
+                                 int cpu)
 {
        int delay = delay_for_sampling_rate(sampling_rate);
+       struct cpu_dbs_common_info *cdbs_local = dbs_data->get_cpu_cdbs(cpu);
 
-       INIT_DEFERRABLE_WORK(&cdbs->work, dbs_data->gov_dbs_timer);
-       schedule_delayed_work_on(cdbs->cpu, &cdbs->work, delay);
+       schedule_delayed_work_on(cpu, &cdbs_local->work, delay);
 }
 
 static inline void dbs_timer_exit(struct cpu_dbs_common_info *cdbs)
@@ -217,6 +227,10 @@ int cpufreq_governor_dbs(struct dbs_data *dbs_data,
                        if (ignore_nice)
                                j_cdbs->prev_cpu_nice =
                                        kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+
+                       mutex_init(&j_cdbs->timer_mutex);
+                       INIT_DEFERRABLE_WORK(&j_cdbs->work,
+                                            dbs_data->gov_dbs_timer);
                }
 
                /*
@@ -275,15 +289,33 @@ second_time:
                }
                mutex_unlock(&dbs_data->mutex);
 
-               mutex_init(&cpu_cdbs->timer_mutex);
-               dbs_timer_init(dbs_data, cpu_cdbs, *sampling_rate);
+               if (dbs_sw_coordinated_cpus(cpu_cdbs)) {
+                       for_each_cpu(j, policy->cpus) {
+                               struct cpu_dbs_common_info *j_cdbs;
+
+                               j_cdbs = dbs_data->get_cpu_cdbs(j);
+                               dbs_timer_init(dbs_data, j_cdbs,
+                                              *sampling_rate, j);
+                       }
+               } else {
+                       dbs_timer_init(dbs_data, cpu_cdbs, *sampling_rate, cpu);
+               }
                break;
 
        case CPUFREQ_GOV_STOP:
                if (dbs_data->governor == GOV_CONSERVATIVE)
                        cs_dbs_info->enable = 0;
 
-               dbs_timer_exit(cpu_cdbs);
+               if (dbs_sw_coordinated_cpus(cpu_cdbs)) {
+                       for_each_cpu(j, policy->cpus) {
+                               struct cpu_dbs_common_info *j_cdbs;
+
+                               j_cdbs = dbs_data->get_cpu_cdbs(j);
+                               dbs_timer_exit(j_cdbs);
+                       }
+               } else {
+                       dbs_timer_exit(cpu_cdbs);
+               }
 
                mutex_lock(&dbs_data->mutex);
                mutex_destroy(&cpu_cdbs->timer_mutex);
index f6616540c53d929043ca1fb2b7ed4ce64a6739b0..5bf6fb8023efde98ff83c7aa367e564c546b5439 100644 (file)
@@ -171,6 +171,7 @@ static inline int delay_for_sampling_rate(unsigned int sampling_rate)
 
 u64 get_cpu_idle_time(unsigned int cpu, u64 *wall);
 void dbs_check_cpu(struct dbs_data *dbs_data, int cpu);
+bool dbs_sw_coordinated_cpus(struct cpu_dbs_common_info *cdbs);
 int cpufreq_governor_dbs(struct dbs_data *dbs_data,
                struct cpufreq_policy *policy, unsigned int event);
 #endif /* _CPUFREQ_GOVERNER_H */
index 7731f7c7e79ae162a51a5d57c6a4366492d2c810..93bb56d14cfaf2c4e8ecc8f7e52dd02664a62b67 100644 (file)
@@ -243,7 +243,8 @@ static void od_dbs_timer(struct work_struct *work)
                }
        }
 
-       schedule_delayed_work_on(cpu, &dbs_info->cdbs.work, delay);
+       schedule_delayed_work_on(smp_processor_id(), &dbs_info->cdbs.work,
+                       delay);
        mutex_unlock(&dbs_info->cdbs.timer_mutex);
 }