Since i386 time handling is not overflow-safe, these are the adjustments needed for allowing debuggers to update time after having halted the system for perhaps extended periods of time. Signed-Off-By: Jan Beulich Index: 2.6.14-nlkd/arch/i386/kernel/timers/common.c =================================================================== --- 2.6.14-nlkd.orig/arch/i386/kernel/timers/common.c 2005-11-09 10:40:17.000000000 +0100 +++ 2.6.14-nlkd/arch/i386/kernel/timers/common.c 2005-11-04 16:19:33.000000000 +0100 @@ -149,7 +149,7 @@ unsigned long read_timer_tsc(void) /* calculate cpu_khz */ -void init_cpu_khz(void) +unsigned long init_cpu_khz(void) { if (cpu_has_tsc) { unsigned long tsc_quotient = calibrate_tsc(); @@ -166,7 +166,9 @@ void init_cpu_khz(void) printk("Detected %u.%03u MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); } + return tsc_quotient; } } + return 0; } Index: 2.6.14-nlkd/arch/i386/kernel/timers/timer_hpet.c =================================================================== --- 2.6.14-nlkd.orig/arch/i386/kernel/timers/timer_hpet.c 2005-11-09 10:40:17.000000000 +0100 +++ 2.6.14-nlkd/arch/i386/kernel/timers/timer_hpet.c 2005-11-04 16:19:33.000000000 +0100 @@ -99,7 +99,7 @@ static unsigned long get_offset_hpet(voi static void mark_offset_hpet(void) { unsigned long long this_offset, last_offset; - unsigned long offset; + unsigned long offset, lost_ticks = 0; write_seqlock(&monotonic_lock); last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; @@ -109,14 +109,34 @@ static void mark_offset_hpet(void) offset = hpet_readl(HPET_T0_CMP) - hpet_tick; else offset = hpet_readl(HPET_COUNTER); - if (unlikely(((offset - hpet_last) >= (2*hpet_tick)) && (hpet_last != 0))) { - int lost_ticks = ((offset - hpet_last) / hpet_tick) - 1; - jiffies_64 += lost_ticks; + this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; + if (unlikely(debugger_jiffies)) { + unsigned long long delta = this_offset - last_offset; + unsigned long lo, hi, lo1; + +#ifndef CONFIG_SMP + /* When the TSC gets reset during AP startup, the code below would + incorrectly think we lost a huge amount of ticks. */ + if (unlikely((long long)delta < 0)) + delta = this_offset; +#endif + ASM_MUL64_REG(hi /* dummy */, lo1, tsc_hpet_quotient, (unsigned long)delta); + ASM_MUL64_REG(lo, hi, tsc_hpet_quotient, (unsigned long)(delta >> 32)); + __asm__("addl %3, %1\n\t" + "adcl $0, %2" + : "=A" (delta) + : "a" (lo), "d" (hi), "rm" (lo1) + : "flags"); + do_div(delta, hpet_tick); + lost_ticks = delta; } + else if (unlikely(((offset - hpet_last) >= (2*hpet_tick)) && (hpet_last != 0))) + lost_ticks = ((offset - hpet_last) / hpet_tick); + if (lost_ticks) + jiffies_64 += lost_ticks - 1; hpet_last = offset; /* update the monotonic base value */ - this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; monotonic_base += cycles_2_ns(this_offset - last_offset); write_sequnlock(&monotonic_lock); } Index: 2.6.14-nlkd/arch/i386/kernel/timers/timer_pm.c =================================================================== --- 2.6.14-nlkd.orig/arch/i386/kernel/timers/timer_pm.c 2005-11-09 10:40:17.000000000 +0100 +++ 2.6.14-nlkd/arch/i386/kernel/timers/timer_pm.c 2005-11-04 16:19:33.000000000 +0100 @@ -42,6 +42,10 @@ static u32 offset_delay; static unsigned long long monotonic_base; static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED; +#ifndef debugger_jiffies +static unsigned long tsc_quotient; +static u64 tsc_offset; +#endif #define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */ @@ -127,6 +131,9 @@ pm_good: if (verify_pmtmr_rate() != 0) return -ENODEV; +#ifndef debugger_jiffies + tsc_quotient = +#endif init_cpu_khz(); return 0; } @@ -151,6 +158,11 @@ static inline u32 cyc2us(u32 cycles) static void mark_offset_pmtmr(void) { u32 lost, delta, last_offset; +#ifdef debugger_jiffies +# define delta64 delta +#else + u64 delta64, last_tsc_offset = tsc_offset; +#endif static int first_run = 1; last_offset = offset_tick; @@ -158,21 +170,60 @@ static void mark_offset_pmtmr(void) offset_tick = read_pmtmr(); - /* calculate tick interval */ - delta = (offset_tick - last_offset) & ACPI_PM_MASK; +#ifndef debugger_jiffies + rdtscll(tsc_offset); - /* convert to usecs */ - delta = cyc2us(delta); + if (likely(!debugger_jiffies)) { +#endif + + /* calculate tick interval */ + delta = (offset_tick - last_offset) & ACPI_PM_MASK; + + /* convert to usecs */ + delta = cyc2us(delta); + +#ifndef debugger_jiffies + delta64 = delta; + } + else { +# ifdef CONFIG_SMP + /* When the TSC gets reset during AP startup, the code below would + incorrectly think we lost a huge amount of ticks. */ + if (unlikely((long long)(tsc_offset - last_tsc_offset) < 0)) + delta64 = tsc_offset; + else +# endif + delta64 = tsc_offset - last_tsc_offset; + __asm__("mull %2" + : "=a" (lost /* just a dummy */), + "=d" (delta) + : "rm" (tsc_quotient), + "0" ((unsigned long)delta64)); + __asm__("mull %1" + : "=A" (delta64) + : "rm" (tsc_quotient), + "a" ((unsigned long)(delta64 >> 32))); + delta64 += delta; + delta = (u32)delta64 ? (u32)delta64 - 1 : (offset_delay = 0); + } +#endif /* update the monotonic base value */ monotonic_base += delta * NSEC_PER_USEC; write_sequnlock(&monotonic_lock); /* convert to ticks */ - delta += offset_delay; - lost = delta / (USEC_PER_SEC / HZ); - offset_delay = delta % (USEC_PER_SEC / HZ); - + if (likely(delta == delta64)) { + delta += offset_delay; + lost = delta / (USEC_PER_SEC / HZ); + offset_delay = delta % (USEC_PER_SEC / HZ); + } + else { + do_div(delta64, USEC_PER_SEC / HZ); + lost = delta64; + offset_delay = 0; + } +#undef delta64 /* compensate for lost ticks */ if (lost >= 2) Index: 2.6.14-nlkd/arch/i386/kernel/timers/timer_tsc.c =================================================================== --- 2.6.14-nlkd.orig/arch/i386/kernel/timers/timer_tsc.c 2005-11-09 10:40:17.000000000 +0100 +++ 2.6.14-nlkd/arch/i386/kernel/timers/timer_tsc.c 2005-11-04 16:19:33.000000000 +0100 @@ -38,11 +38,21 @@ int tsc_disable __devinitdata = 0; static int use_tsc; /* Number of usecs that the last interrupt was delayed */ -static int delay_at_last_interrupt; +static unsigned long delay_at_last_interrupt; static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ static unsigned long last_tsc_high; /* msb 32 bits of Time Stamp Counter */ static unsigned long long monotonic_base; +#ifdef CONFIG_HPET_TIMER +#ifdef CONFIG_DEBUG_KERNEL +static unsigned long tsc_hpet_quotient; /* convert tsc to hpet clks */ +#define TSC_HPET_QUOTIENT_PTR (&tsc_hpet_quotient) +#else +#define tsc_hpet_quotient 0 +#define TSC_HPET_QUOTIENT_PTR NULL +#endif +#endif + static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED; /* convert from cycles(64bits) => nanoseconds (64bits) @@ -170,7 +180,7 @@ static void delay_tsc(unsigned long loop static void mark_offset_tsc_hpet(void) { unsigned long long this_offset, last_offset; - unsigned long offset, temp, hpet_current; + unsigned long offset, temp, hpet_current, lost_ticks = 0; write_seqlock(&monotonic_lock); last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; @@ -190,15 +200,33 @@ static void mark_offset_tsc_hpet(void) rdtsc(last_tsc_low, last_tsc_high); /* lost tick compensation */ + this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; offset = hpet_readl(HPET_T0_CMP) - hpet_tick; - if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) { - int lost_ticks = (offset - hpet_last) / hpet_tick; - jiffies_64 += lost_ticks; + if (unlikely(debugger_jiffies)) { + unsigned long long delta = this_offset - last_offset; + + if (likely((long long)delta > 0)) { + unsigned long lo, hi, lo1; + unsigned long long full; + + ASM_MUL64_REG(temp, lo1, tsc_hpet_quotient, (unsigned long)delta); + ASM_MUL64_REG(lo, hi, tsc_hpet_quotient, (unsigned long)(delta >> 32)); + __asm__("addl %3, %1\n\t" + "adcl $0, %2" + : "=A" (full) + : "a" (lo), "d" (hi), "rm" (lo1) + : "flags"); + do_div(full, hpet_tick); + lost_ticks = full; + } } + else if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) + lost_ticks = (offset - hpet_last) / hpet_tick; + if (lost_ticks) + jiffies_64 += lost_ticks - 1; hpet_last = hpet_current; /* update the monotonic base value */ - this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; monotonic_base += cycles_2_ns(this_offset - last_offset); write_sequnlock(&monotonic_lock); @@ -342,7 +370,6 @@ EXPORT_SYMBOL(recalibrate_cpu_khz); static void mark_offset_tsc(void) { unsigned long lost,delay; - unsigned long delta = last_tsc_low; int count; int countmp; static int count1 = 0; @@ -400,25 +427,55 @@ static void mark_offset_tsc(void) } } + this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; + /* lost tick compensation */ - delta = last_tsc_low - delta; - { + if (likely(!debugger_jiffies)) { + unsigned long delta = last_tsc_low - (unsigned long)last_offset; register unsigned long eax, edx; + eax = delta; __asm__("mull %2" :"=a" (eax), "=d" (edx) :"rm" (fast_gettimeoffset_quotient), "0" (eax)); delta = edx; + delta += delay_at_last_interrupt; + lost = delta / (USEC_PER_SEC / HZ); + delay = delta % (USEC_PER_SEC / HZ); + } + else { + unsigned long long delta; + unsigned long delta_low; + +#ifdef CONFIG_SMP + /* When the TSC gets reset during AP startup, the code below would + incorrectly think we lost a huge amount of ticks. */ + if (unlikely((long long)(this_offset - last_offset) < 0)) + delta = this_offset; + else +#endif + delta = this_offset - last_offset; + + __asm__("mull %2" + : "=a" (lost /* just a dummy */), + "=d" (delta_low) + : "rm" (fast_gettimeoffset_quotient), + "0" ((unsigned long)delta)); + __asm__("mull %1" + : "=A" (delta) + : "rm" (fast_gettimeoffset_quotient), + "a" ((unsigned long)(delta >> 32))); + delta += delta_low; + delta += delay_at_last_interrupt; + delay = do_div(delta, USEC_PER_SEC / HZ); + lost = delta; } - delta += delay_at_last_interrupt; - lost = delta/(1000000/HZ); - delay = delta%(1000000/HZ); if (lost >= 2) { jiffies_64 += lost-1; /* sanity check to ensure we're not always losing ticks */ - if (lost_count++ > 100) { + if (!debugger_jiffies && lost_count++ > 100) { printk(KERN_WARNING "Losing too many ticks!\n"); printk(KERN_WARNING "TSC cannot be used as a timesource. \n"); printk(KERN_WARNING "Possible reasons for this are:\n"); @@ -435,7 +492,6 @@ static void mark_offset_tsc(void) } else lost_count = 0; /* update the monotonic base value */ - this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; monotonic_base += cycles_2_ns(this_offset - last_offset); write_sequnlock(&monotonic_lock); @@ -499,7 +555,7 @@ static int __init init_tsc(char* overrid if (is_hpet_enabled() && hpet_use_timer) { unsigned long result, remain; printk("Using TSC for gettimeofday\n"); - tsc_quotient = calibrate_tsc_hpet(NULL); + tsc_quotient = calibrate_tsc_hpet(TSC_HPET_QUOTIENT_PTR); timer_tsc.mark_offset = &mark_offset_tsc_hpet; /* * Math to calculate hpet to usec multiplier Index: 2.6.14-nlkd/include/asm-i386/timer.h =================================================================== --- 2.6.14-nlkd.orig/include/asm-i386/timer.h 2005-11-09 10:40:17.000000000 +0100 +++ 2.6.14-nlkd/include/asm-i386/timer.h 2005-11-04 16:19:34.000000000 +0100 @@ -57,7 +57,7 @@ extern struct init_timer_opts timer_cycl extern unsigned long calibrate_tsc(void); extern unsigned long read_timer_tsc(void); -extern void init_cpu_khz(void); +extern unsigned long init_cpu_khz(void); extern int recalibrate_cpu_khz(void); #ifdef CONFIG_HPET_TIMER extern struct init_timer_opts timer_hpet_init;