Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1874

kernel-2.6.18-238.el5.src.rpm

From: Prarit Bhargava <prarit@redhat.com>
Date: Fri, 15 Oct 2010 18:46:30 -0400
Subject: [misc] cpufreq: add missing cpufreq_cpu_put
Message-id: <4CB8A186.9050101@redhat.com>
Patchwork-id: 28768
O-Subject: Re: [RHEL5.6 BZ 643080 PATCH v2]: Add missing cpufreq_cpu_put() to
	cpufreq code
Bugzilla: 643080
RH-Acked-by: Don Dutile <ddutile@redhat.com>
RH-Acked-by: Vivek Goyal <vgoyal@redhat.com>

RHEL5 commit a8b7e45cecee43a921c34fc94f1d913f8a60b4e3, "[cpufreq] add
APERF/MPERF support for AMD processors" broke CPU Hotplug.  Bringing down
any CPU will lead to a system hang.

Originally I thought that this was because the cpu's frequency policy was
not valid during a hotplug event.  I also noted in the submit for v1 that the
reason this was likely was because of a broken get/put pair somewhere in the
code.

It turns out that I was half right.  A cpufreq_cpu_put() is missing but it
doesn't effect the validity of the policy code.

What happens with the introduction of the APERF/MPERF patch is that there
is now an imbalanced cpufreq_cpu_get() and cpufreq_cpu_put() pair.

When a cpu is removed __cpufreq_remove_dev() is called which does

        kobject_unregister(&data->kobj);

        kobject_put(&data->kobj);

        /* we need to make sure that the underlying kobj is actually
         * not referenced anymore by anybody before we proceed with
         * unloading.
         */
	dprintk("waiting for dropping of refcount\n");
        wait_for_completion(&data->kobj_unregister);
        dprintk("wait complete\n");

Because a cpufreq_cpu_put() is missing, the refcount never drops to zero and
we hang waiting for completion.

This patch moves the cpufreq_cpu_get() after the get_cpu() and adds in the
missing cpufreq_cpu_put().

Successfully tested by me on an Intel Nehalem system and an AMD Dinar system.

Resolves BZ 643080.

Signed-off-by: Jarod Wilson <jarod@redhat.com>

diff --git a/arch/i386/kernel/cpu/cpufreq/mperf.c b/arch/i386/kernel/cpu/cpufreq/mperf.c
index bbbebb3..dea33d5 100644
--- a/arch/i386/kernel/cpu/cpufreq/mperf.c
+++ b/arch/i386/kernel/cpu/cpufreq/mperf.c
@@ -35,7 +35,7 @@ unsigned int cpufreq_get_measured_perf(unsigned int cpu)
 	cpumask_t saved_mask;
 	unsigned int perf_percent;
 	unsigned int retval;
-	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+	struct cpufreq_policy *policy = NULL;
 
 	saved_mask = current->cpus_allowed;
 	set_cpus_allowed(current, cpumask_of_cpu(cpu));
@@ -45,6 +45,12 @@ unsigned int cpufreq_get_measured_perf(unsigned int cpu)
 		return 0;
 	}
 
+	policy = cpufreq_cpu_get(cpu);
+	if (unlikely(!policy)) {
+		put_cpu();
+		return 0;
+	}
+
 	rdmsr(MSR_IA32_APERF, aperf_cur.split.lo, aperf_cur.split.hi);
 	rdmsr(MSR_IA32_MPERF, mperf_cur.split.lo, mperf_cur.split.hi);
 
@@ -95,6 +101,7 @@ unsigned int cpufreq_get_measured_perf(unsigned int cpu)
 
 	retval = policy->cpuinfo.max_freq * perf_percent / 100;
 
+	cpufreq_cpu_put(policy);
 	put_cpu();
 	set_cpus_allowed(current, saved_mask);