Cpufreq: Serialize policy update across hotplug
Puneet Saxena [Sat, 24 Aug 2013 11:35:14 +0000 (16:35 +0530)]
It serialise policy parameters update when hot-unplug
happens.

Bug 1327428

Change-Id: I0be6523add2866e45e413c79ef7c86605a359f44
Signed-off-by: Puneet Saxena <puneets@nvidia.com>
Reviewed-on: http://git-master/r/265844
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Peter Boonstoppel <pboonstoppel@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>

drivers/cpufreq/cpufreq.c

index 774b678..0c1f3ac 100644 (file)
@@ -72,6 +72,8 @@ static DEFINE_SPINLOCK(cpufreq_driver_lock);
  */
 static DEFINE_PER_CPU(int, cpufreq_policy_cpu);
 static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem);
+/* Serialize policy access across hotplug */
+static DEFINE_PER_CPU(struct mutex, cpu_policy_hp_mutex);
 
 #define lock_policy_rwsem(mode, cpu)                                   \
 static int lock_policy_rwsem_##mode                                    \
@@ -626,19 +628,29 @@ EXPORT_SYMBOL(cpufreq_global_kobject);
 
 static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
 {
+       int cpu;
        struct cpufreq_cpu_sysinfo *freqobj;
-       struct cpufreq_policy *policy;
+       struct cpufreq_policy *policy = NULL;
        struct freq_attr *fattr = to_attr(attr);
        ssize_t ret = -EINVAL;
 
        freqobj = to_cpu_kobj(kobj);
        if (!freqobj->cpu_policy)
                goto no_policy;
+       else
+               cpu = freqobj->cpu_policy->cpu;
 
-       policy = freqobj->cpu_policy;
-       policy = cpufreq_cpu_get(policy->cpu);
-       if (!policy)
+       mutex_lock(&per_cpu(cpu_policy_hp_mutex, cpu));
+       if (!freqobj->cpu_policy) {
+               mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
                goto no_policy;
+       }
+
+       policy = cpufreq_cpu_get(freqobj->cpu_policy->cpu);
+       if (!policy) {
+               mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
+               goto no_policy;
+       }
 
        if (lock_policy_rwsem_read(policy->cpu) < 0)
                goto fail;
@@ -651,6 +663,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
        unlock_policy_rwsem_read(policy->cpu);
 fail:
        cpufreq_cpu_put(policy);
+       mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
 no_policy:
        return ret;
 }
@@ -658,20 +671,30 @@ no_policy:
 static ssize_t store(struct kobject *kobj, struct attribute *attr,
                     const char *buf, size_t count)
 {
+       int cpu;
        struct cpufreq_cpu_sysinfo *freqobj;
-       struct cpufreq_policy *policy;
+       struct cpufreq_policy *policy = NULL;
        struct freq_attr *fattr = to_attr(attr);
        ssize_t ret = -EINVAL;
 
        freqobj = to_cpu_kobj(kobj);
        if (!freqobj->cpu_policy)
                goto no_policy;
+       else
+               cpu = freqobj->cpu_policy->cpu;
 
-       policy = freqobj->cpu_policy;
+       mutex_lock(&per_cpu(cpu_policy_hp_mutex, cpu));
 
-       policy = cpufreq_cpu_get(policy->cpu);
-       if (!policy)
+       if (!freqobj->cpu_policy) {
+               mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
                goto no_policy;
+       }
+
+       policy = cpufreq_cpu_get(freqobj->cpu_policy->cpu);
+       if (!policy) {
+               mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
+                       goto no_policy;
+       }
 
        if (lock_policy_rwsem_write(policy->cpu) < 0)
                goto fail;
@@ -684,6 +707,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
        unlock_policy_rwsem_write(policy->cpu);
 fail:
        cpufreq_cpu_put(policy);
+       mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
 no_policy:
        return ret;
 }
@@ -1932,18 +1956,25 @@ static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb,
                switch (action) {
                case CPU_ONLINE:
                case CPU_ONLINE_FROZEN:
+                       mutex_lock(&per_cpu(cpu_policy_hp_mutex, cpu));
                        cpufreq_add_dev(dev, NULL);
+                       mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
                        break;
                case CPU_DOWN_PREPARE:
                case CPU_DOWN_PREPARE_FROZEN:
+                       mutex_lock(&per_cpu(cpu_policy_hp_mutex, cpu));
                        if (unlikely(lock_policy_rwsem_write(cpu)))
                                BUG();
 
                        __cpufreq_remove_dev(dev, NULL);
+                       mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
                        break;
                case CPU_DOWN_FAILED:
                case CPU_DOWN_FAILED_FROZEN:
+                       mutex_lock(&per_cpu(cpu_policy_hp_mutex, cpu));
                        cpufreq_add_dev(dev, NULL);
+                       mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
+
                        break;
                }
        }
@@ -2088,14 +2119,19 @@ 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);
+               struct cpufreq_policy *policy;
+
+               mutex_lock(&per_cpu(cpu_policy_hp_mutex, cpu));
+               policy = cpufreq_cpu_get(cpu);
                if (policy) {
                        cpufreq_update_policy(policy->cpu);
                        cpufreq_cpu_put(policy);
                }
+               mutex_unlock(&per_cpu(cpu_policy_hp_mutex, cpu));
        }
        return NOTIFY_OK;
 }
@@ -2111,6 +2147,7 @@ static int __init cpufreq_core_init(void)
        for_each_possible_cpu(cpu) {
                per_cpu(cpufreq_policy_cpu, cpu) = -1;
                init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
+               mutex_init(&per_cpu(cpu_policy_hp_mutex, cpu));
        }
 
        cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);