From fefd5b4b05039ea0a0770291b12b0eb931079970 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 18 Jun 2014 15:44:49 +0200 Subject: [PATCH] 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 --- clock.c | 9 +++++++ clockadj.c | 11 ++++++++ clockadj.h | 6 +++++ phc2sys.c | 77 +++++++++++++++++++++++++++++++----------------------- 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/clock.c b/clock.c index 4378dec..14e20ad 100644 --- a/clock.c +++ b/clock.c @@ -88,6 +88,7 @@ struct clock { int freq_est_interval; int grand_master_capable; /* for 802.1AS only */ int utc_timescale; + int utc_offset_set; int leap_set; int kernel_leap; 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)) return 0; @@ -789,6 +797,7 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count, max_adj = sysclk_max_freq(); sysclk_set_leap(0); } + c->utc_offset_set = 0; c->leap_set = 0; c->time_flags = c->utc_timescale ? 0 : PTP_TIMESCALE; diff --git a/clockadj.c b/clockadj.c index fbf9423..5eb7cce 100644 --- a/clockadj.c +++ b/clockadj.c @@ -129,6 +129,17 @@ void sysclk_set_leap(int leap) 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) { clockid_t clkid = CLOCK_REALTIME; diff --git a/clockadj.h b/clockadj.h index 7578e84..492418e 100644 --- a/clockadj.h +++ b/clockadj.h @@ -57,6 +57,12 @@ void clockadj_step(clockid_t clkid, int64_t step); */ 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). * @return The maximum frequency adjustment in parts per billion (ppb). diff --git a/phc2sys.c b/phc2sys.c index 62a51bd..e063372 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -78,6 +78,7 @@ struct clock { int new_state; int sync_offset; int leap_set; + int utc_offset_set; struct servo *servo; enum servo_state servo_state; char *device; @@ -103,6 +104,7 @@ struct node { double phc_interval; int sync_offset; int forced_sync_offset; + int utc_offset_traceable; int leap; int kernel_leap; struct pmc *pmc; @@ -864,9 +866,12 @@ static int run_pmc_get_utc_offset(struct node *node, int timeout) node->leap = -1; else node->leap = 0; + node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && + tds->flags & TIME_TRACEABLE; } else { node->sync_offset = 0; node->leap = 0; + node->utc_offset_traceable = 0; } msg_put(msg); return 1; @@ -1076,42 +1081,48 @@ static int clock_handle_leap(struct node *node, struct clock *clock, clock->sync_offset = node->sync_offset; - if (!node_leap && !clock->leap_set) - return 0; - - if (clock->is_utc == node->master->is_utc) - return 0; - - /* If the system clock is the master clock, get a time stamp from - it, as it is the clock which will include the leap second. */ - if (node->master->is_utc) { - struct timespec tp; - if (clock_gettime(node->master->clkid, &tp)) { - pr_err("failed to read clock: %m"); - return -1; + if ((node_leap || clock->leap_set) && + clock->is_utc != node->master->is_utc) { + /* If the master clock is in UTC, get a time stamp from it, as + it is the clock which will include the leap second. */ + if (node->master->is_utc) { + struct timespec tp; + if (clock_gettime(node->master->clkid, &tp)) { + pr_err("failed to read clock: %m"); + return -1; + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + } + + /* 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 - target 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; + if (node->utc_offset_traceable && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); + clock->utc_offset_set = clock->sync_offset; } return 0;