ptp4l: Handle leap seconds.
Extend the clock_utc_correct function to handle leap seconds announced in the time properties data set. With software time stamping, it sets the STA_INS/STA_DEL bit for the system clock. Clock updates are suspended in the last second of the day. Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>master
parent
09667479b8
commit
e21af97091
73
clock.c
73
clock.c
|
@ -78,6 +78,8 @@ struct clock {
|
||||||
int free_running;
|
int free_running;
|
||||||
int freq_est_interval;
|
int freq_est_interval;
|
||||||
int utc_timescale;
|
int utc_timescale;
|
||||||
|
int leap_set;
|
||||||
|
enum servo_state servo_state;
|
||||||
tmv_t master_offset;
|
tmv_t master_offset;
|
||||||
tmv_t path_delay;
|
tmv_t path_delay;
|
||||||
struct mave *avg_delay;
|
struct mave *avg_delay;
|
||||||
|
@ -449,23 +451,66 @@ static void clock_update_slave(struct clock *c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clock_utc_correct(struct clock *c)
|
static int clock_utc_correct(struct clock *c, tmv_t ingress)
|
||||||
{
|
{
|
||||||
struct timespec offset;
|
struct timespec offset;
|
||||||
|
int utc_offset, leap, clock_leap;
|
||||||
|
uint64_t ts;
|
||||||
|
|
||||||
if (!c->utc_timescale)
|
if (!c->utc_timescale)
|
||||||
return;
|
return 0;
|
||||||
if (!(c->tds.flags & PTP_TIMESCALE))
|
|
||||||
return;
|
|
||||||
if (c->tds.flags & UTC_OFF_VALID && c->tds.flags & TIME_TRACEABLE) {
|
if (c->tds.flags & UTC_OFF_VALID && c->tds.flags & TIME_TRACEABLE) {
|
||||||
offset.tv_sec = c->tds.currentUtcOffset;
|
utc_offset = c->tds.currentUtcOffset;
|
||||||
} else if (c->tds.currentUtcOffset > CURRENT_UTC_OFFSET) {
|
} else if (c->tds.currentUtcOffset > CURRENT_UTC_OFFSET) {
|
||||||
offset.tv_sec = c->tds.currentUtcOffset;
|
utc_offset = c->tds.currentUtcOffset;
|
||||||
} else {
|
} else {
|
||||||
offset.tv_sec = CURRENT_UTC_OFFSET;
|
utc_offset = CURRENT_UTC_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c->tds.flags & LEAP_61) {
|
||||||
|
leap = 1;
|
||||||
|
} else if (c->tds.flags & LEAP_59) {
|
||||||
|
leap = -1;
|
||||||
|
} else {
|
||||||
|
leap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle leap seconds. */
|
||||||
|
if ((leap || c->leap_set) && c->clkid == CLOCK_REALTIME) {
|
||||||
|
/* If the clock will be stepped, the time stamp has to be the
|
||||||
|
target time. Ignore possible 1 second error in utc_offset. */
|
||||||
|
if (c->servo_state == SERVO_UNLOCKED) {
|
||||||
|
ts = tmv_to_nanoseconds(tmv_sub(ingress,
|
||||||
|
c->master_offset));
|
||||||
|
if (c->tds.flags & PTP_TIMESCALE)
|
||||||
|
ts -= utc_offset * NS_PER_SEC;
|
||||||
|
} else {
|
||||||
|
ts = tmv_to_nanoseconds(ingress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suspend clock updates in the last second before midnight. */
|
||||||
|
if (is_utc_ambiguous(ts)) {
|
||||||
|
pr_info("clock update suspended due to leap second");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
clock_leap = leap_second_status(ts, c->leap_set,
|
||||||
|
&leap, &utc_offset);
|
||||||
|
if (c->leap_set != clock_leap) {
|
||||||
|
clockadj_set_leap(c->clkid, clock_leap);
|
||||||
|
c->leap_set = clock_leap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(c->tds.flags & PTP_TIMESCALE))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
offset.tv_sec = utc_offset;
|
||||||
offset.tv_nsec = 0;
|
offset.tv_nsec = 0;
|
||||||
/* Local clock is UTC, but master is TAI. */
|
/* Local clock is UTC, but master is TAI. */
|
||||||
c->master_offset = tmv_add(c->master_offset, timespec_to_tmv(offset));
|
c->master_offset = tmv_add(c->master_offset, timespec_to_tmv(offset));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int forwarding(struct clock *c, struct port *p)
|
static int forwarding(struct clock *c, struct port *p)
|
||||||
|
@ -534,7 +579,10 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||||
c->clkid = CLOCK_REALTIME;
|
c->clkid = CLOCK_REALTIME;
|
||||||
c->utc_timescale = 1;
|
c->utc_timescale = 1;
|
||||||
max_adj = 512000;
|
max_adj = 512000;
|
||||||
|
clockadj_set_leap(c->clkid, 0);
|
||||||
}
|
}
|
||||||
|
c->leap_set = 0;
|
||||||
|
c->kernel_leap = dds->kernel_leap;
|
||||||
|
|
||||||
if (c->clkid != CLOCK_INVALID) {
|
if (c->clkid != CLOCK_INVALID) {
|
||||||
fadj = (int) clockadj_get_freq(c->clkid);
|
fadj = (int) clockadj_get_freq(c->clkid);
|
||||||
|
@ -544,6 +592,7 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||||
pr_err("Failed to create clock servo");
|
pr_err("Failed to create clock servo");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
c->servo_state = SERVO_UNLOCKED;
|
||||||
c->avg_delay = mave_create(MAVE_LENGTH);
|
c->avg_delay = mave_create(MAVE_LENGTH);
|
||||||
if (!c->avg_delay) {
|
if (!c->avg_delay) {
|
||||||
pr_err("Failed to create moving average");
|
pr_err("Failed to create moving average");
|
||||||
|
@ -968,18 +1017,20 @@ enum servo_state clock_synchronize(struct clock *c,
|
||||||
c->master_offset = tmv_sub(ingress,
|
c->master_offset = tmv_sub(ingress,
|
||||||
tmv_add(origin, tmv_add(c->path_delay, tmv_add(c->c1, c->c2))));
|
tmv_add(origin, tmv_add(c->path_delay, tmv_add(c->c1, c->c2))));
|
||||||
|
|
||||||
clock_utc_correct(c);
|
|
||||||
|
|
||||||
c->cur.offsetFromMaster = tmv_to_TimeInterval(c->master_offset);
|
|
||||||
|
|
||||||
if (!c->path_delay)
|
if (!c->path_delay)
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
|
if (clock_utc_correct(c, ingress))
|
||||||
|
return c->servo_state;
|
||||||
|
|
||||||
|
c->cur.offsetFromMaster = tmv_to_TimeInterval(c->master_offset);
|
||||||
|
|
||||||
if (c->free_running)
|
if (c->free_running)
|
||||||
return clock_no_adjust(c);
|
return clock_no_adjust(c);
|
||||||
|
|
||||||
adj = servo_sample(c->servo, tmv_to_nanoseconds(c->master_offset),
|
adj = servo_sample(c->servo, tmv_to_nanoseconds(c->master_offset),
|
||||||
tmv_to_nanoseconds(ingress), &state);
|
tmv_to_nanoseconds(ingress), &state);
|
||||||
|
c->servo_state = state;
|
||||||
|
|
||||||
if (c->stats.max_count > 1) {
|
if (c->stats.max_count > 1) {
|
||||||
clock_stats_update(&c->stats,
|
clock_stats_update(&c->stats,
|
||||||
|
|
Loading…
Reference in New Issue