question about use of x86 tsc in do_fast_gettimeoffset

Charles 'Buck' Krasic (krasic@cse.ogi.edu)
Tue, 2 Nov 1999 18:41:47 -0800


Hi.

I've been reading the code in arch/i386/kernel/time.c and I think I
see a what should be a bug in do_fast_gettimeoffset(), but I can not
trigger it. So I wonder what I'm missing...

The bug I think I see concerns the tsc_low_delta calculation during
the period of time when the low 32 bits of the x86 tsc are about to
wrap. It appears to me that if a timer_interrupt runs just before the
wrap, and then gettimeoffset runs just after the wrap, then the line
eax -= last_tsc_low will produce a value close to 2^32. So, all calls
to do_gettimeofday will warp forward in time (eg by about 10s on a
400MHz machine) for the duration of the tick.

However, I can not cause this to actually happen...

Why is it safe to compute this tsc_low_delta using only the low 32
bits of the tsc?

-- Buck

static inline unsigned long do_fast_gettimeoffset(void)
{
register unsigned long eax asm("ax");
register unsigned long edx asm("dx");

/* Read the Time Stamp Counter */

rdtsc(eax,edx);

/* .. relative to previous jiffy (32 bits is enough) */
eax -= last_tsc_low; /* tsc_low delta */

/*
* Time offset = (tsc_low delta) * fast_gettimeoffset_quotient
* = (tsc_low delta) * (usecs_per_clock)
* = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)
*
* Using a mull instead of a divl saves up to 31 clock cycles
* in the critical path.
*/

__asm__("mull %2"
:"=a" (eax), "=d" (edx)
:"g" (fast_gettimeoffset_quotient),
"0" (eax));

/* our adjusted time offset in microseconds */
return delay_at_last_interrupt + edx;
}

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
* disabled but we don't know if the timer_bh is running on the other
* CPU. We need to avoid to SMP race with it. NOTE: we don' t need
* the irq version of write_lock because as just said we have irq
* locally disabled. -arca
*/
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);

outb_p(0x00, 0x43); /* latch the count ASAP */

count = inb_p(0x40); /* read the latched count */
count |= inb(0x40) << 8;

count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH;
}

do_timer_interrupt(irq, NULL, regs);

write_unlock(&xtime_lock);

}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/