linreg: Handle leap second gracefully.

When leap second is inserted/deleted to the reference time, adjust the
reference point as if the clock was stepped in the opposite direction to
keep the slope and error statistics valid and correct the offset
gracefully.

Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
master
Miroslav Lichvar 2014-06-20 16:32:50 +02:00 committed by Richard Cochran
parent bc84b88053
commit 97f72ceecc
1 changed files with 17 additions and 0 deletions

View File

@ -76,6 +76,8 @@ struct linreg_servo {
double update_interval; double update_interval;
/* Current ratio between remote and local frequency */ /* Current ratio between remote and local frequency */
double frequency_ratio; double frequency_ratio;
/* Upcoming leap second */
int leap;
}; };
static void linreg_destroy(struct servo *servo) static void linreg_destroy(struct servo *servo)
@ -305,6 +307,20 @@ static double linreg_rate_ratio(struct servo *servo)
return s->frequency_ratio; return s->frequency_ratio;
} }
static void linreg_leap(struct servo *servo, int leap)
{
struct linreg_servo *s = container_of(servo, struct linreg_servo, servo);
/*
* Move reference when leap second is applied to the reference
* time as if the clock was stepped in the opposite direction
*/
if (s->leap && !leap)
move_reference(s, 0, s->leap * 1000000000);
s->leap = leap;
}
struct servo *linreg_servo_create(int fadj) struct servo *linreg_servo_create(int fadj)
{ {
struct linreg_servo *s; struct linreg_servo *s;
@ -318,6 +334,7 @@ struct servo *linreg_servo_create(int fadj)
s->servo.sync_interval = linreg_sync_interval; s->servo.sync_interval = linreg_sync_interval;
s->servo.reset = linreg_reset; s->servo.reset = linreg_reset;
s->servo.rate_ratio = linreg_rate_ratio; s->servo.rate_ratio = linreg_rate_ratio;
s->servo.leap = linreg_leap;
s->clock_freq = -fadj; s->clock_freq = -fadj;
s->frequency_ratio = 1.0; s->frequency_ratio = 1.0;