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
_______________________________________________
freebsd-stable@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-stable
To unsubscribe, send any mail to "freebsd-stable-unsubscr...@freebsd.org"

Reply via email to