aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorSrivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>2013-09-11 16:12:59 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-11 17:29:57 -0400
commit0d66b91ebff49841f607a3c079984c907c8a4199 (patch)
treef6902da36a77353a0034bf17d0120f1ae5e4dbd3 /drivers/cpufreq
parent6cdcdb793791f776ea9408581b1242b636d43b37 (diff)
cpufreq: Fix crash in cpufreq-stats during suspend/resume
Stephen Warren reported that the cpufreq-stats code hits a NULL pointer dereference during the second attempt to suspend a system. He also pin-pointed the problem to commit 5302c3f "cpufreq: Perform light-weight init/teardown during suspend/resume". That commit actually ensured that the cpufreq-stats table and the cpufreq-stats sysfs entries are *not* torn down (ie., not freed) during suspend/resume, which makes it all the more surprising. However, it turns out that the root-cause is not that we access an already freed memory, but that the reference to the allocated memory gets moved around and we lose track of that during resume, leading to the reported crash in a subsequent suspend attempt. In the suspend path, during CPU offline, the value of policy->cpu is updated by choosing one of the surviving CPUs in that policy, as long as there is atleast one CPU in that policy. And cpufreq_stats_update_policy_cpu() is invoked to update the reference to the stats structure by assigning it to the new CPU. However, in the resume path, during CPU online, we end up assigning a fresh CPU as the policy->cpu, without letting cpufreq-stats know about this. Thus the reference to the stats structure remains (incorrectly) associated with the old CPU. So, in a subsequent suspend attempt, during CPU offline, we end up accessing an incorrect location to get the stats structure, which eventually leads to the NULL pointer dereference. Fix this by letting cpufreq-stats know about the update of the policy->cpu during CPU online in the resume path. (Also, move the update_policy_cpu() function higher up in the file, so that __cpufreq_add_dev() can invoke it). Reported-and-tested-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/cpufreq.c37
1 files changed, 24 insertions, 13 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 5a64f66d36e0..62bdb955ea56 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -947,6 +947,18 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
947 kfree(policy); 947 kfree(policy);
948} 948}
949 949
950static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
951{
952 policy->last_cpu = policy->cpu;
953 policy->cpu = cpu;
954
955#ifdef CONFIG_CPU_FREQ_TABLE
956 cpufreq_frequency_table_update_policy_cpu(policy);
957#endif
958 blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
959 CPUFREQ_UPDATE_POLICY_CPU, policy);
960}
961
950static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, 962static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
951 bool frozen) 963 bool frozen)
952{ 964{
@@ -1000,7 +1012,18 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
1000 if (!policy) 1012 if (!policy)
1001 goto nomem_out; 1013 goto nomem_out;
1002 1014
1003 policy->cpu = cpu; 1015
1016 /*
1017 * In the resume path, since we restore a saved policy, the assignment
1018 * to policy->cpu is like an update of the existing policy, rather than
1019 * the creation of a brand new one. So we need to perform this update
1020 * by invoking update_policy_cpu().
1021 */
1022 if (frozen && cpu != policy->cpu)
1023 update_policy_cpu(policy, cpu);
1024 else
1025 policy->cpu = cpu;
1026
1004 policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 1027 policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
1005 cpumask_copy(policy->cpus, cpumask_of(cpu)); 1028 cpumask_copy(policy->cpus, cpumask_of(cpu));
1006 1029
@@ -1092,18 +1115,6 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
1092 return __cpufreq_add_dev(dev, sif, false); 1115 return __cpufreq_add_dev(dev, sif, false);
1093} 1116}
1094 1117
1095static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
1096{
1097 policy->last_cpu = policy->cpu;
1098 policy->cpu = cpu;
1099
1100#ifdef CONFIG_CPU_FREQ_TABLE
1101 cpufreq_frequency_table_update_policy_cpu(policy);
1102#endif
1103 blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
1104 CPUFREQ_UPDATE_POLICY_CPU, policy);
1105}
1106
1107static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, 1118static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy,
1108 unsigned int old_cpu, bool frozen) 1119 unsigned int old_cpu, bool frozen)
1109{ 1120{