Index: arch/i386/conf/GENERIC.MPACPI =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC.MPACPI,v retrieving revision 1.48 diff -d -p -u -r1.48 GENERIC.MPACPI --- arch/i386/conf/GENERIC.MPACPI 7 Oct 2005 15:59:49 -0000 1.48 +++ arch/i386/conf/GENERIC.MPACPI 4 Dec 2005 11:19:49 -0000 @@ -290,6 +290,7 @@ ioapic* at mainbus? options MPACPI # configure CPUs and APICs using ACPI # (acpi at mainbus must also be enabled) #options MPACPI_SCANPCI # find PCI roots using MPACPI +options LAPIC_TIMER_IS_BUGGERED # enable if kernel can't keep time acpi0 at mainbus0 Index: arch/x86/x86/lapic.c =================================================================== RCS file: /cvsroot/src/sys/arch/x86/x86/lapic.c,v retrieving revision 1.12 diff -d -p -u -r1.12 lapic.c --- arch/x86/x86/lapic.c 29 May 2005 21:36:40 -0000 1.12 +++ arch/x86/x86/lapic.c 4 Dec 2005 11:19:50 -0000 @@ -257,6 +257,85 @@ lapic_clockintr(void *arg, struct intrfr #endif cc_microset(ci); } +#ifdef LAPIC_TIMER_IS_BUGGERED + /* + * Some MP systems have been observed to not have a + * stable local APIC timer interrupt. We count the + * number of TSC cycles since the last call to + * lapic_clockintr(), and if it has been longer than + * expected we add in some extract time for hardclock() + * to add in when it computes the next value of the + * system "time" variable. Note that we don't skip + * time backwards - early arrivals to lapic_clockintr() + * have only been observed sporadically, and we'll + * soon catch up. + */ + if ( +#if defined(MULTIPROCESSOR) + CPU_IS_PRIMARY(ci) && +#endif + 1) { + /* Fix up broken APIC timers here too */ + uint64_t tsc; + int64_t extra_tsc; + static uint64_t last_tsc = 0; + static uint64_t tsc_per_hz = 0; + static int timer_calc_count = 0; + static int remaining_tsc = 0; + extern int fix_hardware_bugs_us; + + tsc = rdtsc(); + if (__predict_false(time.tv_sec == 0)) { + if (tsc_per_hz == 0) + tsc_per_hz = ci->ci_tsc_freq / hz; + /* do nothing else to while time settles... */ + } else if (__predict_false(timer_calc_count <= hz * 60)) { + uint64_t total_tsc; + static uint64_t start_tsc; + + /* + * For the next 60 seconds after we book, work + * out the lowest elapsed TSC value over a + * second, and use this as the best estimate of + * the CPU frequency. + * + * XXX - is first 60 secs long enough? + */ + if (timer_calc_count == 0) + start_tsc = tsc; + else if (timer_calc_count % hz == 0) { + total_tsc = tsc - start_tsc; + if (total_tsc < ci->ci_tsc_freq) { + ci->ci_tsc_freq = total_tsc; + tsc_per_hz = + ci->ci_tsc_freq / hz; + } + start_tsc = tsc; + } + timer_calc_count++; + } else { + /* we're initialised */ + extra_tsc = (tsc - last_tsc) - tsc_per_hz; + + /* Don't go backwards */ + if (extra_tsc > 0) { + /* + * XXX for the divides, use reciprocal + * multiplies? + */ + fix_hardware_bugs_us = + extra_tsc * 1000000 / + (ci->ci_tsc_freq); + remaining_tsc = extra_tsc - + (fix_hardware_bugs_us * + ci->ci_tsc_freq) / 1000000; + } else { + fix_hardware_bugs_us = 0; + } + } + last_tsc = tsc; + } +#endif /* LAPIC_TIMER_IS_BUGGERED */ } #endif Index: kern/kern_clock.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_clock.c,v retrieving revision 1.95 diff -d -p -u -r1.95 kern_clock.c --- kern/kern_clock.c 12 Sep 2005 16:21:31 -0000 1.95 +++ kern/kern_clock.c 4 Dec 2005 11:19:51 -0000 @@ -341,6 +341,12 @@ int shifthz; #endif /* + * Some hardware may not call hardclock() at a fixeded interval and may + * provide an external adjustment to help with timekeeping. + */ +int fix_hardware_bugs_us = 0; + +/* * We might want ldd to load the both words from time at once. * To succeed we need to be quadword aligned. * The sparc already does that, and that it has worked so far is a fluke. @@ -519,6 +525,7 @@ hardclock(struct clockframe *frame) */ hardclock_ticks++; delta = tick; + delta += fix_hardware_bugs_us; #ifndef NTP if (tickfix) {