[PATCH] adjust clock for lost ticks

A large number of lost ticks can cause an overadjustment of the clock.  To
compensate for this we look at the current error and the larger the error
already is the more careful we are at adjusting the error.  As small extra
fix reset the error when the clock is set.

Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Acked-by: john stultz <johnstul@us.ibm.com>
Cc: Uwe Bugla <uwe.bugla@gmx.de>
Cc: James Bottomley <James.Bottomley@SteelEye.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Roman Zippel 2006-07-10 04:44:32 -07:00 committed by Linus Torvalds
parent 32dd66fce3
commit e154ff3d2c

View file

@ -891,6 +891,7 @@ int do_settimeofday(struct timespec *tv)
set_normalized_timespec(&xtime, sec, nsec); set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
clock->error = 0;
ntp_clear(); ntp_clear();
write_sequnlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
@ -1008,52 +1009,52 @@ static int __init timekeeping_init_device(void)
device_initcall(timekeeping_init_device); device_initcall(timekeeping_init_device);
/* /*
* If the error is already larger, we look ahead another tick, * If the error is already larger, we look ahead even further
* to compensate for late or lost adjustments. * to compensate for late or lost adjustments.
*/ */
static __always_inline int clocksource_bigadjust(int sign, s64 error, s64 *interval, s64 *offset) static __always_inline int clocksource_bigadjust(s64 error, s64 *interval, s64 *offset)
{ {
int adj; s64 tick_error, i;
u32 look_ahead, adj;
s32 error2, mult;
/* /*
* As soon as the machine is synchronized to the external time * Use the current error value to determine how much to look ahead.
* source this should be the common case. * The larger the error the slower we adjust for it to avoid problems
* with losing too many ticks, otherwise we would overadjust and
* produce an even larger error. The smaller the adjustment the
* faster we try to adjust for it, as lost ticks can do less harm
* here. This is tuned so that an error of about 1 msec is adusted
* within about 1 sec (or 2^20 nsec in 2^SHIFT_HZ ticks).
*/ */
error >>= 2; error2 = clock->error >> (TICK_LENGTH_SHIFT + 22 - 2 * SHIFT_HZ);
if (likely(sign > 0 ? error <= *interval : error >= *interval)) error2 = abs(error2);
return sign; for (look_ahead = 0; error2 > 0; look_ahead++)
error2 >>= 2;
/* /*
* An extra look ahead dampens the effect of the current error, * Now calculate the error in (1 << look_ahead) ticks, but first
* which can grow quite large with continously late updates, as * remove the single look ahead already included in the error.
* it would dominate the adjustment value and can lead to
* oscillation.
*/ */
error += current_tick_length() >> (TICK_LENGTH_SHIFT - clock->shift + 1); tick_error = current_tick_length() >> (TICK_LENGTH_SHIFT - clock->shift + 1);
error -= clock->xtime_interval >> 1; tick_error -= clock->xtime_interval >> 1;
error = ((error - tick_error) >> look_ahead) + tick_error;
adj = 0; /* Finally calculate the adjustment shift value. */
while (1) { i = *interval;
error >>= 1; mult = 1;
if (sign > 0 ? error <= *interval : error >= *interval) if (error < 0) {
break; error = -error;
adj++; *interval = -*interval;
*offset = -*offset;
mult = -1;
} }
for (adj = 0; error > i; adj++)
/* error >>= 1;
* Add the current adjustments to the error and take the offset
* into account, the latter can cause the error to be hardly
* reduced at the next tick. Check the error again if there's
* room for another adjustment, thus further reducing the error
* which otherwise had to be corrected at the next update.
*/
error = (error << 1) - *interval + *offset;
if (sign > 0 ? error > *interval : error < *interval)
adj++;
*interval <<= adj; *interval <<= adj;
*offset <<= adj; *offset <<= adj;
return sign << adj; return mult << adj;
} }
/* /*
@ -1068,11 +1069,19 @@ static void clocksource_adjust(struct clocksource *clock, s64 offset)
error = clock->error >> (TICK_LENGTH_SHIFT - clock->shift - 1); error = clock->error >> (TICK_LENGTH_SHIFT - clock->shift - 1);
if (error > interval) { if (error > interval) {
adj = clocksource_bigadjust(1, error, &interval, &offset); error >>= 2;
if (likely(error <= interval))
adj = 1;
else
adj = clocksource_bigadjust(error, &interval, &offset);
} else if (error < -interval) { } else if (error < -interval) {
interval = -interval; error >>= 2;
offset = -offset; if (likely(error >= -interval)) {
adj = clocksource_bigadjust(-1, error, &interval, &offset); adj = -1;
interval = -interval;
offset = -offset;
} else
adj = clocksource_bigadjust(error, &interval, &offset);
} else } else
return; return;
@ -1129,7 +1138,7 @@ static void update_wall_time(void)
clocksource_adjust(clock, offset); clocksource_adjust(clock, offset);
/* store full nanoseconds into xtime */ /* store full nanoseconds into xtime */
xtime.tv_nsec = clock->xtime_nsec >> clock->shift; xtime.tv_nsec = (s64)clock->xtime_nsec >> clock->shift;
clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift; clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift;
/* check to see if there is a new clocksource to use */ /* check to see if there is a new clocksource to use */