diff options
author | steven finney <Steven.Finney@palm.com> | 2011-05-02 14:29:17 -0400 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2011-05-04 11:50:57 -0400 |
commit | 98586ed8b8878e10691203687e89a42fa3355300 (patch) | |
tree | 728275611557268583bac0232c90b928151e3c66 /drivers/cpufreq/cpufreq_stats.c | |
parent | 335dc3335ff692173bc766b248f7a97f3a23b30a (diff) |
[CPUFREQ] Fix memory leak in cpufreq_stat
When a CPU is taken offline in an SMP system, cpufreq_remove_dev()
nulls out the per-cpu policy before cpufreq_stats_free_table() can
make use of it. cpufreq_stats_free_table() then skips the
call to sysfs_remove_group(), leaving about 100 bytes of sysfs-related
memory unclaimed each time a CPU-removal occurs. Break up
cpu_stats_free_table into sysfs and table portions, and
call the sysfs portion early.
Signed-off-by: Steven Finney <steven.finney@palm.com>
Signed-off-by: Dave Jones <davej@redhat.com>
Cc: stable@kernel.org
Diffstat (limited to 'drivers/cpufreq/cpufreq_stats.c')
-rw-r--r-- | drivers/cpufreq/cpufreq_stats.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 00d73fc8e4e2..4f1b8de2c9f3 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c | |||
@@ -165,17 +165,27 @@ static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) | |||
165 | return -1; | 165 | return -1; |
166 | } | 166 | } |
167 | 167 | ||
168 | /* should be called late in the CPU removal sequence so that the stats | ||
169 | * memory is still available in case someone tries to use it. | ||
170 | */ | ||
168 | static void cpufreq_stats_free_table(unsigned int cpu) | 171 | static void cpufreq_stats_free_table(unsigned int cpu) |
169 | { | 172 | { |
170 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); | 173 | struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); |
171 | struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); | ||
172 | if (policy && policy->cpu == cpu) | ||
173 | sysfs_remove_group(&policy->kobj, &stats_attr_group); | ||
174 | if (stat) { | 174 | if (stat) { |
175 | kfree(stat->time_in_state); | 175 | kfree(stat->time_in_state); |
176 | kfree(stat); | 176 | kfree(stat); |
177 | } | 177 | } |
178 | per_cpu(cpufreq_stats_table, cpu) = NULL; | 178 | per_cpu(cpufreq_stats_table, cpu) = NULL; |
179 | } | ||
180 | |||
181 | /* must be called early in the CPU removal sequence (before | ||
182 | * cpufreq_remove_dev) so that policy is still valid. | ||
183 | */ | ||
184 | static void cpufreq_stats_free_sysfs(unsigned int cpu) | ||
185 | { | ||
186 | struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); | ||
187 | if (policy && policy->cpu == cpu) | ||
188 | sysfs_remove_group(&policy->kobj, &stats_attr_group); | ||
179 | if (policy) | 189 | if (policy) |
180 | cpufreq_cpu_put(policy); | 190 | cpufreq_cpu_put(policy); |
181 | } | 191 | } |
@@ -316,6 +326,9 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, | |||
316 | case CPU_ONLINE_FROZEN: | 326 | case CPU_ONLINE_FROZEN: |
317 | cpufreq_update_policy(cpu); | 327 | cpufreq_update_policy(cpu); |
318 | break; | 328 | break; |
329 | case CPU_DOWN_PREPARE: | ||
330 | cpufreq_stats_free_sysfs(cpu); | ||
331 | break; | ||
319 | case CPU_DEAD: | 332 | case CPU_DEAD: |
320 | case CPU_DEAD_FROZEN: | 333 | case CPU_DEAD_FROZEN: |
321 | cpufreq_stats_free_table(cpu); | 334 | cpufreq_stats_free_table(cpu); |
@@ -324,9 +337,11 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, | |||
324 | return NOTIFY_OK; | 337 | return NOTIFY_OK; |
325 | } | 338 | } |
326 | 339 | ||
340 | /* priority=1 so this will get called before cpufreq_remove_dev */ | ||
327 | static struct notifier_block cpufreq_stat_cpu_notifier __refdata = | 341 | static struct notifier_block cpufreq_stat_cpu_notifier __refdata = |
328 | { | 342 | { |
329 | .notifier_call = cpufreq_stat_cpu_callback, | 343 | .notifier_call = cpufreq_stat_cpu_callback, |
344 | .priority = 1, | ||
330 | }; | 345 | }; |
331 | 346 | ||
332 | static struct notifier_block notifier_policy_block = { | 347 | static struct notifier_block notifier_policy_block = { |