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