--- isa/clock.c@@/main/RELENG_6/1 Mon Jul 18 19:52:04 2005 +++ isa/clock.c Tue Jun 10 16:21:41 2008 @@ -73,6 +73,9 @@ #include #include #include +#if defined(SMP) +#include +#endif #ifdef DEV_APIC #include #endif @@ -124,6 +127,8 @@ static int (*i8254_pending)(struct intsrc *); static int i8254_ticked; static int using_lapic_timer; +extern int lapic_statclock; +static int using_lapic_statclock; static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; static u_char rtc_statusb = RTCSB_24HR; @@ -137,7 +142,7 @@ static unsigned i8254_get_timecount(struct timecounter *tc); static unsigned i8254_simple_get_timecount(struct timecounter *tc); -static void set_timer_freq(u_int freq, int intr_freq); +static void set_timer_freq(u_int freq, int intr_freq, int freerun); static struct timecounter i8254_timecounter = { i8254_get_timecount, /* get_timecount */ @@ -234,8 +239,13 @@ pscnt = psdiv; profclock(frame); } - if (pscnt == psdiv) + if (pscnt == psdiv) { statclock(frame); +#ifdef SMP + if (!using_lapic_statclock) + forward_statclock(); +#endif + } } } @@ -298,7 +308,7 @@ * early for console i/o. */ if (timer0_max_count == 0) - set_timer_freq(timer_freq, hz); + set_timer_freq(timer_freq, hz, 0); /* * Read the counter first, so that the rest of the setup overhead is @@ -378,6 +388,20 @@ #endif } +/* + * XXX This is a gross hack for reqst00084792. The tsc clock frequency was + * being miscalibrated by about 0.1%, due to some issue with a new BIOS version + * from SuperMicro. To avoid the issue we let the i8254 counter free run so + * that we're virtually guaranteed not to miss a counter rollover. + */ +void +DELAY_TSCCAL(int n) +{ + set_timer_freq(timer_freq, hz, 1); + DELAY(n); + set_timer_freq(timer_freq, hz, 0); +} + static void sysbeepstop(void *chan) { @@ -429,8 +453,8 @@ return (val); } -static __inline void -writertc(u_char reg, u_char val) +void +writertc(int reg, u_char val) { RTC_LOCK; @@ -525,18 +549,19 @@ if (bootverbose) printf("failed, using default i8254 clock of %u Hz\n", timer_freq); + return (timer_freq); } static void -set_timer_freq(u_int freq, int intr_freq) +set_timer_freq(u_int freq, int intr_freq, int freerun) { int new_timer0_real_max_count; i8254_timecounter.tc_frequency = freq; mtx_lock_spin(&clock_lock); timer_freq = freq; - if (using_lapic_timer) + if (using_lapic_timer || freerun) new_timer0_real_max_count = 0x10000; else new_timer0_real_max_count = TIMER_DIV(intr_freq); @@ -603,7 +628,7 @@ writertc(RTC_STATUSA, rtc_statusa); writertc(RTC_STATUSB, RTCSB_24HR); - set_timer_freq(timer_freq, hz); + set_timer_freq(timer_freq, hz, 0); freq = calibrate_clocks(); #ifdef CLK_CALIBRATION_LOOP if (bootverbose) { @@ -635,7 +660,7 @@ freq, timer_freq); } - set_timer_freq(timer_freq, hz); + set_timer_freq(timer_freq, hz, 0); tc_init(&i8254_timecounter); init_TSC(); @@ -786,6 +811,7 @@ #ifdef DEV_APIC using_lapic_timer = lapic_setup_clock(); + using_lapic_statclock = using_lapic_timer && lapic_statclock; #endif /* * If we aren't using the local APIC timer to drive the kernel @@ -804,7 +830,7 @@ i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount; i8254_timecounter.tc_counter_mask = 0xffff; - set_timer_freq(timer_freq, hz); + set_timer_freq(timer_freq, hz, 0); } /* Initialize RTC. */ @@ -817,7 +843,7 @@ * kernel clocks, then setup the RTC to periodically interrupt to * drive statclock() and profclock(). */ - if (!statclock_disable && !using_lapic_timer) { + if (!statclock_disable && !using_lapic_statclock) { diag = rtcin(RTC_DIAG); if (diag != 0) printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS); @@ -842,7 +868,7 @@ cpu_startprofclock(void) { - if (using_lapic_timer) + if (using_lapic_statclock) return; rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF; writertc(RTC_STATUSA, rtc_statusa); @@ -853,7 +879,7 @@ cpu_stopprofclock(void) { - if (using_lapic_timer) + if (using_lapic_statclock) return; rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; writertc(RTC_STATUSA, rtc_statusa); @@ -873,7 +899,7 @@ freq = timer_freq; error = sysctl_handle_int(oidp, &freq, sizeof(freq), req); if (error == 0 && req->newptr != NULL) - set_timer_freq(freq, hz); + set_timer_freq(freq, hz, 0); return (error); } --- i386/local_apic.c@@/main/RELENG_6/6 Fri Mar 10 19:37:33 2006 +++ i386/local_apic.c Fri Mar 9 17:24:03 2007 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -156,6 +157,11 @@ static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); +int lapic_statclock = 0; +TUNABLE_INT("machdep.lapic_statclock", &lapic_statclock); +SYSCTL_INT(_machdep, OID_AUTO, lapic_statclock, CTLFLAG_RD, + &lapic_statclock, 0, "Use local apic for statclock"); + static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { @@ -362,9 +368,12 @@ * both of the other timers with similarly small but relatively * prime divisors. */ - lapic_timer_hz = hz * LAPIC_TIMER_HZ_DIVIDER; - stathz = lapic_timer_hz / LAPIC_TIMER_STATHZ_DIVIDER; - profhz = lapic_timer_hz / LAPIC_TIMER_PROFHZ_DIVIDER; + if (lapic_statclock) { + lapic_timer_hz = hz * LAPIC_TIMER_HZ_DIVIDER; + stathz = lapic_timer_hz / LAPIC_TIMER_STATHZ_DIVIDER; + profhz = lapic_timer_hz / LAPIC_TIMER_PROFHZ_DIVIDER; + } else + lapic_timer_hz = hz; lapic_timer_period = value / lapic_timer_hz; /* @@ -616,28 +625,35 @@ critical_enter(); /* Fire hardclock at hz. */ - la->la_hard_ticks += hz; - if (la->la_hard_ticks >= lapic_timer_hz) { - la->la_hard_ticks -= lapic_timer_hz; + if (lapic_statclock) { + la->la_hard_ticks += hz; + if (la->la_hard_ticks >= lapic_timer_hz) { + la->la_hard_ticks -= lapic_timer_hz; + if (PCPU_GET(cpuid) == 0) + hardclock(&frame); + else + hardclock_process(&frame); + } + + /* Fire statclock at stathz. */ + la->la_stat_ticks += stathz; + if (la->la_stat_ticks >= lapic_timer_hz) { + la->la_stat_ticks -= lapic_timer_hz; + statclock(&frame); + } + + /* Fire profclock at profhz, but only when needed. */ + la->la_prof_ticks += profhz; + if (la->la_prof_ticks >= lapic_timer_hz) { + la->la_prof_ticks -= lapic_timer_hz; + if (profprocs != 0) + profclock(&frame); + } + } else { if (PCPU_GET(cpuid) == 0) hardclock(&frame); else hardclock_process(&frame); - } - - /* Fire statclock at stathz. */ - la->la_stat_ticks += stathz; - if (la->la_stat_ticks >= lapic_timer_hz) { - la->la_stat_ticks -= lapic_timer_hz; - statclock(&frame); - } - - /* Fire profclock at profhz, but only when needed. */ - la->la_prof_ticks += profhz; - if (la->la_prof_ticks >= lapic_timer_hz) { - la->la_prof_ticks -= lapic_timer_hz; - if (profprocs != 0) - profclock(&frame); } critical_exit(); }