diff -urNp x-ref/arch/x86_64/kernel/apic.c x/arch/x86_64/kernel/apic.c --- x-ref/arch/x86_64/kernel/apic.c Thu Sep 26 04:14:10 2002 +++ x/arch/x86_64/kernel/apic.c Fri Oct 4 05:39:08 2002 @@ -31,6 +31,8 @@ #include #include +extern spinlock_t i8253_lock; + /* Using APIC to generate smp_local_timer_interrupt? */ int using_apic_timer = 0; @@ -772,14 +774,18 @@ void setup_APIC_timer(void * data) while (hpet_readl(HPET_COUNTER) < trigger); } else { int c1, c2; + spin_lock(&i8253_lock); outb_p(0x00, 0x43); c2 = inb_p(0x40); c2 |= inb_p(0x40) << 8; + spin_unlock(&i8253_lock); do { c1 = c2; + spin_lock(&i8253_lock); outb_p(0x00, 0x43); c2 = inb_p(0x40); c2 |= inb_p(0x40) << 8; + spin_unlock(&i8253_lock); } while (c2 - c1 < 300); } diff -urNp x-ref/arch/x86_64/kernel/smpboot.c x/arch/x86_64/kernel/smpboot.c --- x-ref/arch/x86_64/kernel/smpboot.c Fri Oct 4 05:38:19 2002 +++ x/arch/x86_64/kernel/smpboot.c Fri Oct 4 05:39:08 2002 @@ -76,6 +76,8 @@ struct cpuinfo_x86 cpu_data[NR_CPUS] __c /* Set when the idlers are all forked */ int smp_threads_ready; +extern int last_tsc; + /* * Setup routine for controlling SMP activation * @@ -241,11 +243,15 @@ static void __init synchronize_tsc_bp (v atomic_inc(&tsc_count_start); rdtscll(tsc_values[smp_processor_id()]); + /* * We clear the TSC in the last loop: */ - if (i == NR_LOOPS-1) + + if (i == NR_LOOPS-1) { write_tsc(0, 0); + last_tsc = 0; + } /* * Wait for all APs to leave the synchronization point: diff -urNp x-ref/arch/x86_64/kernel/time.c x/arch/x86_64/kernel/time.c --- x-ref/arch/x86_64/kernel/time.c Thu Sep 26 04:14:10 2002 +++ x/arch/x86_64/kernel/time.c Fri Oct 4 05:41:45 2002 @@ -12,8 +12,6 @@ * */ -#define HPET_BIOS_SUPPORT_WORKING - #include #include #include @@ -26,6 +24,7 @@ extern rwlock_t xtime_lock; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; +spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; unsigned int cpu_khz; /* TSC clocks / usec, not used here */ unsigned long hpet_period; /* fsecs / HPET clock */ @@ -39,24 +38,45 @@ unsigned long __wall_jiffies __section_w struct timeval __xtime __section_xtime; struct timezone __sys_tz __section_sys_tz; +long last_tsc; +spinlock_t last_tsc_lock = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_SMP + +void safe_rdtscll(long *x) +{ + long temp; + unsigned long flags; + + spin_lock_irqsave(&last_tsc_lock, flags); + rdtscll(temp); + if (last_tsc - temp < 0L) last_tsc = temp; + *x = last_tsc; + spin_unlock_irqrestore(&last_tsc_lock, flags); +} + +static inline unsigned int __do_gettimeoffset(void) +{ + unsigned long t; + safe_rdtscll(&t); + return ((t - hpet.last_tsc) * hpet.tsc_quot) >> 32; +} + +#else +#define safe_rdtscll(x) rdtscll(*(x)) +#define __do_gettimeoffset do_gettimeoffset +#endif + /* * do_gettimeoffset() returns microseconds since last timer interrupt was - * triggered by hardware. A memory read of HPET is slower than a register read - * of TSC, but much more reliable. It's also synchronized to the timer - * interrupt. Note that do_gettimeoffset() may return more than hpet_tick, if a - * timer interrupt has happened already, but hpet.trigger wasn't updated yet. - * This is not a problem, because jiffies hasn't updated either. They are bound - * together by xtime_lock. + * triggered by hardware. */ -static spinlock_t time_offset_lock = SPIN_LOCK_UNLOCKED; -static unsigned long timeoffset = 0; - inline unsigned int do_gettimeoffset(void) { unsigned long t; rdtscll(t); - return (t - hpet.last_tsc) * (1000000L / HZ) / hpet.ticks + hpet.offset; + return ((t - hpet.last_tsc) * hpet.tsc_quot) >> 32; } /* @@ -67,20 +87,15 @@ inline unsigned int do_gettimeoffset(voi void do_gettimeofday(struct timeval *tv) { - unsigned long flags, t; + unsigned long flags; unsigned int sec, usec; read_lock_irqsave(&xtime_lock, flags); - spin_lock(&time_offset_lock); sec = xtime.tv_sec; - usec = xtime.tv_usec; + usec = xtime.tv_usec + + (jiffies - wall_jiffies) * tick + __do_gettimeoffset(); - t = (jiffies - wall_jiffies) * (1000000L / HZ) + do_gettimeoffset(); - if (t > timeoffset) timeoffset = t; - usec += timeoffset; - - spin_unlock(&time_offset_lock); read_unlock_irqrestore(&xtime_lock, flags); tv->tv_sec = sec + usec / 1000000; @@ -98,7 +113,7 @@ void do_settimeofday(struct timeval *tv) write_lock_irq(&xtime_lock); vxtime_lock(); - tv->tv_usec -= do_gettimeoffset() + + tv->tv_usec -= __do_gettimeoffset() + (jiffies - wall_jiffies) * tick; while (tv->tv_usec < 0) { @@ -201,15 +216,39 @@ static void timer_interrupt(int irq, voi vxtime_lock(); { - unsigned long t; + long tsc, offset; + int delay; + + if (hpet.address) { + + delay = hpet_readl(HPET_COUNTER) + hpet_tick - hpet_readl(HPET_T0_CMP); + + } else { - rdtscll(t); - hpet.offset = (t - hpet.last_tsc) * (1000000L / HZ) / hpet.ticks + hpet.offset - 1000000L / HZ; - if (hpet.offset >= 1000000L / HZ) - hpet.offset = 0; - hpet.ticks = min_t(long, max_t(long, (t - hpet.last_tsc) * (1000000L / HZ) / (1000000L / HZ - hpet.offset), - cpu_khz * 1000/HZ * 15 / 16), cpu_khz * 1000/HZ * 16 / 15); - hpet.last_tsc = t; + spin_lock(&i8253_lock); + outb_p(0x00, 0x43); + delay = inb_p(0x40); + delay |= inb(0x40) << 8; + spin_unlock(&i8253_lock); + delay = LATCH - 1 - delay; + } + + safe_rdtscll(&tsc); + + offset = (((tsc - hpet.last_tsc) * hpet.tsc_quot) >> 32) - tick; + + if (offset > tick) { + if (hpet_report_lost_ticks) + printk(KERN_WARNING "time.c: lost %ld ticks\n", offset / tick); + jiffies += offset / tick; + offset %= tick; + } + + hpet.last_tsc = tsc - hpet.quot * delay / hpet.tsc_quot; + + + if ((((tsc - hpet.last_tsc) * hpet.tsc_quot) >> 32) < offset) + hpet.last_tsc = tsc - ((offset << 32) / hpet.tsc_quot) - 1; } /* @@ -337,12 +376,10 @@ static unsigned int __init hpet_calibrat static unsigned int __init pit_calibrate_tsc(void) { unsigned long start, end; - unsigned long flags; outb((inb(0x61) & ~0x02) | 0x01, 0x61); - __save_flags(flags); - __cli(); + spin_lock_irq(&i8253_lock); outb(0xb0, 0x43); outb((1193182 / (1000 / 50)) & 0xff, 0x42); @@ -352,7 +389,7 @@ static unsigned int __init pit_calibrate while ((inb(0x61) & 0x20) == 0); rdtscll(end); - __restore_flags(flags); + spin_unlock_irq(&i8253_lock); return (end - start) / 50; } @@ -411,9 +448,11 @@ static int hpet_init(void) void __init pit_init(void) { + spin_lock_irq(&i8253_lock); outb_p(0x34, 0x43); /* binary, mode 2, LSB/MSB, ch 0 */ outb_p(LATCH & 0xff, 0x40); /* LSB */ outb_p(LATCH >> 8, 0x40); /* MSB */ + spin_unlock_irq(&i8253_lock); } int __init time_setup(char *str) @@ -428,18 +467,50 @@ extern void __init config_acpi_tables(vo void __init time_init(void) { + char *timename; + + config_acpi_tables(); + +#ifdef HPET_HACK_ENABLE_DANGEROUS + if (!hpet.address) { + printk(KERN_WARNING "time.c: WARNING: Enabling HPET base manually!\n"); + outl(0x800038a0, 0xcf8); + outl(0xff000001, 0xcfc); + outl(0x800038a0, 0xcf8); + hpet.address = inl(0xcfc) & 0xfffffffe; + printk(KERN_WARNING "time.c: WARNING: Enabled HPET at at %#lx.\n", hpet.address); + } +#endif + +#ifndef CONFIG_HPET_TIMER + hpet.address = 0; +#endif + + write_lock(&xtime_lock); xtime.tv_sec = get_cmos_time(); xtime.tv_usec = 0; + write_unlock(&xtime_lock); - printk(KERN_WARNING "time.c: HPET timer not found, precise timing unavailable.\n"); + if (!hpet_init()) { + hpet.hz = (1000000000000000L + hpet_period / 2) / hpet_period; + cpu_khz = hpet_calibrate_tsc(); + timename = "HPET"; + } else { pit_init(); - printk(KERN_INFO "time.c: Using 1.1931816 MHz PIT timer.\n"); - setup_irq(0, &irq0); + hpet.hz = 1193182; cpu_khz = pit_calibrate_tsc(); - printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", - cpu_khz / 1000, cpu_khz % 1000); - hpet.ticks = cpu_khz * (1000 / HZ); - rdtscll(hpet.last_tsc); + timename = "PIT"; + } + + hpet.quot = (1000000L << 32) / hpet.hz; + hpet.tsc_quot = (1000L << 32) / cpu_khz; + setup_irq(0, &irq0); + + printk(KERN_INFO "time.c: Using %ld.%06ld MHz %s timer.\n", + hpet.hz / 1000000, hpet.hz % 1000000, timename); + printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", + cpu_khz / 1000, cpu_khz % 1000); + } __setup("report_lost_ticks", time_setup); diff -urNp x-ref/arch/x86_64/kernel/vsyscall.c x/arch/x86_64/kernel/vsyscall.c --- x-ref/arch/x86_64/kernel/vsyscall.c Thu Sep 26 04:14:10 2002 +++ x/arch/x86_64/kernel/vsyscall.c Fri Oct 4 05:39:08 2002 @@ -50,7 +50,11 @@ long __vxtime_sequence[2] __section_vxtime_sequence; +#ifdef CONFIG_SMP #undef USE_VSYSCALL +#else +#define USE_VSYSCALL +#endif #ifdef USE_VSYSCALL @@ -67,7 +71,7 @@ static inline void do_vgettimeofday(stru sec = __xtime.tv_sec; usec = __xtime.tv_usec + (__jiffies - __wall_jiffies) * (1000000 / HZ) + - (t - __hpet.last_tsc) * (1000000 / HZ) / __hpet.ticks + __hpet.offset; + (((t - __hpet.last_tsc) * __hpet.tsc_quot) >> 32); rmb(); } while (sequence != __vxtime_sequence[0]); diff -urNp x-ref/include/asm-x86_64/vsyscall.h x/include/asm-x86_64/vsyscall.h --- x-ref/include/asm-x86_64/vsyscall.h Wed Oct 2 08:12:13 2002 +++ x/include/asm-x86_64/vsyscall.h Fri Oct 4 05:39:08 2002 @@ -23,13 +23,11 @@ enum vsyscall_num { #define __section_vxtime_sequence __attribute__ ((unused, __section__ (".vxtime_sequence"), aligned(16))) struct hpet_data { - long address; /* base address */ - unsigned long hz; /* HPET clocks / sec */ - int trigger; /* value at last interrupt */ - int last; - int offset; - unsigned long last_tsc; - long ticks; + long address; + long hz; + long last_tsc; + long quot; + long tsc_quot; }; #define hpet_readl(a) readl(fix_to_virt(FIX_HPET_BASE) + a)