From: John Stultz Accumulate time in update_wall_time() exponentially. This avoids long running loops seen with the dynticks patch as well as the problematic hang seen on systems with broken clocksources. NOTE: this only has relevance on dyntick kernels, so the quality of NTP updates on jiffies-tick systems is unaffected. (non-dyntick kernels call update_wall_time() in every timer tick) Signed-off-by: John Stultz Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner -- kernel/timer.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) Index: linux-2.6.18-mm2/kernel/timer.c =================================================================== --- linux-2.6.18-mm2.orig/kernel/timer.c 2006-09-30 01:41:14.000000000 +0200 +++ linux-2.6.18-mm2/kernel/timer.c 2006-09-30 01:41:14.000000000 +0200 @@ -907,6 +907,7 @@ static void clocksource_adjust(struct cl static void update_wall_time(void) { cycle_t offset; + int shift = 0; /* Make sure we're fully resumed: */ if (unlikely(timekeeping_suspended)) @@ -919,28 +920,39 @@ static void update_wall_time(void) #endif clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift; + while (offset > clock->cycle_interval << (shift + 1)) + shift++; + /* normally this loop will run just once, however in the * case of lost or late ticks, it will accumulate correctly. */ while (offset >= clock->cycle_interval) { + if (offset < (clock->cycle_interval << shift)) { + shift--; + continue; + } + /* accumulate one interval */ - clock->xtime_nsec += clock->xtime_interval; - clock->cycle_last += clock->cycle_interval; - offset -= clock->cycle_interval; + clock->xtime_nsec += clock->xtime_interval << shift; + clock->cycle_last += clock->cycle_interval << shift; + offset -= clock->cycle_interval << shift; - if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) { + while (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) { clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift; xtime.tv_sec++; second_overflow(); } /* interpolator bits */ - time_interpolator_update(clock->xtime_interval - >> clock->shift); + time_interpolator_update((clock->xtime_interval + >> clock->shift)<error += current_tick_length(); - clock->error -= clock->xtime_interval << (TICK_LENGTH_SHIFT - clock->shift); + clock->error += current_tick_length() << shift; + clock->error -= (clock->xtime_interval + << (TICK_LENGTH_SHIFT - clock->shift))<