]> git.hungrycats.org Git - linux/commitdiff
[PATCH] Time precision, adjtime(x) vs. gettimeofday
authorAndrew Morton <akpm@osdl.org>
Wed, 22 Oct 2003 01:19:33 +0000 (18:19 -0700)
committerLinus Torvalds <torvalds@home.osdl.org>
Wed, 22 Oct 2003 01:19:33 +0000 (18:19 -0700)
From: Stephen Hemminger <shemminger@osdl.org>

The following will prevent adjtime from causing time regression.  It delays
starting the adjtime mechanism for one tick, and keeps gettimeofday inside
the window.

Only fixes i386, but changes to other arch would be similar.

Running a simple clock test program and playing with adjtime demonstrates
that this fixes the problem (and 2.6.0-test6 is broken).  But given the
fragile nature of the timer code, it should go through some more testing
before inclusion.

arch/i386/kernel/time.c
include/linux/timex.h
kernel/time.c
kernel/timer.c

index a1a583b6bc04356efa170ba0a8260c13de9942dc..61b7b24f8915a5c2f174bbdc0c3e8b71ff5ab784 100644 (file)
@@ -104,6 +104,15 @@ void do_gettimeofday(struct timeval *tv)
                lost = jiffies - wall_jiffies;
                if (lost)
                        usec += lost * (1000000 / HZ);
+
+               /*
+                * If time_adjust is negative then NTP is slowing the clock
+                * so make sure not to go into next possible interval.
+                * Better to lose some accuracy than have time go backwards..
+                */
+               if (unlikely(time_adjust < 0) && usec > tickadj)
+                       usec = tickadj;
+
                sec = xtime.tv_sec;
                usec += (xtime.tv_nsec / 1000);
        } while (read_seqretry(&xtime_lock, seq));
index 2b1665d57ad08cba35119e448ea2fee0be123c0c..c9e92e016524ec1d90f3d4b13be1d09893d0d6ce 100644 (file)
@@ -302,6 +302,7 @@ extern long time_adj;               /* tick adjust (scaled 1 / HZ) */
 extern long time_reftime;      /* time at last adjustment (s) */
 
 extern long time_adjust;       /* The amount of adjtime left */
+extern long time_next_adjust;  /* Value for time_adjust at next tick */
 
 /* interface variables pps->timer interrupt */
 extern long pps_offset;                /* pps time offset (us) */
index 6279ee846ed557bc679da7c79cd6a1c4eb6bc5e9..33a6fe086304673e96a0af77418c5567f27b6cbb 100644 (file)
@@ -236,7 +236,7 @@ int do_adjtimex(struct timex *txc)
        result = time_state;    /* mostly `TIME_OK' */
 
        /* Save for later - semantics of adjtime is to return old value */
-       save_adjust = time_adjust;
+       save_adjust = time_next_adjust ? time_next_adjust : time_adjust;
 
 #if 0  /* STA_CLOCKERR is never set yet */
        time_status &= ~STA_CLOCKERR;           /* reset STA_CLOCKERR */
@@ -283,7 +283,8 @@ int do_adjtimex(struct timex *txc)
            if (txc->modes & ADJ_OFFSET) {      /* values checked earlier */
                if (txc->modes == ADJ_OFFSET_SINGLESHOT) {
                    /* adjtime() is independent from ntp_adjtime() */
-                   time_adjust = txc->offset;
+                   if ((time_next_adjust = txc->offset) == 0)
+                        time_adjust = 0;
                }
                else if ( time_status & (STA_PLL | STA_PPSTIME) ) {
                    ltemp = (time_status & (STA_PPSTIME | STA_PPSSIGNAL)) ==
index 1c513bd0b1e0e2d4b29deea4a66fd3b18f356d44..a8442b38659b9962810b520c7d77587f4a488ae1 100644 (file)
@@ -474,6 +474,7 @@ long time_freq = (((NSEC_PER_SEC + HZ/2) % HZ - HZ/2) << SHIFT_USEC) / NSEC_PER_
 long time_adj;                         /* tick adjust (scaled 1 / HZ)  */
 long time_reftime;                     /* time at last adjustment (s)  */
 long time_adjust;
+long time_next_adjust;
 
 /*
  * this routine handles the overflow of the microsecond field
@@ -654,6 +655,12 @@ static void update_wall_time_one_tick(void)
        }
        xtime.tv_nsec += delta_nsec;
        time_interpolator_update(delta_nsec);
+
+       /* Changes by adjtime() do not take effect till next tick. */
+       if (time_next_adjust != 0) {
+               time_adjust = time_next_adjust;
+               time_next_adjust = 0;
+       }
 }
 
 /*