Bug in 2.2.14 gettimeofday() on i386 (and patch to fix it)

From: Peter Chubb (peterc@aurema.com)
Date: Wed Feb 23 2000 - 01:31:14 EST


When xntpd (or similar) wants to slow down the system clock, it (effectively)
subtracts from xtime.tv_usecs the amount it wants to slow the clock
down by. If an application (e.g., squid, XF86, netscape, etc.) then
does a gettimeofday(), the time appears to have gone forward by around
an hour and a quarter (4294 seconds, to be precise). A couple of
seconds later, the time appears to go back to what it should have been.

This is because of the way that arch/i386/kernel/time.c works: it
grabs xtime.tv_usecs, assigns its contents to an unsigned long (thus
converting it to a very large number if it was negative) thn adjusts
the seconds value it'll return by the number of microseconds it
thinks it has.

Here's a program that detects the problem (although the main effect I
was seeing was that squid kept timing out immediately):

----
#include <time.h>
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
main()
{
        time_t last;
        time_t t;
        struct timeval tv;
        char buf[128];
        char buf1[128];

gettimeofday(&tv, NULL); last = tv.tv_sec; sleep(1); for(;;) { gettimeofday(&tv, NULL); t = tv.tv_sec; if (t > last + 2 || t < last) printf("%s -> %s (%lu -> %lu)\n", strcpy(buf, ctime(&last)), strcpy(buf1, ctime(&t)), last, t); last = t; sleep(1); } return 0; }

---- When I ran this with xntpd running, and a slightly fast running clock, I saw at about ten-minute intervals, the time apparently leap forwards by about one and a quarter hours, then back again after a couple of seconds.

Here's a patch that fixes it. There's a choice here --- I chose to keep tv_secs from different calls to gettimeofday to be monotonically increasing. by zeroing tv_usecs if it would otherwise be negative. The other alternative would be to correct secs down.

Here's the patch against 2.2.14:

--- linux/arch/i386/kernel/time.c-old Mon Nov 15 15:40:11 1999 +++ linux/arch/i386/kernel/time.c Wed Feb 23 14:00:44 2000 @@ -239,10 +239,10 @@ { extern volatile unsigned long lost_ticks; unsigned long flags; - unsigned long usec, sec; + long usec, sec; read_lock_irqsave(&xtime_lock, flags); - usec = do_gettimeoffset(); + usec = (long)do_gettimeoffset(); { unsigned long lost = lost_ticks; if (lost) @@ -252,6 +252,11 @@ usec += xtime.tv_usec; read_unlock_irqrestore(&xtime_lock, flags); + if (usec < 0) { +/* printk(KERN_WARNING "xtime/gettimeoffset race usec = %ld\n", usec); */ + usec = 0; + } + while (usec >= 1000000) { usec -= 1000000; sec++;

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



This archive was generated by hypermail 2b29 : Wed Feb 23 2000 - 21:00:32 EST