[patch] bugfix in gettimeofday not monotone increasing [was Re: arca-57-against-2.1.131-ac11.diff.gz

Andrea Arcangeli (andrea@e-mind.com)
Wed, 16 Dec 1998 21:57:16 +0100 (CET)


On Wed, 16 Dec 1998, Stephen Donnelly wrote:

>It seems that the kernel time is not monotonically increasing in recent
>(i386) kernels.

[snip]

>See this URL for more details and graphs:
>
>http://atm.cs.waikato.ac.nz/~sfd/bug/

Reproduced and so fixed (at least on how I can reproduce it here). It
seems that somebody cutted out too much code from the old (<2.1.126)
gettimeofday().

The only important lines of the following patch are these:

+ if (lost_ticks)
+ tv->tv_usec += lost_ticks * (1000000/HZ);

all other are experimental (working fine so far) improvements from me done
now before to fix the bug ;)

Patch against linux-2.1.131-ac11 (the patch is just in arca-61 btw).

Index: linux/arch/i386/kernel/time.c
diff -u linux/arch/i386/kernel/time.c:1.1.3.1 linux/arch/i386/kernel/time.c:1.1.1.1.2.5
--- linux/arch/i386/kernel/time.c:1.1.3.1 Sat Dec 5 11:12:00 1998
+++ linux/arch/i386/kernel/time.c Wed Dec 16 21:55:20 1998
@@ -92,9 +92,9 @@
eax -= last_tsc_low; /* tsc_low delta */

/*
- * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient.
- * = (tsc_low delta) / (clocks_per_usec)
- * = (tsc_low delta) / (clocks_per_jiffy / usecs_per_jiffy)
+ * 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.
@@ -230,17 +230,18 @@
*/
void do_gettimeofday(struct timeval *tv)
{
- unsigned long flags;
+ extern volatile unsigned long lost_ticks;

- save_flags(flags);
- cli();
+ disable_irq(0);
*tv = xtime;
tv->tv_usec += do_gettimeoffset();
- if (tv->tv_usec >= 1000000) {
+ if (lost_ticks)
+ tv->tv_usec += lost_ticks * (1000000/HZ);
+ enable_irq(0);
+ while (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
- restore_flags(flags);
}

void do_settimeofday(struct timeval *tv)
@@ -254,7 +255,7 @@
*/
tv->tv_usec -= do_gettimeoffset();

- if (tv->tv_usec < 0) {
+ while (tv->tv_usec < 0) {
tv->tv_usec += 1000000;
tv->tv_sec--;
}
@@ -399,17 +400,20 @@
*/
static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- int count, flags, unused;
+ 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 disable interrupts locally.
+ * avoid any inconsistencies, we need interrupts disabled locally.
*/
+
+ /*
+ * Interrupts are just disabled locally since the timer irq has the
+ * SA_INTERRUPT flag set. -arca
+ */

- __save_flags(flags);
- __cli();
/* read Pentium cycle counter */
- __asm__("rdtsc" :"=a" (last_tsc_low), "=d" (unused):);
+ __asm__("rdtsc" : "=a" (last_tsc_low) : : "edx");

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

@@ -418,8 +422,7 @@

count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH;
- __restore_flags(flags);
-
+
timer_interrupt(irq, NULL, regs);
}

Tell me if it fix the problem for you too.

Andrea Arcangeli

-
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/