From: Stephen Hemminger 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. 25-akpm/arch/i386/kernel/time.c | 9 +++++++++ 25-akpm/include/linux/timex.h | 1 + 25-akpm/kernel/time.c | 5 +++-- 25-akpm/kernel/timer.c | 7 +++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff -puN arch/i386/kernel/time.c~ajdtimex-vs-gettimeofday arch/i386/kernel/time.c --- 25/arch/i386/kernel/time.c~ajdtimex-vs-gettimeofday Tue Oct 14 14:59:40 2003 +++ 25-akpm/arch/i386/kernel/time.c Tue Oct 14 14:59:40 2003 @@ -105,6 +105,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)); diff -puN include/linux/timex.h~ajdtimex-vs-gettimeofday include/linux/timex.h --- 25/include/linux/timex.h~ajdtimex-vs-gettimeofday Tue Oct 14 14:59:40 2003 +++ 25-akpm/include/linux/timex.h Tue Oct 14 14:59:40 2003 @@ -302,6 +302,7 @@ extern long time_adj; /* tick adjust (s 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) */ diff -puN kernel/time.c~ajdtimex-vs-gettimeofday kernel/time.c --- 25/kernel/time.c~ajdtimex-vs-gettimeofday Tue Oct 14 14:59:40 2003 +++ 25-akpm/kernel/time.c Tue Oct 14 14:59:40 2003 @@ -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)) == diff -puN kernel/timer.c~ajdtimex-vs-gettimeofday kernel/timer.c --- 25/kernel/timer.c~ajdtimex-vs-gettimeofday Tue Oct 14 14:59:40 2003 +++ 25-akpm/kernel/timer.c Tue Oct 14 14:59:40 2003 @@ -474,6 +474,7 @@ long time_freq = (((NSEC_PER_SEC + HZ/2) 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(vo } 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; + } } /* _