aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-07-29 18:32:00 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-11 21:35:24 -0400
commite9ef4410a75bea04e5f9e30c41fba0ebfa9b7559 (patch)
tree5112ac021251fea5587b8c09422d368534d6e147 /drivers/cpufreq
parent657e142082bec684725383eccd54e4ace5a2c293 (diff)
cpufreq: Fix cpufreq driver module refcount balance after suspend/resume
commit 2a99859932281ed6c2ecdd988855f8f6838f6743 upstream. Since cpufreq_cpu_put() called by __cpufreq_remove_dev() drops the driver module refcount, __cpufreq_remove_dev() causes that refcount to become negative for the cpufreq driver after a suspend/resume cycle. This is not the only bad thing that happens there, however, because kobject_put() should only be called for the policy kobject at this point if the CPU is not the last one for that policy. Namely, if the given CPU is the last one for that policy, the policy kobject's refcount should be 1 at this point, as set by cpufreq_add_dev_interface(), and only needs to be dropped once for the kobject to go away. This actually happens under the cpu == 1 check, so it need not be done before by cpufreq_cpu_put(). On the other hand, if the given CPU is not the last one for that policy, this means that cpufreq_add_policy_cpu() has been called at least once for that policy and cpufreq_cpu_get() has been called for it too. To balance that cpufreq_cpu_get(), we need to call cpufreq_cpu_put() in that case. Thus, to fix the described problem and keep the reference counters balanced in both cases, move the cpufreq_cpu_get() call in __cpufreq_remove_dev() to the code path executed only for CPUs that share the policy with other CPUs. Reported-and-tested-by: Toralf Förster <toralf.foerster@gmx.de> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/cpufreq.c19
1 files changed, 10 insertions, 9 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 178fe7a69056..648554742a99 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1075,14 +1075,11 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif
1075 __func__, cpu_dev->id, cpu); 1075 __func__, cpu_dev->id, cpu);
1076 } 1076 }
1077 1077
1078 if ((cpus == 1) && (cpufreq_driver->target))
1079 __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
1080
1081 pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
1082 cpufreq_cpu_put(data);
1083
1084 /* If cpu is last user of policy, free policy */ 1078 /* If cpu is last user of policy, free policy */
1085 if (cpus == 1) { 1079 if (cpus == 1) {
1080 if (cpufreq_driver->target)
1081 __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
1082
1086 lock_policy_rwsem_read(cpu); 1083 lock_policy_rwsem_read(cpu);
1087 kobj = &data->kobj; 1084 kobj = &data->kobj;
1088 cmp = &data->kobj_unregister; 1085 cmp = &data->kobj_unregister;
@@ -1103,9 +1100,13 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif
1103 free_cpumask_var(data->related_cpus); 1100 free_cpumask_var(data->related_cpus);
1104 free_cpumask_var(data->cpus); 1101 free_cpumask_var(data->cpus);
1105 kfree(data); 1102 kfree(data);
1106 } else if (cpufreq_driver->target) { 1103 } else {
1107 __cpufreq_governor(data, CPUFREQ_GOV_START); 1104 pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
1108 __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); 1105 cpufreq_cpu_put(data);
1106 if (cpufreq_driver->target) {
1107 __cpufreq_governor(data, CPUFREQ_GOV_START);
1108 __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
1109 }
1109 } 1110 }
1110 1111
1111 per_cpu(cpufreq_policy_cpu, cpu) = -1; 1112 per_cpu(cpufreq_policy_cpu, cpu) = -1;