Commit e154ff3d authored by Roman Zippel's avatar Roman Zippel Committed by Linus Torvalds

[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: default avatarRoman Zippel <zippel@linux-m68k.org>
Acked-by: default avatarjohn stultz <johnstul@us.ibm.com>
Cc: Uwe Bugla <uwe.bugla@gmx.de>
Cc: James Bottomley <James.Bottomley@SteelEye.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 32dd66fc
...@@ -891,6 +891,7 @@ int do_settimeofday(struct timespec *tv) ...@@ -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) ...@@ -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
error >>= 2; * produce an even larger error. The smaller the adjustment the
if (likely(sign > 0 ? error <= *interval : error >= *interval)) * faster we try to adjust for it, as lost ticks can do less harm
return sign; * 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).
*/
error2 = clock->error >> (TICK_LENGTH_SHIFT + 22 - 2 * SHIFT_HZ);
error2 = abs(error2);
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. tick_error = current_tick_length() >> (TICK_LENGTH_SHIFT - clock->shift + 1);
*/ tick_error -= clock->xtime_interval >> 1;
error += current_tick_length() >> (TICK_LENGTH_SHIFT - clock->shift + 1); error = ((error - tick_error) >> look_ahead) + tick_error;
error -= clock->xtime_interval >> 1;
/* Finally calculate the adjustment shift value. */
adj = 0; i = *interval;
while (1) { mult = 1;
error >>= 1; if (error < 0) {
if (sign > 0 ? error <= *interval : error >= *interval) error = -error;
break; *interval = -*interval;
adj++; *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) ...@@ -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) {
error >>= 2;
if (likely(error >= -interval)) {
adj = -1;
interval = -interval; interval = -interval;
offset = -offset; offset = -offset;
adj = clocksource_bigadjust(-1, error, &interval, &offset); } else
adj = clocksource_bigadjust(error, &interval, &offset);
} else } else
return; return;
...@@ -1129,7 +1138,7 @@ static void update_wall_time(void) ...@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment