powernow regression in 8-STABLE

Jung-uk Kim jkim at FreeBSD.org
Wed Jul 20 23:29:12 UTC 2011


On Tuesday 19 July 2011 07:20 am, Callum Gibson wrote:
> Hi,
> I've just noticed and tracked down a regression in
> x86/cpufreq/powernow.c (on amd64) which was first mentioned here:
>
> http://lists.freebsd.org/pipermail/freebsd-current/2011-March/02350
>9.html
>
> although no followup seems to have occurred.

The above thread is irrelevant.  It was an Intel processor.

> Symptoms are that powerd stops working because the dev.cpu.0.freq
> OID is no longer gettable nor settable.
>
> This seems to have been caused by the following revision:
> http://svnweb.freebsd.org/base?view=revision&revision=222148
> which was in turn an MFC of r221102, so I guess the problem exists
> on -current as well, although I can't confirm that since I don't
> run it.
>
> Reverting the change fixes the problem and powerd will work again.
> Also other utilities, such as xacpim, work properly.
>
> I'm running a ML-40 Turion laptop (HP Compaq nx6125).

From your dmesg output, I see that the processor speed was not 
calibrated properly.  ML-40's max. core freq. is 2,200 MHz according 
to its specification but it was probed at 2,282 MHz, which is too 
high.  I think that's the problem.  Can you please try the attached 
patch?

Jung-uk Kim
-------------- next part --------------
Index: sys/kern/kern_cpu.c
===================================================================
--- sys/kern/kern_cpu.c	(revision 224231)
+++ sys/kern/kern_cpu.c	(working copy)
@@ -159,16 +159,21 @@ cpufreq_attach(device_t dev)
 	CF_MTX_INIT(&sc->lock);
 	sc->curr_level.total_set.freq = CPUFREQ_VAL_UNKNOWN;
 	SLIST_INIT(&sc->saved_freq);
-	/* Try to get nominal CPU freq to use it as maximum later if needed */
-	sc->max_mhz = cpu_get_nominal_mhz(dev);
-	/* If that fails, try to measure the current rate */
-	if (sc->max_mhz <= 0) {
-		pc = cpu_get_pcpu(dev);
-		if (cpu_est_clockrate(pc->pc_cpuid, &rate) == 0)
-			sc->max_mhz = rate / 1000000;
-		else
-			sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
+	if (sc->max_mhz == CPUFREQ_VAL_UNKNOWN) {
+		/* Try to get nominal CPU freq to use it as maximum later. */
+		sc->max_mhz = cpu_get_nominal_mhz(dev);
+		/* If that fails, try to measure the current rate */
+		if (sc->max_mhz <= 0) {
+			pc = cpu_get_pcpu(dev);
+			if (cpu_est_clockrate(pc->pc_cpuid, &rate) == 0)
+				sc->max_mhz = rate / 1000000;
+			else
+				sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
+		}
 	}
+	if (sc->max_mhz == CPUFREQ_VAL_UNKNOWN)
+		CF_DEBUG("unknown max frequency for %s\n",
+		    device_get_nameunit(dev));
 
 	/*
 	 * Only initialize one set of sysctls for all CPUs.  In the future,
@@ -1001,7 +1006,9 @@ int
 cpufreq_register(device_t dev)
 {
 	struct cpufreq_softc *sc;
+	struct cf_setting *sets;
 	device_t cf_dev, cpu_dev;
+	int error, max, set_count, type;
 
 	/* Add a sysctl to get each driver's settings separately. */
 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
@@ -1009,14 +1016,29 @@ cpufreq_register(device_t dev)
 	    OID_AUTO, "freq_settings", CTLTYPE_STRING | CTLFLAG_RD, dev, 0,
 	    cpufreq_settings_sysctl, "A", "CPU frequency driver settings");
 
+	/* Get settings from the device and find maximum if possible. */
+	max = CPUFREQ_VAL_UNKNOWN;
+	if (CPUFREQ_DRV_TYPE(dev, &type) == 0 &&
+	    (type & CPUFREQ_TYPE_MASK) == CPUFREQ_TYPE_ABSOLUTE) {
+		set_count = MAX_SETTINGS;
+		sets = malloc(set_count * sizeof(*sets), M_TEMP, M_NOWAIT);
+		if (sets != NULL) {
+			if (CPUFREQ_DRV_SETTINGS(dev, sets, &set_count) == 0 &&
+			    set_count > 0)
+				max = sets[0].freq;
+			free(sets, M_TEMP);
+		}
+	}
+
 	/*
 	 * Add only one cpufreq device to each CPU.  Currently, all CPUs
 	 * must offer the same levels and be switched at the same time.
 	 */
 	cpu_dev = device_get_parent(dev);
-	if ((cf_dev = device_find_child(cpu_dev, "cpufreq", -1))) {
+	cf_dev = device_find_child(cpu_dev, "cpufreq", -1);
+	if (cf_dev != NULL) {
 		sc = device_get_softc(cf_dev);
-		sc->max_mhz = CPUFREQ_VAL_UNKNOWN;
+		sc->max_mhz = max;
 		return (0);
 	}
 
@@ -1025,8 +1047,12 @@ cpufreq_register(device_t dev)
 	if (cf_dev == NULL)
 		return (ENOMEM);
 	device_quiet(cf_dev);
-
-	return (device_probe_and_attach(cf_dev));
+	error = device_probe(cf_dev);
+	if (error != 0)
+		return (error);
+	sc = device_get_softc(cf_dev);
+	sc->max_mhz = max;
+	return (device_attach(cf_dev));
 }
 
 int


More information about the freebsd-amd64 mailing list