powernow regression in 8-STABLE

John Baldwin jhb at freebsd.org
Fri Jul 22 12:16:54 UTC 2011


On Thursday, July 21, 2011 5:43:10 pm Jeremy Chadwick wrote:
> On Fri, Jul 22, 2011 at 06:56:00AM +1000, Callum Gibson wrote:
> > On 21Jul11 12:07, Jung-uk Kim wrote:
> > }Can you please do "set debug.cpufreq.verbose=1" from loader prompt and 
> > }show me the dmesg output?  I want to see intial settings.  You can 
> > }reset it from command line with "sysctl debug.cpufreq.verbose=0" 
> > }later.
> > 
> > http://members.optusnet.com.au/callumgibson/boot_verboser.out
> > 
> > Also, as suggested by jhb@, with legacy usb disabled:
> > http://members.optusnet.com.au/callumgibson/boot_verboser_nousb.out
> > and dev.cpu.0.freq reappears! Spooky. Is that a solution or a workaround?
> > I noticed this disables usb keyboard support at the boot menus.
> 
> "Legacy USB" support is a horribly-named BIOS option.
> 
> What the option actually does, without getting into the technicalities,
> is make your USB keyboard work inside of environments where there is no
> USB driver.  The most commonly-used examples are bootloader/bootstraps
> and MS-DOS.
> 
> The BIOS itself (meaning the firmware/BIOS, not "the BIOS as in a piece
> of legacy technology") has a tiny USB stack in it.  That's how your
> USB keyboard is able to work (e.g. to press Del or F2 to get into the
> BIOS itself), and how you're able to boot from USB-attached devices.
> 
> You should keep "Legacy USB" enabled if you want your keyboard to work
> in bootloaders/bootstraps.  If enabling this feature breaks something
> else, that sounds like a bug in the vendor BIOS to me, and you should
> contact the vendor or motherboard manufacturer to inform them of the
> bug.

It's a known issue that the Legacy USB option can break our TSC calibration 
code causing an inflated value of the TSC frequency.  The issue is that the 
legacy USB code is often very dumb.  It is implemented via polling in SMI#.  
It appears that each interrupt from the ISA timer triggers an SMI# that polls 
the USB bus looking for any keyboards and checking for any pending keyboard 
events) that it can then use to trigger simulated PS/2 keyboard actions.

The problem is that we calibrate the TSC using this algorithm:

 - grab the TSC
 - spin on the ISA timer waiting for it to run for a second
 - grab the TSC

The issue is that the SMI# fires at the same time we want to be execuiting 
step 3, and step 3 is deferred while the SMI# handler runs.  As a result, the 
TSC delta ends up being "1 second + time of an SMI# to poll USB".  We have a 
hack fix for this at work that originally came from Attilio Rao.  This is a 
patch for it relative to 8.  It disables interrupt generation for the ISA 
timer while we calibrate the TSC (which disables the SMI# temporarily):

Index: x86/isa/clock.c
===================================================================
--- x86/isa/clock.c	(.../mirror/FreeBSD/stable/8/sys)	(revision 224114)
+++ x86/isa/clock.c	(.../stable/8/sys)	(revision 224114)
@@ -119,7 +119,7 @@
 
 static	unsigned i8254_get_timecount(struct timecounter *tc);
 static	unsigned i8254_simple_get_timecount(struct timecounter *tc);
-static	void	set_i8254_freq(u_int freq, int intr_freq);
+static	void	set_i8254_freq(u_int freq, int intr_freq, int freerun);
 
 static struct timecounter i8254_timecounter = {
 	i8254_get_timecount,	/* get_timecount */
@@ -447,15 +447,32 @@
 #endif
 }
 
+/*
+ * XXX: This is a gross hack to workaround USB legacy emulation.  For
+ * some systems, the USB legacy emulation is implemented by a periodic
+ * SMI# triggered by the i8254.  The resulting SMI# could cause the
+ * DELAY() to run too long resulting in the TSC being miscalibrated.
+ * The workaround is to disable i8254 interrupts while calibrating the
+ * TSC.
+ */
+void
+DELAY_TSCCAL(int n)
+{
+
+	set_i8254_freq(i8254_freq, hz, 1);
+	DELAY(n);
+	set_i8254_freq(i8254_freq, hz, 0);
+}
+
 static void
-set_i8254_freq(u_int freq, int intr_freq)
+set_i8254_freq(u_int freq, int intr_freq, int freerun)
 {
 	int new_i8254_real_max_count;
 
 	i8254_timecounter.tc_frequency = freq;
 	mtx_lock_spin(&clock_lock);
 	i8254_freq = freq;
-	if (using_lapic_timer != LAPIC_CLOCK_NONE)
+	if (using_lapic_timer != LAPIC_CLOCK_NONE || freerun)
 		new_i8254_real_max_count = 0x10000;
 	else
 		new_i8254_real_max_count = TIMER_DIV(intr_freq);
@@ -508,7 +525,7 @@
 {
 
 	mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE);
-	set_i8254_freq(i8254_freq, hz);
+	set_i8254_freq(i8254_freq, hz, 0);
 }
 
 void
@@ -517,7 +534,7 @@
 
 	atrtc_start();
 
-	set_i8254_freq(i8254_freq, hz);
+	set_i8254_freq(i8254_freq, hz, 0);
 	tc_init(&i8254_timecounter);
 
 	init_TSC();
@@ -565,7 +582,7 @@
 		i8254_timecounter.tc_get_timecount =
 		    i8254_simple_get_timecount;
 		i8254_timecounter.tc_counter_mask = 0xffff;
-		set_i8254_freq(i8254_freq, hz);
+		set_i8254_freq(i8254_freq, hz, 0);
 	}
 
 	/*
@@ -627,7 +644,7 @@
 	freq = i8254_freq;
 	error = sysctl_handle_int(oidp, &freq, 0, req);
 	if (error == 0 && req->newptr != NULL)
-		set_i8254_freq(freq, hz);
+		set_i8254_freq(freq, hz, 0);
 	return (error);
 }
 
Index: amd64/include/clock.h
===================================================================
--- amd64/include/clock.h	(.../mirror/FreeBSD/stable/8/sys)	(revision 
224114)
+++ amd64/include/clock.h	(.../stable/8/sys)	(revision 224114)
@@ -32,10 +70,11 @@
 /*
  * Driver to clock driver interface.
  */
 
 void	startrtclock(void);
 void	init_TSC(void);
 void	init_TSC_tc(void);
+void	DELAY_TSCCAL(int n);
 
 #define	HAS_TIMER_SPKR 1
 int	timer_spkr_acquire(void);
Index: amd64/amd64/tsc.c
===================================================================
--- amd64/amd64/tsc.c	(.../mirror/FreeBSD/stable/8/sys)	(revision 224114)
+++ amd64/amd64/tsc.c	(.../stable/8/sys)	(revision 224114)
@@ -87,7 +92,7 @@
 	        printf("Calibrating TSC clock ... ");
 
 	tscval[0] = rdtsc();
-	DELAY(1000000);
+	DELAY_TSCCAL(1000000);
 	tscval[1] = rdtsc();
 
 	tsc_freq = tscval[1] - tscval[0];

-- 
John Baldwin


More information about the freebsd-stable mailing list