Set TAI offset of system clock.

When synchronizing the system clock and the PTP UTC offset is valid and
traceable, set the TAI offset of the clock to have correct CLOCK_TAI
(which is implemented in the kernel as CLOCK_REALTIME + TAI offset).

Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
master
Miroslav Lichvar 2014-06-18 15:44:49 +02:00 committed by Richard Cochran
parent a3c32b8c65
commit fefd5b4b05
4 changed files with 70 additions and 33 deletions

View File

@ -88,6 +88,7 @@ struct clock {
int freq_est_interval; int freq_est_interval;
int grand_master_capable; /* for 802.1AS only */ int grand_master_capable; /* for 802.1AS only */
int utc_timescale; int utc_timescale;
int utc_offset_set;
int leap_set; int leap_set;
int kernel_leap; int kernel_leap;
int utc_offset; /* grand master role */ int utc_offset; /* grand master role */
@ -700,6 +701,13 @@ static int clock_utc_correct(struct clock *c, tmv_t ingress)
} }
} }
/* Update TAI-UTC offset of the system clock if valid and traceable. */
if (c->tds.flags & UTC_OFF_VALID && c->tds.flags & TIME_TRACEABLE &&
c->utc_offset_set != utc_offset && c->clkid == CLOCK_REALTIME) {
sysclk_set_tai_offset(utc_offset);
c->utc_offset_set = utc_offset;
}
if (!(c->tds.flags & PTP_TIMESCALE)) if (!(c->tds.flags & PTP_TIMESCALE))
return 0; return 0;
@ -789,6 +797,7 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
max_adj = sysclk_max_freq(); max_adj = sysclk_max_freq();
sysclk_set_leap(0); sysclk_set_leap(0);
} }
c->utc_offset_set = 0;
c->leap_set = 0; c->leap_set = 0;
c->time_flags = c->utc_timescale ? 0 : PTP_TIMESCALE; c->time_flags = c->utc_timescale ? 0 : PTP_TIMESCALE;

View File

@ -129,6 +129,17 @@ void sysclk_set_leap(int leap)
realtime_leap_bit = tx.status; realtime_leap_bit = tx.status;
} }
void sysclk_set_tai_offset(int offset)
{
clockid_t clkid = CLOCK_REALTIME;
struct timex tx;
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_TAI;
tx.constant = offset;
if (clock_adjtime(clkid, &tx) < 0)
pr_err("failed to set TAI offset: %m");
}
int sysclk_max_freq(void) int sysclk_max_freq(void)
{ {
clockid_t clkid = CLOCK_REALTIME; clockid_t clkid = CLOCK_REALTIME;

View File

@ -57,6 +57,12 @@ void clockadj_step(clockid_t clkid, int64_t step);
*/ */
void sysclk_set_leap(int leap); void sysclk_set_leap(int leap);
/**
* Set the TAI offset of the system clock to have correct CLOCK_TAI.
* @param offset The TAI-UTC offset in seconds.
*/
void sysclk_set_tai_offset(int offset);
/** /**
* Read maximum frequency adjustment of the system clock (CLOCK_REALTIME). * Read maximum frequency adjustment of the system clock (CLOCK_REALTIME).
* @return The maximum frequency adjustment in parts per billion (ppb). * @return The maximum frequency adjustment in parts per billion (ppb).

View File

@ -78,6 +78,7 @@ struct clock {
int new_state; int new_state;
int sync_offset; int sync_offset;
int leap_set; int leap_set;
int utc_offset_set;
struct servo *servo; struct servo *servo;
enum servo_state servo_state; enum servo_state servo_state;
char *device; char *device;
@ -103,6 +104,7 @@ struct node {
double phc_interval; double phc_interval;
int sync_offset; int sync_offset;
int forced_sync_offset; int forced_sync_offset;
int utc_offset_traceable;
int leap; int leap;
int kernel_leap; int kernel_leap;
struct pmc *pmc; struct pmc *pmc;
@ -864,9 +866,12 @@ static int run_pmc_get_utc_offset(struct node *node, int timeout)
node->leap = -1; node->leap = -1;
else else
node->leap = 0; node->leap = 0;
node->utc_offset_traceable = tds->flags & UTC_OFF_VALID &&
tds->flags & TIME_TRACEABLE;
} else { } else {
node->sync_offset = 0; node->sync_offset = 0;
node->leap = 0; node->leap = 0;
node->utc_offset_traceable = 0;
} }
msg_put(msg); msg_put(msg);
return 1; return 1;
@ -1076,42 +1081,48 @@ static int clock_handle_leap(struct node *node, struct clock *clock,
clock->sync_offset = node->sync_offset; clock->sync_offset = node->sync_offset;
if (!node_leap && !clock->leap_set) if ((node_leap || clock->leap_set) &&
return 0; clock->is_utc != node->master->is_utc) {
/* If the master clock is in UTC, get a time stamp from it, as
if (clock->is_utc == node->master->is_utc) it is the clock which will include the leap second. */
return 0; if (node->master->is_utc) {
struct timespec tp;
/* If the system clock is the master clock, get a time stamp from if (clock_gettime(node->master->clkid, &tp)) {
it, as it is the clock which will include the leap second. */ pr_err("failed to read clock: %m");
if (node->master->is_utc) { return -1;
struct timespec tp; }
if (clock_gettime(node->master->clkid, &tp)) { ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
pr_err("failed to read clock: %m"); }
return -1;
/* If the clock will be stepped, the time stamp has to be the
new time. Ignore possible 1 second error in UTC offset. */
if (clock->is_utc && clock->servo_state == SERVO_UNLOCKED)
ts -= offset + get_sync_offset(node, clock);
/* 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, clock->leap_set,
&node_leap,
&clock->sync_offset);
if (clock->leap_set != clock_leap) {
/* Only the system clock can leap. */
if (clock->clkid == CLOCK_REALTIME &&
node->kernel_leap)
sysclk_set_leap(clock_leap);
clock->leap_set = clock_leap;
} }
ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
} }
/* If the clock will be stepped, the time stamp has to be the if (node->utc_offset_traceable &&
target time. Ignore possible 1 second error in UTC offset. */ clock->utc_offset_set != clock->sync_offset) {
if (clock->is_utc && clock->servo_state == SERVO_UNLOCKED) if (clock->clkid == CLOCK_REALTIME)
ts -= offset + get_sync_offset(node, clock); sysclk_set_tai_offset(clock->sync_offset);
clock->utc_offset_set = clock->sync_offset;
/* 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, clock->leap_set,
&node_leap, &clock->sync_offset);
if (clock->leap_set != clock_leap) {
/* Only the system clock can leap. */
if (clock->clkid == CLOCK_REALTIME && node->kernel_leap)
sysclk_set_leap(clock_leap);
clock->leap_set = clock_leap;
} }
return 0; return 0;