cpufreq: Enforce PM QoS min/max limits
Antti P Miettinen [Tue, 27 Dec 2011 10:41:39 +0000 (12:41 +0200)]
Observe PM QoS CPU frequency minimum and maximum in addition
to policy settings.

Bug 888312

Change-Id: Ia4f60a1649a9952e02f6847c8add3b2ea5d47524
Reviewed-on: http://git-master/r/72207
Signed-off-by: Antti P Miettinen <amiettinen@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/75884
Reviewed-by: Automatic_Commit_Validation_User

drivers/cpufreq/cpufreq.c
kernel/pm_qos_params.c

index dda10dc..65fba49 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/completion.h>
 #include <linux/mutex.h>
 #include <linux/syscore_ops.h>
+#include <linux/pm_qos_params.h>
 
 #include <trace/events/power.h>
 
@@ -1632,9 +1633,17 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
                                struct cpufreq_policy *policy)
 {
        int ret = 0;
+       unsigned int pmin = policy->min;
+       unsigned int pmax = policy->max;
+       unsigned int qmin = pm_qos_request(PM_QOS_CPU_FREQ_MIN);
+       unsigned int qmax = pm_qos_request(PM_QOS_CPU_FREQ_MAX);
 
-       pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
-               policy->min, policy->max);
+       pr_debug("setting new policy for CPU %u: %u - %u (%u - %u) kHz\n",
+               policy->cpu, pmin, pmax, qmin, qmax);
+
+       /* clamp the new policy to PM QoS limits */
+       policy->min = max(pmin, qmin);
+       policy->max = min(pmax, qmax);
 
        memcpy(&policy->cpuinfo, &data->cpuinfo,
                                sizeof(struct cpufreq_cpuinfo));
@@ -1709,6 +1718,9 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
        }
 
 error_out:
+       /* restore the limits that the policy requested */
+       policy->min = pmin;
+       policy->max = pmax;
        return ret;
 }
 
@@ -1904,9 +1916,36 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
 }
 EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
 
+static int cpu_freq_notify(struct notifier_block *b,
+                          unsigned long l, void *v);
+
+static struct notifier_block min_freq_notifier = {
+       .notifier_call = cpu_freq_notify,
+};
+static struct notifier_block max_freq_notifier = {
+       .notifier_call = cpu_freq_notify,
+};
+
+static int cpu_freq_notify(struct notifier_block *b,
+                          unsigned long l, void *v)
+{
+       int cpu;
+       pr_debug("PM QoS %s %lu\n",
+               b == &min_freq_notifier ? "min" : "max", l);
+       for_each_online_cpu(cpu) {
+               struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+               if (policy) {
+                       cpufreq_update_policy(policy->cpu);
+                       cpufreq_cpu_put(policy);
+               }
+       }
+       return NOTIFY_OK;
+}
+
 static int __init cpufreq_core_init(void)
 {
        int cpu;
+       int rc;
 
        for_each_possible_cpu(cpu) {
                per_cpu(cpufreq_policy_cpu, cpu) = -1;
@@ -1917,6 +1956,12 @@ static int __init cpufreq_core_init(void)
                                                &cpu_sysdev_class.kset.kobj);
        BUG_ON(!cpufreq_global_kobject);
        register_syscore_ops(&cpufreq_syscore_ops);
+       rc = pm_qos_add_notifier(PM_QOS_CPU_FREQ_MIN,
+                                &min_freq_notifier);
+       BUG_ON(rc);
+       rc = pm_qos_add_notifier(PM_QOS_CPU_FREQ_MAX,
+                                &max_freq_notifier);
+       BUG_ON(rc);
 
        return 0;
 }
index f798df9..324f342 100644 (file)
@@ -116,7 +116,7 @@ static struct pm_qos_object max_online_cpus_pm_qos = {
 
 static BLOCKING_NOTIFIER_HEAD(cpu_freq_min_notifier);
 static struct pm_qos_object cpu_freq_min_pm_qos = {
-       .requests = PLIST_HEAD_INIT(cpu_freq_min_pm_qos.requests, pm_qos_lock),
+       .requests = PLIST_HEAD_INIT(cpu_freq_min_pm_qos.requests),
        .notifiers = &cpu_freq_min_notifier,
        .name = "cpu_freq_min",
        .target_value = PM_QOS_CPU_FREQ_MIN_DEFAULT_VALUE,
@@ -127,7 +127,7 @@ static struct pm_qos_object cpu_freq_min_pm_qos = {
 
 static BLOCKING_NOTIFIER_HEAD(cpu_freq_max_notifier);
 static struct pm_qos_object cpu_freq_max_pm_qos = {
-       .requests = PLIST_HEAD_INIT(cpu_freq_max_pm_qos.requests, pm_qos_lock),
+       .requests = PLIST_HEAD_INIT(cpu_freq_max_pm_qos.requests),
        .notifiers = &cpu_freq_max_notifier,
        .name = "cpu_freq_max",
        .target_value = PM_QOS_CPU_FREQ_MAX_DEFAULT_VALUE,