diff options
author | steven finney <Steven.Finney@palm.com> | 2011-05-02 11:29:17 -0700 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2011-10-04 17:43:11 -0700 |
commit | 5ca12c21bf4bfc23099c80e5cd39d8e4c7fa1fbe (patch) | |
tree | e7f13a55dfa79363d5990ed42a4ee960073dc80b /drivers | |
parent | b7aa37992043da5a6b49f53f14c9ae60d1e34d76 (diff) |
Fix memory leak in cpufreq_stat
commit 98586ed8b8878e10691203687e89a42fa3355300 upstream.
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.
Change-Id: I54fdd6fc38ad7e341083ffc3f56a65f288ec6f74
Signed-off-by: Steven Finney <steven.finney@palm.com>
Signed-off-by: Dave Jones <davej@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Reviewed-on: http://git-master/r/55851
Reviewed-by: Xin Xie <xxie@nvidia.com>
Tested-by: Xin Xie <xxie@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Venkata (Muni) Anda <vanda@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/cpufreq/cpufreq_stats.c | 47 |
1 files changed, 18 insertions, 29 deletions
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 8aebde517c22..6b9a5c61941a 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) return index - 1; /* below lowest freq in table: return -1 */ } +/* should be called late in the CPU removal sequence so that the stats + * memory is still available in case someone tries to use it. + */ static void cpufreq_stats_free_table(unsigned int cpu) { struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); - if (policy && policy->cpu == cpu) - sysfs_remove_group(&policy->kobj, &stats_attr_group); if (stat) { kfree(stat->time_in_state); kfree(stat); } per_cpu(cpufreq_stats_table, cpu) = NULL; +} + +/* must be called early in the CPU removal sequence (before + * cpufreq_remove_dev) so that policy is still valid. + */ +static void cpufreq_stats_free_sysfs(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + if (policy && policy->cpu == cpu) + sysfs_remove_group(&policy->kobj, &stats_attr_group); if (policy) cpufreq_cpu_put(policy); } @@ -311,27 +321,6 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, return 0; } -static int cpufreq_stats_create_table_cpu(unsigned int cpu) -{ - struct cpufreq_policy *policy; - struct cpufreq_frequency_table *table; - int ret = -ENODEV; - - policy = cpufreq_cpu_get(cpu); - if (!policy) - return -ENODEV; - - table = cpufreq_frequency_get_table(cpu); - if (!table) - goto out; - - ret = cpufreq_stats_create_table(policy, table); - -out: - cpufreq_cpu_put(policy); - return ret; -} - static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) @@ -344,17 +333,17 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, cpufreq_update_policy(cpu); break; case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - cpufreq_stats_free_table(cpu); + cpufreq_stats_free_sysfs(cpu); break; - case CPU_DOWN_FAILED: - case CPU_DOWN_FAILED_FROZEN: - cpufreq_stats_create_table_cpu(cpu); + case CPU_DEAD: + case CPU_DEAD_FROZEN: + cpufreq_stats_free_table(cpu); break; } return NOTIFY_OK; } +/* priority=1 so this will get called before cpufreq_remove_dev */ static struct notifier_block cpufreq_stat_cpu_notifier __refdata = { .notifier_call = cpufreq_stat_cpu_callback, |