summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorsteven finney <Steven.Finney@palm.com>2011-05-02 11:29:17 -0700
committerSimone Willett <swillett@nvidia.com>2011-10-04 17:43:11 -0700
commit5ca12c21bf4bfc23099c80e5cd39d8e4c7fa1fbe (patch)
treee7f13a55dfa79363d5990ed42a4ee960073dc80b /drivers
parentb7aa37992043da5a6b49f53f14c9ae60d1e34d76 (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.c47
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,