timer-changes_A0
RFC 1/1 timer-changes_A0
Hey Alan,
I was looking over the changes you made in your -ac tree to the timer
code, and I was inspired to further go through and try to just clean up, rename, and just move around a bit of code in arch/i386/kernel/time.c.
Hopefully it should lead to more flexibility in adding different clocks to use for do_gettimeoffset. Although I do want to split it up more, where you have clock_tsc.c, clock_pit.c, clock_cyclone.c etc. Each one implementing init_clock_xxxx, mark_timeoffset_xxxx, and do_gettimeoffset_xxxx (possibly probing functions too?). But for now I just kept it all in time.c
Anyway, all of this is completely untested (i just made sure it compiled), but I just wanted some feedback as to weather I should keep on working in this direction, or hold off and try this for 2.5 instead.
thanks
-john
arch/i386/kernel/time.c +105 -75
@@ -11,11 +11,11 @@* fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 * precision CMOS clock update * 1996-05-03 Ingo Molnar -* fixed time warps in do_[slow|fast]_gettimeoffset() +* fixed time warps in do_gettimeoffset_[tsc|pit]() * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills * 1998-09-05 (Various) -* More robust do_fast_gettimeoffset() algorithm implemented +* More robust do_gettimeoffset_tsc() algorithm implemented * (works with APM, Cyrix 6x86MX and Centaur C6), * monotonic gettimeofday() with fast_get_timeoffset(), * drift-proof precision TSC calibration on boot
@@ -69,6 +69,7 @@/* Number of usecs that the last interrupt was delayed */ static int delay_at_last_interrupt; +static int use_tsc;static unsigned long last_tsc_low;/* lsb 32 bits of Time Stamp Counter */ /* Cached *multiplier* to convert TSC counts to microseconds.
@@ -76,14 +77,49 @@* Equal to 2^32 * (1 / (clocks per usec) ). * Initialized in time_init. */ -unsigned long fast_gettimeoffset_quotient; +unsigned long gettimeoffset_tsc_quotient;extern rwlock_t xtime_lock;extern unsigned long wall_jiffies; +#define TICK_SIZE tick +spinlock_t rtc_lock =SPIN_LOCK_UNLOCKED ; +spinlock_t i8253_lock =SPIN_LOCK_UNLOCKED ; +extern spinlock_t i8259A_lock; -static inline unsigned long do_fast_gettimeoffset (void ) +static inline void mark_timeoffset_tsc (void ) +{ +int count; + +/* +* It is important that these two operations happen almost at +* the same time. We do the RDTSC stuff first, since it's +* faster. To avoid any inconsistencies, we need interrupts +* disabled locally. +*/ + +/* +* Interrupts are just disabled locally since the timer irq +* has the SA_INTERRUPT flag set. -arca +*/ + +/* read Pentium cycle counter */ + +rdtscl (last_tsc_low); + +spin_lock (&i8253_lock); +outb_p (0x00 ,0x43 );/* latch the count ASAP */ + + count =inb_p (0x40 );/* read the latched count */ + count |=inb (0x40 ) <<8 ; +spin_unlock (&i8253_lock); + + count = ((LATCH -1 ) - count) *TICK_SIZE ; + delay_at_last_interrupt = (count +LATCH /2 ) /LATCH ; +} + +static inline unsigned long do_gettimeoffset_tsc (void ) { registerunsigned long eax, edx;
@@ -95,7 +131,7 @@ eax -= last_tsc_low;/* tsc_low delta */ /* -* Time offset = (tsc_low delta) * fast_gettimeoffset_quotient +* Time offset = (tsc_low delta) * gettimeoffset_tsc_quotient * = (tsc_low delta) * (usecs_per_clock) * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) *
@@ -105,20 +141,20 @@ __asm__("mull %2" :"=a" (eax),"=d" (edx) - :"rm" (fast_gettimeoffset_quotient), + :"rm" (gettimeoffset_tsc_quotient),"0" (eax));/* our adjusted time offset in microseconds */ return delay_at_last_interrupt + edx; } -#define TICK_SIZE tick -spinlock_t i8253_lock =SPIN_LOCK_UNLOCKED ; - -extern spinlock_t i8259A_lock;#ifndef CONFIG_X86_TSC +static inline void mark_timeoffset_pit (void ) +{ +} +/* This function must be called with interrupts disabled * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
@@ -152,7 +188,7 @@* comp.protocols.time.ntp! */ -static unsigned long do_slow_gettimeoffset (void ) +static unsigned long do_gettimeoffset_pit (void ) {int count;
@@ -236,7 +272,7 @@ count -=256 ;#else -printk ("do_slow_gettimeoffset(): hardware timer problem?\n" ); +printk ("do_gettimeoffset_pit(): hardware timer problem?\n" );#endif } }
@@ -251,14 +287,50 @@return count; } -static unsigned long (*do_gettimeoffset)(void ) = do_slow_gettimeoffset; - +static unsigned long (*do_gettimeoffset)(void ) = do_gettimeoffset_pit; +static void (*mark_timeoffset)(void ) = mark_timeoffset_pit;#else -#define do_gettimeoffset () do_fast_gettimeoffset() +#define do_gettimeoffset () do_gettimeoffset_tsc() +#define mark_timeoffset () mark_timeoffset_tsc() + +#endif +/*fwd dec for init_clock_tsc*/ +static unsigned long __init calibrate_tsc (void ); + +static inline void init_clock_tsc (void ) +{ +extern int x86_udelay_tsc; +unsigned long tsc_quotient =calibrate_tsc (); +if (tsc_quotient) { + gettimeoffset_tsc_quotient = tsc_quotient; + use_tsc =1 ; +/* +* We could be more selective here I suspect +* and just enable this for the next intel chips ? +*/ + x86_udelay_tsc =1 ; +#ifndef do_gettimeoffset + do_gettimeoffset = do_gettimeoffset_tsc;#endif +/* report CPU clock rate in Hz. +* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = +* clock/second. Our precision is about 100 ppm. +*/ + {unsigned long eax=0 , edx=1000 ; + __asm__("divl %2" + :"=a" (cpu_khz),"=d" (edx) + :"r" (tsc_quotient), +"0" (eax),"1" (edx)); +printk ("Detected %lu.%03lu MHz processor.\n" , cpu_khz /1000 , cpu_khz %1000 ); + } + + } +} + +/* * This version of gettimeofday has microsecond resolution * and better than microsecond precision on fast x86 machines with TSC.
@@ -396,7 +468,7 @@if (timer_ack) {/* * Subtle, when I/O APICs are used we have to ack timer IRQ -* manually to reset the IRR bit for do_slow_gettimeoffset(). +* manually to reset the IRR bit for do_gettimeoffset_pit(). * This will also deassert NMI lines for the watchdog if run * on an 82489DX-based system. */
@@ -458,7 +530,6 @@#endif } -static int use_tsc;/* * This is the same as the above, except we _also_ save the current
@@ -467,7 +538,6 @@*/ static void timer_interrupt (int irq,void *dev_id,struct pt_regs *regs) { -int count;/* * Here we are in the timer irq handler. We just have irqs locally
@@ -478,35 +548,8 @@*/ write_lock (&xtime_lock); -if (use_tsc) - { -/* -* It is important that these two operations happen almost at -* the same time. We do the RDTSC stuff first, since it's -* faster. To avoid any inconsistencies, we need interrupts -* disabled locally. -*/ - -/* -* Interrupts are just disabled locally since the timer irq -* has the SA_INTERRUPT flag set. -arca -*/ - -/* read Pentium cycle counter */ - -rdtscl (last_tsc_low); - -spin_lock (&i8253_lock); -outb_p (0x00 ,0x43 );/* latch the count ASAP */ - - count =inb_p (0x40 );/* read the latched count */ - count |=inb (0x40 ) <<8 ; -spin_unlock (&i8253_lock); - - count = ((LATCH -1 ) - count) *TICK_SIZE ; - delay_at_last_interrupt = (count +LATCH /2 ) /LATCH ; - } - +mark_timeoffset (); +do_timer_interrupt (irq,NULL , regs);write_unlock (&xtime_lock);
@@ -558,7 +601,7 @@static struct irqaction irq0 = { timer_interrupt,SA_INTERRUPT ,0 ,"timer" ,NULL ,NULL };/* ------ Calibrate the TSC ------- -* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). +* Return 2^32 * (1 / (TSC clocks per usec)) for do_gettimeoffset_tsc(). * Too much 64-bit arithmetic here to do this cleanly in C, and for * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2) * output busy loop as low as possible. We avoid reading the CTC registers
@@ -634,9 +677,16 @@return 0 ; } +enum clock_type {CLOCK_PIT ,CLOCK_TSC }; +enum clock_type select_clock (void ) +{ +if (cpu_has_tsc) +return CLOCK_TSC ; +return CLOCK_PIT ; +} +void __init time_init (void ) { -extern int x86_udelay_tsc; xtime.tv_sec =get_cmos_time (); xtime.tv_usec =0 ;
@@ -668,32 +718,12 @@dodgy_tsc (); -if (cpu_has_tsc) { -unsigned long tsc_quotient =calibrate_tsc (); -if (tsc_quotient) { - fast_gettimeoffset_quotient = tsc_quotient; - use_tsc =1 ; -/* -* We could be more selective here I suspect -* and just enable this for the next intel chips ? -*/ - x86_udelay_tsc =1 ; -#ifndef do_gettimeoffset - do_gettimeoffset = do_fast_gettimeoffset; -#endif - -/* report CPU clock rate in Hz. -* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = -* clock/second. Our precision is about 100 ppm. -*/ - {unsigned long eax=0 , edx=1000 ; - __asm__("divl %2" - :"=a" (cpu_khz),"=d" (edx) - :"r" (tsc_quotient), -"0" (eax),"1" (edx)); -printk ("Detected %lu.%03lu MHz processor.\n" , cpu_khz /1000 , cpu_khz %1000 ); - } - } +switch (select_clock ()){ +case CLOCK_TSC : +init_clock_tsc (); +break ; +case CLOCK_PIT : +break ; }#ifdef CONFIG_VISWS
Quoted 4 lines · john stultz
where you have
clock_tsc.c,clock_pit.c,clock_cyclone.cetc. Each one implementinginit_clock_xxxx,mark_timeoffset_xxxx, anddo_gettimeoffset_xxxx(possibly probing functions too?). But for now I just kept it all intime.c
Longer term a struct timer_ops would indeed be really clean. Splitting it makes sense
arch/i386/kernel/time.c line 686
Quoted 9 lines
}
arch/i386/kernel/time.c +7
-0+enum clock_type {CLOCK_PIT ,CLOCK_TSC }; +enum clock_type select_clock (void ) +{ +if (cpu_has_tsc) +return CLOCK_TSC ; +return CLOCK_PIT ; +}
I put this in setup.c in my case so I could also cover the VISWS more nicely, and also because of the 2.5 plans to do a nice architecture split
Pulling the other code out of the switch into functions is definitely good. I'll go apply that bit now