Discussion:
[Linuxptp-devel] [PATCH 00/14] automatic phc2sys configuration, phc2sys part
Jiri Benc
2014-05-07 16:23:04 UTC
Permalink
This is the phc2sys part of the autoconfig patchset. It uses the event
subscription mechanism implemented in ptp4l.

The patchset is prepared to handle the "emulated boundary clock" scenario,
i.e. ptp4l working over multiple PHC's. I have old patches to do that (the
dynamic port allocation patchset + patchset to support multiple PHC's) which
I'll rebase on top of this and post after the patchset is accepted.

Jiri

Jiri Benc (14):
phc2sys: generalize run_pmc
phc2sys: split update_sync_offset
phc2sys: split clock and node
phc2sys: store information about clocks being UTC or TAI
phc2sys: rearrange declarations
phc2sys: open devices in clock_add
phc2sys: track ports
pmc_common: easy way to set port and broadcast target
phc2sys: event subscription
phc2sys: propagate received errors
phc2sys: autoconfiguration
phc2sys: autoconfigure realtime clock on demand only
phc2sys: check clockIdentity
phc2sys: man page update for -a and -r options

phc2sys.8 | 115 +++++--
phc2sys.c | 1076 ++++++++++++++++++++++++++++++++++++++++++++--------------
pmc_common.c | 12 +-
pmc_common.h | 2 +
4 files changed, 908 insertions(+), 297 deletions(-)
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:06 UTC
Permalink
Split the generic (global) part of update_sync_offset and the part that
affects individual clocks. This is in preparation for phc2sys handling
synchronization of more clocks.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 0581eb5bcb24..19dce45964eb 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -60,7 +60,9 @@
#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC)

struct clock;
-static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts);
+static int update_sync_offset(struct clock *clock);
+static int clock_handle_leap(struct clock *clock, clockid_t src,
+ int64_t offset, uint64_t ts, int do_leap);

static clockid_t clock_open(char *device)
{
@@ -181,13 +183,14 @@ static void update_clock_stats(struct clock *clock,
stats_reset(clock->delay_stats);
}

-static void update_clock(struct clock *clock,
- int64_t offset, uint64_t ts, int64_t delay)
+static void update_clock(struct clock *clock, clockid_t src,
+ int64_t offset, uint64_t ts, int64_t delay,
+ int do_leap)
{
enum servo_state state;
double ppb;

- if (update_sync_offset(clock, offset, ts))
+ if (clock_handle_leap(clock, src, offset, ts, do_leap))
return;

if (clock->sync_offset_direction)
@@ -268,6 +271,7 @@ static int do_pps_loop(struct clock *clock, int fd,
{
int64_t pps_offset, phc_offset, phc_delay;
uint64_t pps_ts, phc_ts;
+ int do_leap;

clock->source_label = "pps";

@@ -304,7 +308,10 @@ static int do_pps_loop(struct clock *clock, int fd,
pps_offset = pps_ts - phc_ts;
}

- update_clock(clock, pps_offset, pps_ts, -1);
+ do_leap = update_sync_offset(clock);
+ if (do_leap <= 0)
+ continue;
+ update_clock(clock, src, pps_offset, pps_ts, -1, do_leap);
}
close(fd);
return 0;
@@ -316,6 +323,7 @@ static int do_sysoff_loop(struct clock *clock, clockid_t src,
uint64_t ts;
int64_t offset, delay;
int err = 0, fd = CLOCKID_TO_FD(src);
+ int do_leap;

clock->source_label = "sys";

@@ -325,7 +333,10 @@ static int do_sysoff_loop(struct clock *clock, clockid_t src,
err = -1;
break;
}
- update_clock(clock, offset, ts, delay);
+ do_leap = update_sync_offset(clock);
+ if (do_leap <= 0)
+ continue;
+ update_clock(clock, src, offset, ts, delay, do_leap);
}
return err;
}
@@ -335,6 +346,7 @@ static int do_phc_loop(struct clock *clock, clockid_t src,
{
uint64_t ts;
int64_t offset, delay;
+ int do_leap;

clock->source_label = "phc";

@@ -344,7 +356,10 @@ static int do_phc_loop(struct clock *clock, clockid_t src,
&offset, &ts, &delay)) {
continue;
}
- update_clock(clock, offset, ts, delay);
+ do_leap = update_sync_offset(clock);
+ if (do_leap <= 0)
+ continue;
+ update_clock(clock, src, offset, ts, delay, do_leap);
}
return 0;
}
@@ -495,10 +510,19 @@ static void close_pmc(struct clock *clock)
clock->pmc = NULL;
}

-static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts)
+/* Returns: -1 in case of error, 0 for normal sync, 1 to leap clock */
+static int update_sync_offset(struct clock *clock)
{
+ struct timespec tp;
+ uint64_t ts;
int clock_leap;

+ if (clock_gettime(CLOCK_REALTIME, &tp)) {
+ pr_err("failed to read clock: %m");
+ return -1;
+ }
+ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
+
if (clock->pmc &&
!(ts > clock->pmc_last_update &&
ts - clock->pmc_last_update < PMC_UPDATE_INTERVAL)) {
@@ -511,9 +535,28 @@ static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts)
if (!clock->leap && !clock->leap_set)
return 0;

+ clock_leap = leap_second_status(ts, clock->leap_set,
+ &clock->leap, &clock->sync_offset);
+ if (clock->leap_set != clock_leap) {
+ clock->leap_set = clock_leap;
+ return 1;
+ }
+ return 0;
+}
+
+/* Returns: non-zero to skip clock update */
+static int clock_handle_leap(struct clock *clock, clockid_t src,
+ int64_t offset, uint64_t ts, int do_leap)
+{
+ if (!clock->leap && !do_leap)
+ return 0;
+
+ if (clock->clkid != CLOCK_REALTIME && src != CLOCK_REALTIME)
+ 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 (clock->clkid != CLOCK_REALTIME) {
+ if (src == CLOCK_REALTIME) {
struct timespec tp;
if (clock_gettime(CLOCK_REALTIME, &tp)) {
pr_err("failed to read clock: %m");
@@ -533,17 +576,13 @@ static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts)
/* 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;
+ return 1;
}

- clock_leap = leap_second_status(ts, clock->leap_set,
- &clock->leap, &clock->sync_offset);
-
- if (clock->leap_set != clock_leap) {
+ if (do_leap) {
/* Only the system clock can leap. */
if (clock->clkid == CLOCK_REALTIME && clock->kernel_leap)
- sysclk_set_leap(clock_leap);
- clock->leap_set = clock_leap;
+ sysclk_set_leap(clock->leap_set);
}

return 0;
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:05 UTC
Permalink
Make run_pmc usable for any kind of management message. Create wrappers for
waiting for ptp4l and for getting UTC offset.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 131 +++++++++++++++++++++++++++++++------------------------------
1 files changed, 66 insertions(+), 65 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 5ecb602120c3..0581eb5bcb24 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -141,7 +141,6 @@ struct clock {
int leap_set;
int kernel_leap;
struct pmc *pmc;
- int pmc_ds_idx;
int pmc_ds_requested;
uint64_t pmc_last_update;
struct clockcheck *sanity_check;
@@ -390,31 +389,14 @@ static int init_pmc(struct clock *clock, int domain_number)
return 0;
}

-static int run_pmc(struct clock *clock, int timeout,
- int wait_sync, int get_utc_offset)
+static int run_pmc(struct clock *clock, int timeout, int ds_id,
+ struct ptp_message **msg)
{
- struct ptp_message *msg;
- struct timePropertiesDS *tds;
- void *data;
#define N_FD 1
struct pollfd pollfd[N_FD];
- int cnt, ds_done;
-#define N_ID 2
- int ds_ids[N_ID] = {
- PORT_DATA_SET,
- TIME_PROPERTIES_DATA_SET
- };
-
- while (clock->pmc_ds_idx < N_ID) {
- /* Check if the data set is really needed. */
- if ((ds_ids[clock->pmc_ds_idx] == PORT_DATA_SET &&
- !wait_sync) ||
- (ds_ids[clock->pmc_ds_idx] == TIME_PROPERTIES_DATA_SET &&
- !get_utc_offset)) {
- clock->pmc_ds_idx++;
- continue;
- }
+ int cnt;

+ while (1) {
pollfd[0].fd = pmc_get_transport_fd(clock->pmc);
pollfd[0].events = POLLIN|POLLPRI;
if (!clock->pmc_ds_requested)
@@ -434,62 +416,76 @@ static int run_pmc(struct clock *clock, int timeout,
/* Send a new request if there are no pending messages. */
if ((pollfd[0].revents & POLLOUT) &&
!(pollfd[0].revents & (POLLIN|POLLPRI))) {
- pmc_send_get_action(clock->pmc,
- ds_ids[clock->pmc_ds_idx]);
+ pmc_send_get_action(clock->pmc, ds_id);
clock->pmc_ds_requested = 1;
}

if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
continue;

- msg = pmc_recv(clock->pmc);
+ *msg = pmc_recv(clock->pmc);

- if (!msg)
+ if (!*msg)
continue;

- if (!is_msg_mgt(msg) ||
- get_mgt_id(msg) != ds_ids[clock->pmc_ds_idx]) {
- msg_put(msg);
+ if (!is_msg_mgt(*msg) ||
+ get_mgt_id(*msg) != ds_id) {
+ msg_put(*msg);
+ *msg = NULL;
continue;
}
+ clock->pmc_ds_requested = 0;
+ return 1;
+ }
+}

- data = get_mgt_data(msg);
- ds_done = 0;
-
- switch (get_mgt_id(msg)) {
- case PORT_DATA_SET:
- switch (((struct portDS *)data)->portState) {
- case PS_MASTER:
- case PS_SLAVE:
- ds_done = 1;
- break;
- }
+static int run_pmc_wait_sync(struct clock *clock, int timeout)
+{
+ struct ptp_message *msg;
+ int res;
+ void *data;
+ Enumeration8 portState;

- break;
- case TIME_PROPERTIES_DATA_SET:
- tds = (struct timePropertiesDS *)data;
- if (tds->flags & PTP_TIMESCALE) {
- clock->sync_offset = tds->currentUtcOffset;
- if (tds->flags & LEAP_61)
- clock->leap = 1;
- else if (tds->flags & LEAP_59)
- clock->leap = -1;
- else
- clock->leap = 0;
- }
- ds_done = 1;
- break;
- }
+ while (1) {
+ res = run_pmc(clock, timeout, PORT_DATA_SET, &msg);
+ if (res <= 0)
+ return res;

- if (ds_done) {
- /* Proceed with the next data set. */
- clock->pmc_ds_idx++;
- clock->pmc_ds_requested = 0;
- }
+ data = get_mgt_data(msg);
+ portState = ((struct portDS *)data)->portState;
msg_put(msg);
+
+ switch (portState) {
+ case PS_MASTER:
+ case PS_SLAVE:
+ return 1;
+ }
+ /* try to get more data sets (for other ports) */
+ clock->pmc_ds_requested = 1;
}
+}

- clock->pmc_ds_idx = 0;
+static int run_pmc_get_utc_offset(struct clock *clock, int timeout)
+{
+ struct ptp_message *msg;
+ int res;
+ struct timePropertiesDS *tds;
+
+ res = run_pmc(clock, timeout, TIME_PROPERTIES_DATA_SET, &msg);
+ if (res <= 0)
+ return res;
+
+ tds = (struct timePropertiesDS *)get_mgt_data(msg);
+ if (tds->flags & PTP_TIMESCALE) {
+ clock->sync_offset = tds->currentUtcOffset;
+ if (tds->flags & LEAP_61)
+ clock->leap = 1;
+ else if (tds->flags & LEAP_59)
+ clock->leap = -1;
+ else
+ clock->leap = 0;
+ }
+ msg_put(msg);
return 1;
}

@@ -506,7 +502,7 @@ static int update_sync_offset(struct clock *clock, int64_t offset, uint64_t ts)
if (clock->pmc &&
!(ts > clock->pmc_last_update &&
ts - clock->pmc_last_update < PMC_UPDATE_INTERVAL)) {
- if (run_pmc(clock, 0, 0, 1) > 0)
+ if (run_pmc_get_utc_offset(clock, 0) > 0)
clock->pmc_last_update = ts;
}

@@ -767,17 +763,22 @@ int main(int argc, char *argv[])
return -1;

while (1) {
- r = run_pmc(&dst_clock, 1000,
- wait_sync, !forced_sync_offset);
+ r = run_pmc_wait_sync(&dst_clock, 1000);
if (r < 0)
return -1;
- else if (r > 0)
+ if (r > 0)
break;
else
pr_notice("Waiting for ptp4l...");
}

if (!forced_sync_offset) {
+ r = run_pmc_get_utc_offset(&dst_clock, 1000);
+ if (r <= 0) {
+ pr_err("failed to get UTC offset");
+ return -1;
+ }
+
if (src != CLOCK_REALTIME &&
dst_clock.clkid == CLOCK_REALTIME)
dst_clock.sync_offset_direction = 1;
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:10 UTC
Permalink
Do not call clock_open to open a clock device but let clock_add do that and
return the newly created struct. Also, store the device (interface) name in
struct clock.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 81 ++++++++++++++++++++++++++++++++++--------------------------
1 files changed, 46 insertions(+), 35 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 34f5f94ccb91..62e9b8c19e17 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -67,6 +67,7 @@ struct clock {
int is_utc;
struct servo *servo;
enum servo_state servo_state;
+ char *device;
const char *source_label;
struct stats *offset_stats;
struct stats *freq_stats;
@@ -129,19 +130,27 @@ static clockid_t clock_open(char *device)
return clkid;
}

-static int clock_add(struct node *node, clockid_t clkid)
+static struct clock *clock_add(struct node *node, char *device)
{
struct clock *c;
+ clockid_t clkid = CLOCK_INVALID;
int max_ppb;
double ppb;

+ if (device) {
+ clkid = clock_open(device);
+ if (clkid == CLOCK_INVALID)
+ return NULL;
+ }
+
c = calloc(1, sizeof(*c));
if (!c) {
pr_err("failed to allocate memory for a clock");
- return -1;
+ return NULL;
}
c->clkid = clkid;
c->servo_state = SERVO_UNLOCKED;
+ c->device = strdup(device);

if (c->clkid == CLOCK_REALTIME) {
c->source_label = "sys";
@@ -158,14 +167,14 @@ static int clock_add(struct node *node, clockid_t clkid)
!c->freq_stats ||
!c->delay_stats) {
pr_err("failed to create stats");
- return -1;
+ return NULL;
}
}
if (node->sanity_freq_limit) {
c->sanity_check = clockcheck_create(node->sanity_freq_limit);
if (!c->sanity_check) {
pr_err("failed to create clock check");
- return -1;
+ return NULL;
}
}

@@ -181,7 +190,7 @@ static int clock_add(struct node *node, clockid_t clkid)
max_ppb = phc_max_adj(c->clkid);
if (!max_ppb) {
pr_err("clock is not adjustable");
- return -1;
+ return NULL;
}
}

@@ -194,7 +203,7 @@ static int clock_add(struct node *node, clockid_t clkid)
node->phc_readings));

LIST_INSERT_HEAD(&node->clocks, c, list);
- return 0;
+ return c;
}

static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
@@ -699,8 +708,8 @@ static void usage(char *progname)
int main(int argc, char *argv[])
{
char *progname;
- clockid_t src = CLOCK_INVALID;
- clockid_t dst = CLOCK_REALTIME;
+ char *src_name = NULL, *dst_name = NULL;
+ struct clock *src, *dst;
int c, domain_number = 0, pps_fd = -1;
int r, wait_sync = 0;
int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
@@ -723,7 +732,7 @@ int main(int argc, char *argv[])
"c:d:s:E:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
switch (c) {
case 'c':
- dst = clock_open(optarg);
+ dst_name = strdup(optarg);
break;
case 'd':
pps_fd = open(optarg, O_RDONLY);
@@ -737,7 +746,7 @@ int main(int argc, char *argv[])
fprintf(stderr,
"'-i' has been deprecated. please use '-s' instead.\n");
case 's':
- src = clock_open(optarg);
+ src_name = strdup(optarg);
break;
case 'E':
if (!strcasecmp(optarg, "pi")) {
@@ -826,38 +835,46 @@ int main(int argc, char *argv[])
}
}

- if (pps_fd < 0 && src == CLOCK_INVALID) {
+ if (pps_fd < 0 && !src_name) {
fprintf(stderr,
"valid source clock must be selected.\n");
goto bad_usage;
}

- if (dst == CLOCK_INVALID) {
+ if (!wait_sync && !node.forced_sync_offset) {
fprintf(stderr,
- "valid destination clock must be selected.\n");
+ "time offset must be specified using -w or -O\n");
goto bad_usage;
}

- if (pps_fd >= 0 && dst != CLOCK_REALTIME) {
+ print_set_progname(progname);
+ print_set_verbose(verbose);
+ print_set_syslog(use_syslog);
+ print_set_level(print_level);
+
+ src = clock_add(&node, src_name);
+ free(src_name);
+ node.master = src;
+ dst = clock_add(&node, dst_name ? dst_name : "CLOCK_REALTIME");
+ free(dst_name);
+
+ if (!dst) {
fprintf(stderr,
- "cannot use a pps device unless destination is CLOCK_REALTIME\n");
+ "valid destination clock must be selected.\n");
goto bad_usage;
}

- if (!wait_sync && !node.forced_sync_offset) {
+ if (!src) {
fprintf(stderr,
- "time offset must be specified using -w or -O\n");
+ "valid source clock must be selected.\n");
goto bad_usage;
}

- print_set_progname(progname);
- print_set_verbose(verbose);
- print_set_syslog(use_syslog);
- print_set_level(print_level);
-
- clock_add(&node, src);
- node.master = LIST_FIRST(&node.clocks);
- clock_add(&node, dst);
+ if (pps_fd >= 0 && dst->clkid != CLOCK_REALTIME) {
+ fprintf(stderr,
+ "cannot use a pps device unless destination is CLOCK_REALTIME\n");
+ goto bad_usage;
+ }

if (wait_sync) {
if (init_pmc(&node, domain_number))
@@ -882,22 +899,16 @@ int main(int argc, char *argv[])
}

if (node.forced_sync_offset ||
- (src != CLOCK_REALTIME && dst != CLOCK_REALTIME) ||
- src == CLOCK_INVALID)
+ (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) ||
+ src->clkid == CLOCK_INVALID)
close_pmc(&node);
}

if (pps_fd >= 0) {
/* only one destination clock allowed with PPS until we
* implement a mean to specify PTP port to PPS mapping */
- struct clock *dst_clock;
-
- LIST_FOREACH(dst_clock, &node.clocks, list) {
- if (dst_clock != node.master)
- break;
- }
- servo_sync_interval(dst_clock->servo, 1.0);
- return do_pps_loop(&node, dst_clock, pps_fd);
+ servo_sync_interval(dst->servo, 1.0);
+ return do_pps_loop(&node, dst, pps_fd);
}

return do_loop(&node);
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:09 UTC
Permalink
This just moves code around to have related functions together and forward
declaration at the beginning of the file. No code changes.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 208 ++++++++++++++++++++++++++++++-------------------------------
1 files changed, 103 insertions(+), 105 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 6c86b4d9f028..34f5f94ccb91 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -60,7 +60,41 @@
#define PHC_PPS_OFFSET_LIMIT 10000000
#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC)

-struct clock;
+struct clock {
+ LIST_ENTRY(clock) list;
+ clockid_t clkid;
+ int sysoff_supported;
+ int is_utc;
+ struct servo *servo;
+ enum servo_state servo_state;
+ const char *source_label;
+ struct stats *offset_stats;
+ struct stats *freq_stats;
+ struct stats *delay_stats;
+ struct clockcheck *sanity_check;
+};
+
+struct node {
+ unsigned int stats_max_count;
+ int sanity_freq_limit;
+ enum servo_type servo_type;
+ int phc_readings;
+ double phc_interval;
+ int sync_offset;
+ int forced_sync_offset;
+ int leap;
+ int leap_set;
+ int kernel_leap;
+ struct pmc *pmc;
+ int pmc_ds_requested;
+ uint64_t pmc_last_update;
+ LIST_HEAD(clock_head, clock) clocks;
+ struct clock *master;
+};
+
+static int update_sync_offset(struct node *node);
+static int clock_handle_leap(struct node *node, struct clock *clock,
+ int64_t offset, uint64_t ts, int do_leap);

static clockid_t clock_open(char *device)
{
@@ -95,6 +129,74 @@ static clockid_t clock_open(char *device)
return clkid;
}

+static int clock_add(struct node *node, clockid_t clkid)
+{
+ struct clock *c;
+ int max_ppb;
+ double ppb;
+
+ c = calloc(1, sizeof(*c));
+ if (!c) {
+ pr_err("failed to allocate memory for a clock");
+ return -1;
+ }
+ c->clkid = clkid;
+ c->servo_state = SERVO_UNLOCKED;
+
+ if (c->clkid == CLOCK_REALTIME) {
+ c->source_label = "sys";
+ c->is_utc = 1;
+ } else {
+ c->source_label = "phc";
+ }
+
+ if (node->stats_max_count > 0) {
+ c->offset_stats = stats_create();
+ c->freq_stats = stats_create();
+ c->delay_stats = stats_create();
+ if (!c->offset_stats ||
+ !c->freq_stats ||
+ !c->delay_stats) {
+ pr_err("failed to create stats");
+ return -1;
+ }
+ }
+ if (node->sanity_freq_limit) {
+ c->sanity_check = clockcheck_create(node->sanity_freq_limit);
+ if (!c->sanity_check) {
+ pr_err("failed to create clock check");
+ return -1;
+ }
+ }
+
+ clockadj_init(c->clkid);
+ ppb = clockadj_get_freq(c->clkid);
+ /* The reading may silently fail and return 0, reset the frequency to
+ make sure ppb is the actual frequency of the clock. */
+ clockadj_set_freq(c->clkid, ppb);
+ if (c->clkid == CLOCK_REALTIME) {
+ sysclk_set_leap(0);
+ max_ppb = sysclk_max_freq();
+ } else {
+ max_ppb = phc_max_adj(c->clkid);
+ if (!max_ppb) {
+ pr_err("clock is not adjustable");
+ return -1;
+ }
+ }
+
+ c->servo = servo_create(node->servo_type, -ppb, max_ppb, 0);
+ servo_sync_interval(c->servo, node->phc_interval);
+
+ if (clkid != CLOCK_REALTIME)
+ c->sysoff_supported = (SYSOFF_SUPPORTED ==
+ sysoff_probe(CLOCKID_TO_FD(clkid),
+ node->phc_readings));
+
+ LIST_INSERT_HEAD(&node->clocks, c, list);
+ return 0;
+}
+
static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
int64_t *offset, uint64_t *ts, int64_t *delay)
{
@@ -126,42 +228,6 @@ static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
return 1;
}

-struct clock {
- LIST_ENTRY(clock) list;
- clockid_t clkid;
- int sysoff_supported;
- int is_utc;
- struct servo *servo;
- enum servo_state servo_state;
- const char *source_label;
- struct stats *offset_stats;
- struct stats *freq_stats;
- struct stats *delay_stats;
- struct clockcheck *sanity_check;
-};
-
-struct node {
- unsigned int stats_max_count;
- int sanity_freq_limit;
- enum servo_type servo_type;
- int phc_readings;
- double phc_interval;
- int sync_offset;
- int forced_sync_offset;
- int leap;
- int leap_set;
- int kernel_leap;
- struct pmc *pmc;
- int pmc_ds_requested;
- uint64_t pmc_last_update;
- LIST_HEAD(clock_head, clock) clocks;
- struct clock *master;
-};
-
-static int update_sync_offset(struct node *node);
-static int clock_handle_leap(struct node *node, struct clock *clock,
- int64_t offset, uint64_t ts, int do_leap);
-
static int64_t get_sync_offset(struct node *node, struct clock *src)
{
int direction = node->forced_sync_offset;
@@ -600,74 +666,6 @@ static int clock_handle_leap(struct node *node, struct clock *clock,
return 0;
}

-static int clock_add(struct node *node, clockid_t clkid)
-{
- struct clock *c;
- int max_ppb;
- double ppb;
-
- c = calloc(1, sizeof(*c));
- if (!c) {
- pr_err("failed to allocate memory for a clock");
- return -1;
- }
- c->clkid = clkid;
- c->servo_state = SERVO_UNLOCKED;
-
- if (c->clkid == CLOCK_REALTIME) {
- c->source_label = "sys";
- c->is_utc = 1;
- } else {
- c->source_label = "phc";
- }
-
- if (node->stats_max_count > 0) {
- c->offset_stats = stats_create();
- c->freq_stats = stats_create();
- c->delay_stats = stats_create();
- if (!c->offset_stats ||
- !c->freq_stats ||
- !c->delay_stats) {
- pr_err("failed to create stats");
- return -1;
- }
- }
- if (node->sanity_freq_limit) {
- c->sanity_check = clockcheck_create(node->sanity_freq_limit);
- if (!c->sanity_check) {
- pr_err("failed to create clock check");
- return -1;
- }
- }
-
- clockadj_init(c->clkid);
- ppb = clockadj_get_freq(c->clkid);
- /* The reading may silently fail and return 0, reset the frequency to
- make sure ppb is the actual frequency of the clock. */
- clockadj_set_freq(c->clkid, ppb);
- if (c->clkid == CLOCK_REALTIME) {
- sysclk_set_leap(0);
- max_ppb = sysclk_max_freq();
- } else {
- max_ppb = phc_max_adj(c->clkid);
- if (!max_ppb) {
- pr_err("clock is not adjustable");
- return -1;
- }
- }
-
- c->servo = servo_create(node->servo_type, -ppb, max_ppb, 0);
- servo_sync_interval(c->servo, node->phc_interval);
-
- if (clkid != CLOCK_REALTIME)
- c->sysoff_supported = (SYSOFF_SUPPORTED ==
- sysoff_probe(CLOCKID_TO_FD(clkid),
- node->phc_readings));
-
- LIST_INSERT_HEAD(&node->clocks, c, list);
- return 0;
-}
-
static void usage(char *progname)
{
fprintf(stderr,
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:07 UTC
Permalink
Split members that apply to all synchronized clocks and members that apply
to an individual clock. Keep all clocks in a list, with a pointer to the
source clock. This will allow to support multiple clocks synchronization.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 397 +++++++++++++++++++++++++++++++++---------------------------
1 files changed, 218 insertions(+), 179 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 19dce45964eb..825d7328af15 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -60,9 +61,6 @@
#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC)

struct clock;
-static int update_sync_offset(struct clock *clock);
-static int clock_handle_leap(struct clock *clock, clockid_t src,
- int64_t offset, uint64_t ts, int do_leap);

static clockid_t clock_open(char *device)
{
@@ -129,14 +127,24 @@ static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
}

struct clock {
+ LIST_ENTRY(clock) list;
clockid_t clkid;
+ int sysoff_supported;
struct servo *servo;
enum servo_state servo_state;
const char *source_label;
struct stats *offset_stats;
struct stats *freq_stats;
struct stats *delay_stats;
+ struct clockcheck *sanity_check;
+};
+
+struct node {
unsigned int stats_max_count;
+ int sanity_freq_limit;
+ enum servo_type servo_type;
+ int phc_readings;
+ double phc_interval;
int sync_offset;
int sync_offset_direction;
int leap;
@@ -145,10 +153,15 @@ struct clock {
struct pmc *pmc;
int pmc_ds_requested;
uint64_t pmc_last_update;
- struct clockcheck *sanity_check;
+ LIST_HEAD(clock_head, clock) clocks;
+ struct clock *master;
};

-static void update_clock_stats(struct clock *clock,
+static int update_sync_offset(struct node *node);
+static int clock_handle_leap(struct node *node, struct clock *clock,
+ int64_t offset, uint64_t ts, int do_leap);
+
+static void update_clock_stats(struct clock *clock, unsigned int max_count,
int64_t offset, double freq, int64_t delay)
{
struct stats_result offset_stats, freq_stats, delay_stats;
@@ -158,7 +171,7 @@ static void update_clock_stats(struct clock *clock,
if (delay >= 0)
stats_add_value(clock->delay_stats, delay);

- if (stats_get_num_values(clock->offset_stats) < clock->stats_max_count)
+ if (stats_get_num_values(clock->offset_stats) < max_count)
return;

stats_get_result(clock->offset_stats, &offset_stats);
@@ -183,19 +196,19 @@ static void update_clock_stats(struct clock *clock,
stats_reset(clock->delay_stats);
}

-static void update_clock(struct clock *clock, clockid_t src,
+static void update_clock(struct node *node, struct clock *clock,
int64_t offset, uint64_t ts, int64_t delay,
int do_leap)
{
enum servo_state state;
double ppb;

- if (clock_handle_leap(clock, src, offset, ts, do_leap))
+ if (clock_handle_leap(node, clock, offset, ts, do_leap))
return;

- if (clock->sync_offset_direction)
- offset += clock->sync_offset * NS_PER_SEC *
- clock->sync_offset_direction;
+ if (node->sync_offset_direction)
+ offset += node->sync_offset * NS_PER_SEC *
+ node->sync_offset_direction;

if (clock->sanity_check && clockcheck_sample(clock->sanity_check, ts))
servo_reset(clock->servo);
@@ -221,15 +234,15 @@ static void update_clock(struct clock *clock, clockid_t src,
}

if (clock->offset_stats) {
- update_clock_stats(clock, offset, ppb, delay);
+ update_clock_stats(clock, node->stats_max_count, offset, ppb, delay);
} else {
if (delay >= 0) {
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f "
"delay %6" PRId64,
- clock->source_label, offset, state, ppb, delay);
+ node->master->source_label, offset, state, ppb, delay);
} else {
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f",
- clock->source_label, offset, state, ppb);
+ node->master->source_label, offset, state, ppb);
}
}
}
@@ -266,20 +279,20 @@ static int read_pps(int fd, int64_t *offset, uint64_t *ts)
return 1;
}

-static int do_pps_loop(struct clock *clock, int fd,
- clockid_t src, int n_readings)
+static int do_pps_loop(struct node *node, struct clock *clock, int fd)
{
int64_t pps_offset, phc_offset, phc_delay;
uint64_t pps_ts, phc_ts;
+ clockid_t src = node->master->clkid;
int do_leap;

- clock->source_label = "pps";
+ node->master->source_label = "pps";

if (src == CLOCK_INVALID) {
/* The sync offset can't be applied with PPS alone. */
- clock->sync_offset_direction = 0;
+ node->sync_offset_direction = 0;
} else {
- enable_pps_output(src);
+ enable_pps_output(node->master->clkid);
}

while (1) {
@@ -290,7 +303,7 @@ static int do_pps_loop(struct clock *clock, int fd,
/* If a PHC is available, use it to get the whole number
of seconds in the offset and PPS for the rest. */
if (src != CLOCK_INVALID) {
- if (!read_phc(src, clock->clkid, n_readings,
+ if (!read_phc(src, clock->clkid, node->phc_readings,
&phc_offset, &phc_ts, &phc_delay))
return -1;

@@ -308,60 +321,54 @@ static int do_pps_loop(struct clock *clock, int fd,
pps_offset = pps_ts - phc_ts;
}

- do_leap = update_sync_offset(clock);
- if (do_leap <= 0)
+ do_leap = update_sync_offset(node);
+ if (do_leap < 0)
continue;
- update_clock(clock, src, pps_offset, pps_ts, -1, do_leap);
+ update_clock(node, clock, pps_offset, pps_ts, -1, do_leap);
}
close(fd);
return 0;
}

-static int do_sysoff_loop(struct clock *clock, clockid_t src,
- struct timespec *interval, int n_readings)
+static int do_loop(struct node *node)
{
+ struct timespec interval;
+ struct clock *clock;
uint64_t ts;
int64_t offset, delay;
- int err = 0, fd = CLOCKID_TO_FD(src);
+ int src_fd = CLOCKID_TO_FD(node->master->clkid);
int do_leap;

- clock->source_label = "sys";
+ interval.tv_sec = node->phc_interval;
+ interval.tv_nsec = (node->phc_interval - interval.tv_sec) * 1e9;

while (1) {
- clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL);
- if (sysoff_measure(fd, n_readings, &offset, &ts, &delay)) {
- err = -1;
- break;
- }
- do_leap = update_sync_offset(clock);
- if (do_leap <= 0)
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL);
+ do_leap = update_sync_offset(node);
+ if (do_leap < 0)
continue;
- update_clock(clock, src, offset, ts, delay, do_leap);
- }
- return err;
-}
-
-static int do_phc_loop(struct clock *clock, clockid_t src,
- struct timespec *interval, int n_readings)
-{
- uint64_t ts;
- int64_t offset, delay;
- int do_leap;

- clock->source_label = "phc";
+ LIST_FOREACH(clock, &node->clocks, list) {
+ if (clock == node->master)
+ continue;

- while (1) {
- clock_nanosleep(CLOCK_MONOTONIC, 0, interval, NULL);
- if (!read_phc(src, clock->clkid, n_readings,
- &offset, &ts, &delay)) {
- continue;
+ if (clock->clkid == CLOCK_REALTIME &&
+ node->master->sysoff_supported) {
+ /* use sysoff */
+ if (sysoff_measure(src_fd, node->phc_readings,
+ &offset, &ts, &delay))
+ return -1;
+ } else {
+ /* use phc */
+ if (!read_phc(node->master->clkid, clock->clkid,
+ node->phc_readings,
+ &offset, &ts, &delay))
+ continue;
+ }
+ update_clock(node, clock, offset, ts, delay, do_leap);
}
- do_leap = update_sync_offset(clock);
- if (do_leap <= 0)
- continue;
- update_clock(clock, src, offset, ts, delay, do_leap);
}
- return 0;
+ return 0; /* unreachable */
}

static int is_msg_mgt(struct ptp_message *msg)
@@ -392,11 +399,11 @@ static void *get_mgt_data(struct ptp_message *msg)
return mgt->data;
}

-static int init_pmc(struct clock *clock, int domain_number)
+static int init_pmc(struct node *node, int domain_number)
{
- clock->pmc = pmc_create(TRANS_UDS, "/var/run/phc2sys", 0,
+ node->pmc = pmc_create(TRANS_UDS, "/var/run/phc2sys", 0,
domain_number, 0, 1);
- if (!clock->pmc) {
+ if (!node->pmc) {
pr_err("failed to create pmc");
return -1;
}
@@ -404,7 +411,7 @@ static int init_pmc(struct clock *clock, int domain_number)
return 0;
}

-static int run_pmc(struct clock *clock, int timeout, int ds_id,
+static int run_pmc(struct node *node, int timeout, int ds_id,
struct ptp_message **msg)
{
#define N_FD 1
@@ -412,9 +419,9 @@ static int run_pmc(struct clock *clock, int timeout, int ds_id,
int cnt;

while (1) {
- pollfd[0].fd = pmc_get_transport_fd(clock->pmc);
+ pollfd[0].fd = pmc_get_transport_fd(node->pmc);
pollfd[0].events = POLLIN|POLLPRI;
- if (!clock->pmc_ds_requested)
+ if (!node->pmc_ds_requested)
pollfd[0].events |= POLLOUT;

cnt = poll(pollfd, N_FD, timeout);
@@ -424,21 +431,21 @@ static int run_pmc(struct clock *clock, int timeout, int ds_id,
}
if (!cnt) {
/* Request the data set again in the next run. */
- clock->pmc_ds_requested = 0;
+ node->pmc_ds_requested = 0;
return 0;
}

/* Send a new request if there are no pending messages. */
if ((pollfd[0].revents & POLLOUT) &&
!(pollfd[0].revents & (POLLIN|POLLPRI))) {
- pmc_send_get_action(clock->pmc, ds_id);
- clock->pmc_ds_requested = 1;
+ pmc_send_get_action(node->pmc, ds_id);
+ node->pmc_ds_requested = 1;
}

if (!(pollfd[0].revents & (POLLIN|POLLPRI)))
continue;

- *msg = pmc_recv(clock->pmc);
+ *msg = pmc_recv(node->pmc);

if (!*msg)
continue;
@@ -449,12 +456,12 @@ static int run_pmc(struct clock *clock, int timeout, int ds_id,
*msg = NULL;
continue;
}
- clock->pmc_ds_requested = 0;
+ node->pmc_ds_requested = 0;
return 1;
}
}

-static int run_pmc_wait_sync(struct clock *clock, int timeout)
+static int run_pmc_wait_sync(struct node *node, int timeout)
{
struct ptp_message *msg;
int res;
@@ -462,7 +469,7 @@ static int run_pmc_wait_sync(struct clock *clock, int timeout)
Enumeration8 portState;

while (1) {
- res = run_pmc(clock, timeout, PORT_DATA_SET, &msg);
+ res = run_pmc(node, timeout, PORT_DATA_SET, &msg);
if (res <= 0)
return res;

@@ -476,42 +483,42 @@ static int run_pmc_wait_sync(struct clock *clock, int timeout)
return 1;
}
/* try to get more data sets (for other ports) */
- clock->pmc_ds_requested = 1;
+ node->pmc_ds_requested = 1;
}
}

-static int run_pmc_get_utc_offset(struct clock *clock, int timeout)
+static int run_pmc_get_utc_offset(struct node *node, int timeout)
{
struct ptp_message *msg;
int res;
struct timePropertiesDS *tds;

- res = run_pmc(clock, timeout, TIME_PROPERTIES_DATA_SET, &msg);
+ res = run_pmc(node, timeout, TIME_PROPERTIES_DATA_SET, &msg);
if (res <= 0)
return res;

tds = (struct timePropertiesDS *)get_mgt_data(msg);
if (tds->flags & PTP_TIMESCALE) {
- clock->sync_offset = tds->currentUtcOffset;
+ node->sync_offset = tds->currentUtcOffset;
if (tds->flags & LEAP_61)
- clock->leap = 1;
+ node->leap = 1;
else if (tds->flags & LEAP_59)
- clock->leap = -1;
+ node->leap = -1;
else
- clock->leap = 0;
+ node->leap = 0;
}
msg_put(msg);
return 1;
}

-static void close_pmc(struct clock *clock)
+static void close_pmc(struct node *node)
{
- pmc_destroy(clock->pmc);
- clock->pmc = NULL;
+ pmc_destroy(node->pmc);
+ node->pmc = NULL;
}

/* Returns: -1 in case of error, 0 for normal sync, 1 to leap clock */
-static int update_sync_offset(struct clock *clock)
+static int update_sync_offset(struct node *node)
{
struct timespec tp;
uint64_t ts;
@@ -523,40 +530,41 @@ static int update_sync_offset(struct clock *clock)
}
ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;

- if (clock->pmc &&
- !(ts > clock->pmc_last_update &&
- ts - clock->pmc_last_update < PMC_UPDATE_INTERVAL)) {
- if (run_pmc_get_utc_offset(clock, 0) > 0)
- clock->pmc_last_update = ts;
+ if (node->pmc &&
+ !(ts > node->pmc_last_update &&
+ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) {
+ if (run_pmc_get_utc_offset(node, 0) > 0)
+ node->pmc_last_update = ts;
}

/* Handle leap seconds. */

- if (!clock->leap && !clock->leap_set)
+ if (!node->leap && !node->leap_set)
return 0;

- clock_leap = leap_second_status(ts, clock->leap_set,
- &clock->leap, &clock->sync_offset);
- if (clock->leap_set != clock_leap) {
- clock->leap_set = clock_leap;
+ clock_leap = leap_second_status(ts, node->leap_set,
+ &node->leap, &node->sync_offset);
+ if (node->leap_set != clock_leap) {
+ node->leap_set = clock_leap;
return 1;
}
return 0;
}

/* Returns: non-zero to skip clock update */
-static int clock_handle_leap(struct clock *clock, clockid_t src,
+static int clock_handle_leap(struct node *node, struct clock *clock,
int64_t offset, uint64_t ts, int do_leap)
{
- if (!clock->leap && !do_leap)
+ if (!node->leap && !do_leap)
return 0;

- if (clock->clkid != CLOCK_REALTIME && src != CLOCK_REALTIME)
+ if (clock->clkid != CLOCK_REALTIME &&
+ node->master->clkid != CLOCK_REALTIME)
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 (src == CLOCK_REALTIME) {
+ if (node->master->clkid == CLOCK_REALTIME) {
struct timespec tp;
if (clock_gettime(CLOCK_REALTIME, &tp)) {
pr_err("failed to read clock: %m");
@@ -569,8 +577,8 @@ static int clock_handle_leap(struct clock *clock, clockid_t src,
target time. Ignore possible 1 second error in UTC offset. */
if (clock->clkid == CLOCK_REALTIME &&
clock->servo_state == SERVO_UNLOCKED) {
- ts -= offset + clock->sync_offset * NS_PER_SEC *
- clock->sync_offset_direction;
+ ts -= offset + node->sync_offset * NS_PER_SEC *
+ node->sync_offset_direction;
}

/* Suspend clock updates in the last second before midnight. */
@@ -581,13 +589,79 @@ static int clock_handle_leap(struct clock *clock, clockid_t src,

if (do_leap) {
/* Only the system clock can leap. */
- if (clock->clkid == CLOCK_REALTIME && clock->kernel_leap)
- sysclk_set_leap(clock->leap_set);
+ if (clock->clkid == CLOCK_REALTIME && node->kernel_leap)
+ sysclk_set_leap(node->leap_set);
}

return 0;
}

+static int clock_add(struct node *node, clockid_t clkid)
+{
+ struct clock *c;
+ int max_ppb;
+ double ppb;
+
+ c = calloc(1, sizeof(*c));
+ if (!c) {
+ pr_err("failed to allocate memory for a clock");
+ return -1;
+ }
+ c->clkid = clkid;
+ c->servo_state = SERVO_UNLOCKED;
+
+ if (c->clkid == CLOCK_REALTIME)
+ c->source_label = "sys";
+ else
+ c->source_label = "phc";
+
+ if (node->stats_max_count > 0) {
+ c->offset_stats = stats_create();
+ c->freq_stats = stats_create();
+ c->delay_stats = stats_create();
+ if (!c->offset_stats ||
+ !c->freq_stats ||
+ !c->delay_stats) {
+ pr_err("failed to create stats");
+ return -1;
+ }
+ }
+ if (node->sanity_freq_limit) {
+ c->sanity_check = clockcheck_create(node->sanity_freq_limit);
+ if (!c->sanity_check) {
+ pr_err("failed to create clock check");
+ return -1;
+ }
+ }
+
+ clockadj_init(c->clkid);
+ ppb = clockadj_get_freq(c->clkid);
+ /* The reading may silently fail and return 0, reset the frequency to
+ make sure ppb is the actual frequency of the clock. */
+ clockadj_set_freq(c->clkid, ppb);
+ if (c->clkid == CLOCK_REALTIME) {
+ sysclk_set_leap(0);
+ max_ppb = sysclk_max_freq();
+ } else {
+ max_ppb = phc_max_adj(c->clkid);
+ if (!max_ppb) {
+ pr_err("clock is not adjustable");
+ return -1;
+ }
+ }
+
+ c->servo = servo_create(node->servo_type, -ppb, max_ppb, 0);
+ servo_sync_interval(c->servo, node->phc_interval);
+
+ if (clkid != CLOCK_REALTIME)
+ c->sysoff_supported = (SYSOFF_SUPPORTED ==
+ sysoff_probe(CLOCKID_TO_FD(clkid),
+ node->phc_readings));
+
+ LIST_INSERT_HEAD(&node->clocks, c, list);
+ return 0;
+}
+
static void usage(char *progname)
{
fprintf(stderr,
@@ -622,16 +696,16 @@ int main(int argc, char *argv[])
{
char *progname;
clockid_t src = CLOCK_INVALID;
- int c, domain_number = 0, phc_readings = 5, pps_fd = -1;
- int max_ppb, r, wait_sync = 0, forced_sync_offset = 0;
+ clockid_t dst = CLOCK_REALTIME;
+ int c, domain_number = 0, pps_fd = -1;
+ int r, wait_sync = 0, forced_sync_offset = 0;
int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
- int sanity_freq_limit = 200000000;
- enum servo_type servo = CLOCK_SERVO_PI;
- double ppb, phc_interval = 1.0, phc_rate;
- struct timespec phc_interval_tp;
- struct clock dst_clock = {
- .clkid = CLOCK_REALTIME,
- .servo_state = SERVO_UNLOCKED,
+ double phc_rate;
+ struct node node = {
+ .sanity_freq_limit = 200000000,
+ .servo_type = CLOCK_SERVO_PI,
+ .phc_readings = 5,
+ .phc_interval = 1.0,
.kernel_leap = 1,
};

@@ -645,7 +719,7 @@ int main(int argc, char *argv[])
"c:d:s:E:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
switch (c) {
case 'c':
- dst_clock.clkid = clock_open(optarg);
+ dst = clock_open(optarg);
break;
case 'd':
pps_fd = open(optarg, O_RDONLY);
@@ -663,9 +737,9 @@ int main(int argc, char *argv[])
break;
case 'E':
if (!strcasecmp(optarg, "pi")) {
- servo = CLOCK_SERVO_PI;
+ node.servo_type = CLOCK_SERVO_PI;
} else if (!strcasecmp(optarg, "linreg")) {
- servo = CLOCK_SERVO_LINREG;
+ node.servo_type = CLOCK_SERVO_LINREG;
} else {
fprintf(stderr,
"invalid servo name %s\n", optarg);
@@ -695,25 +769,25 @@ int main(int argc, char *argv[])
case 'R':
if (get_arg_val_d(c, optarg, &phc_rate, 1e-9, DBL_MAX))
return -1;
- phc_interval = 1.0 / phc_rate;
+ node.phc_interval = 1.0 / phc_rate;
break;
case 'N':
- if (get_arg_val_i(c, optarg, &phc_readings, 1, INT_MAX))
+ if (get_arg_val_i(c, optarg, &node.phc_readings, 1, INT_MAX))
return -1;
break;
case 'O':
- if (get_arg_val_i(c, optarg, &dst_clock.sync_offset,
+ if (get_arg_val_i(c, optarg, &node.sync_offset,
INT_MIN, INT_MAX))
return -1;
- dst_clock.sync_offset_direction = -1;
+ node.sync_offset_direction = -1;
forced_sync_offset = 1;
break;
case 'L':
- if (get_arg_val_i(c, optarg, &sanity_freq_limit, 0, INT_MAX))
+ if (get_arg_val_i(c, optarg, &node.sanity_freq_limit, 0, INT_MAX))
return -1;
break;
case 'u':
- if (get_arg_val_ui(c, optarg, &dst_clock.stats_max_count,
+ if (get_arg_val_ui(c, optarg, &node.stats_max_count,
0, UINT_MAX))
return -1;
break;
@@ -725,7 +799,7 @@ int main(int argc, char *argv[])
return -1;
break;
case 'x':
- dst_clock.kernel_leap = 0;
+ node.kernel_leap = 0;
break;
case 'l':
if (get_arg_val_i(c, optarg, &print_level,
@@ -755,13 +829,13 @@ int main(int argc, char *argv[])
goto bad_usage;
}

- if (dst_clock.clkid == CLOCK_INVALID) {
+ if (dst == CLOCK_INVALID) {
fprintf(stderr,
"valid destination clock must be selected.\n");
goto bad_usage;
}

- if (pps_fd >= 0 && dst_clock.clkid != CLOCK_REALTIME) {
+ if (pps_fd >= 0 && dst != CLOCK_REALTIME) {
fprintf(stderr,
"cannot use a pps device unless destination is CLOCK_REALTIME\n");
goto bad_usage;
@@ -773,36 +847,21 @@ int main(int argc, char *argv[])
goto bad_usage;
}

- if (dst_clock.stats_max_count > 0) {
- dst_clock.offset_stats = stats_create();
- dst_clock.freq_stats = stats_create();
- dst_clock.delay_stats = stats_create();
- if (!dst_clock.offset_stats ||
- !dst_clock.freq_stats ||
- !dst_clock.delay_stats) {
- fprintf(stderr, "failed to create stats");
- return -1;
- }
- }
- if (sanity_freq_limit) {
- dst_clock.sanity_check = clockcheck_create(sanity_freq_limit);
- if (!dst_clock.sanity_check) {
- fprintf(stderr, "failed to create clock check");
- return -1;
- }
- }
-
print_set_progname(progname);
print_set_verbose(verbose);
print_set_syslog(use_syslog);
print_set_level(print_level);

+ clock_add(&node, src);
+ node.master = LIST_FIRST(&node.clocks);
+ clock_add(&node, dst);
+
if (wait_sync) {
- if (init_pmc(&dst_clock, domain_number))
+ if (init_pmc(&node, domain_number))
return -1;

while (1) {
- r = run_pmc_wait_sync(&dst_clock, 1000);
+ r = run_pmc_wait_sync(&node, 1000);
if (r < 0)
return -1;
if (r > 0)
@@ -812,60 +871,40 @@ int main(int argc, char *argv[])
}

if (!forced_sync_offset) {
- r = run_pmc_get_utc_offset(&dst_clock, 1000);
+ r = run_pmc_get_utc_offset(&node, 1000);
if (r <= 0) {
pr_err("failed to get UTC offset");
return -1;
}

if (src != CLOCK_REALTIME &&
- dst_clock.clkid == CLOCK_REALTIME)
- dst_clock.sync_offset_direction = 1;
+ dst == CLOCK_REALTIME)
+ node.sync_offset_direction = 1;
else if (src == CLOCK_REALTIME &&
- dst_clock.clkid != CLOCK_REALTIME)
- dst_clock.sync_offset_direction = -1;
+ dst != CLOCK_REALTIME)
+ node.sync_offset_direction = -1;
else
- dst_clock.sync_offset_direction = 0;
+ node.sync_offset_direction = 0;
}

- if (forced_sync_offset || !dst_clock.sync_offset_direction)
- close_pmc(&dst_clock);
+ if (forced_sync_offset || !node.sync_offset_direction)
+ close_pmc(&node);
}

- clockadj_init(dst_clock.clkid);
- ppb = clockadj_get_freq(dst_clock.clkid);
- /* The reading may silently fail and return 0, reset the frequency to
- make sure ppb is the actual frequency of the clock. */
- clockadj_set_freq(dst_clock.clkid, ppb);
- if (dst_clock.clkid == CLOCK_REALTIME) {
- sysclk_set_leap(0);
- max_ppb = sysclk_max_freq();
- } else {
- max_ppb = phc_max_adj(dst_clock.clkid);
- if (!max_ppb) {
- pr_err("clock is not adjustable");
- return -1;
- }
- }
-
- dst_clock.servo = servo_create(servo, -ppb, max_ppb, 0);
-
if (pps_fd >= 0) {
- servo_sync_interval(dst_clock.servo, 1.0);
- return do_pps_loop(&dst_clock, pps_fd, src, phc_readings);
- }
-
- servo_sync_interval(dst_clock.servo, phc_interval);
+ /* only one destination clock allowed with PPS until we
+ * implement a mean to specify PTP port to PPS mapping */
+ struct clock *dst_clock;

- phc_interval_tp.tv_sec = phc_interval;
- phc_interval_tp.tv_nsec = (phc_interval - phc_interval_tp.tv_sec) * 1e9;
-
- if (dst_clock.clkid == CLOCK_REALTIME && src != CLOCK_REALTIME &&
- SYSOFF_SUPPORTED == sysoff_probe(CLOCKID_TO_FD(src), phc_readings))
- return do_sysoff_loop(&dst_clock, src, &phc_interval_tp,
- phc_readings);
+ LIST_FOREACH(dst_clock, &node.clocks, list) {
+ if (dst_clock != node.master)
+ break;
+ }
+ servo_sync_interval(dst_clock->servo, 1.0);
+ return do_pps_loop(&node, dst_clock, pps_fd);
+ }

- return do_phc_loop(&dst_clock, src, &phc_interval_tp, phc_readings);
+ return do_loop(&node);

bad_usage:
usage(progname);
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:11 UTC
Permalink
Add tracking of which ports have been added and to which clock they belong.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 62e9b8c19e17..ece4560e0c67 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -75,6 +75,12 @@ struct clock {
struct clockcheck *sanity_check;
};

+struct port {
+ LIST_ENTRY(port) list;
+ unsigned int number;
+ struct clock *clock;
+};
+
struct node {
unsigned int stats_max_count;
int sanity_freq_limit;
@@ -89,6 +95,7 @@ struct node {
struct pmc *pmc;
int pmc_ds_requested;
uint64_t pmc_last_update;
+ LIST_HEAD(port_head, port) ports;
LIST_HEAD(clock_head, clock) clocks;
struct clock *master;
};
@@ -206,6 +213,50 @@ static struct clock *clock_add(struct node *node, char *device)
return c;
}

+static struct port *port_get(struct node *node, unsigned int number)
+{
+ struct port *p;
+
+ LIST_FOREACH(p, &node->ports, list) {
+ if (p->number == number)
+ return p;
+ }
+ return NULL;
+}
+
+static struct port *port_add(struct node *node, unsigned int number,
+ char *device)
+{
+ struct port *p;
+ struct clock *c = NULL, *tmp;
+
+ p = port_get(node, number);
+ if (p)
+ return p;
+ /* port is a new one, look whether we have the device already on
+ * a different port */
+ LIST_FOREACH(tmp, &node->clocks, list) {
+ if (!strcmp(tmp->device, device)) {
+ c = tmp;
+ break;
+ }
+ }
+ if (!c) {
+ c = clock_add(node, device);
+ if (!c)
+ return NULL;
+ }
+ p = malloc(sizeof(*p));
+ if (!p) {
+ pr_err("failed to allocate memory for a port");
+ return NULL;
+ }
+ p->number = number;
+ p->clock = c;
+ LIST_INSERT_HEAD(&node->ports, p, list);
+ return p;
+}
+
static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
int64_t *offset, uint64_t *ts, int64_t *delay)
{
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:12 UTC
Permalink
Implement pmc_target_port to set a port number, leaving clock identity
unchanged, and pmc_target_all to set clock identity and port number to all
1's.

Signed-off-by: Jiri Benc <***@redhat.com>
---
pmc_common.c | 12 +++++++++++-
pmc_common.h | 2 ++
2 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/pmc_common.c b/pmc_common.c
index 2c75074c8fb7..41385da45209 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -78,7 +78,7 @@ struct pmc *pmc_create(enum transport_type transport_type, const char *iface_nam
goto failed;
}
pmc->port_identity.portNumber = 1;
- memset(&pmc->target, 0xff, sizeof(pmc->target));
+ pmc_target_all(pmc);

pmc->boundary_hops = boundary_hops;
pmc->domain_number = domain_number;
@@ -325,3 +325,13 @@ int pmc_target(struct pmc *pmc, struct PortIdentity *pid)
pmc->target = *pid;
return 0;
}
+
+void pmc_target_port(struct pmc *pmc, UInteger16 portNumber)
+{
+ pmc->target.portNumber = portNumber;
+}
+
+void pmc_target_all(struct pmc *pmc)
+{
+ memset(&pmc->target, 0xff, sizeof(pmc->target));
+}
diff --git a/pmc_common.h b/pmc_common.h
index 9fcb51da3fd4..9adb9d1dd98b 100644
--- a/pmc_common.h
+++ b/pmc_common.h
@@ -41,5 +41,7 @@ int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize);
struct ptp_message *pmc_recv(struct pmc *pmc);

int pmc_target(struct pmc *pmc, struct PortIdentity *pid);
+void pmc_target_port(struct pmc *pmc, UInteger16 portNumber);
+void pmc_target_all(struct pmc *pmc);

#endif
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:08 UTC
Permalink
For now, only CLOCK_REALTIME can be UTC. This may stay this way forever but
now we have a clean separation between codepaths where CLOCK_REALTIME is
required and codepaths any UTC clock should take.

The main motiviation behind this change is removal of sync_offset_direction.
It has to be computed on the fly based on the source and destination when we
have multiple clocks supported and automatic following of ptp4l state
changes implemented.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 60 +++++++++++++++++++++++++++++-------------------------------
1 files changed, 29 insertions(+), 31 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 825d7328af15..6c86b4d9f028 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -130,6 +130,7 @@ struct clock {
LIST_ENTRY(clock) list;
clockid_t clkid;
int sysoff_supported;
+ int is_utc;
struct servo *servo;
enum servo_state servo_state;
const char *source_label;
@@ -146,7 +147,7 @@ struct node {
int phc_readings;
double phc_interval;
int sync_offset;
- int sync_offset_direction;
+ int forced_sync_offset;
int leap;
int leap_set;
int kernel_leap;
@@ -161,6 +162,15 @@ static int update_sync_offset(struct node *node);
static int clock_handle_leap(struct node *node, struct clock *clock,
int64_t offset, uint64_t ts, int do_leap);

+static int64_t get_sync_offset(struct node *node, struct clock *src)
+{
+ int direction = node->forced_sync_offset;
+
+ if (!direction)
+ direction = node->master->is_utc - src->is_utc;
+ return (int64_t)node->sync_offset * NS_PER_SEC * direction;
+}
+
static void update_clock_stats(struct clock *clock, unsigned int max_count,
int64_t offset, double freq, int64_t delay)
{
@@ -206,9 +216,7 @@ static void update_clock(struct node *node, struct clock *clock,
if (clock_handle_leap(node, clock, offset, ts, do_leap))
return;

- if (node->sync_offset_direction)
- offset += node->sync_offset * NS_PER_SEC *
- node->sync_offset_direction;
+ offset += get_sync_offset(node, clock);

if (clock->sanity_check && clockcheck_sample(clock->sanity_check, ts))
servo_reset(clock->servo);
@@ -290,7 +298,7 @@ static int do_pps_loop(struct node *node, struct clock *clock, int fd)

if (src == CLOCK_INVALID) {
/* The sync offset can't be applied with PPS alone. */
- node->sync_offset_direction = 0;
+ node->sync_offset = 0;
} else {
enable_pps_output(node->master->clkid);
}
@@ -558,15 +566,14 @@ static int clock_handle_leap(struct node *node, struct clock *clock,
if (!node->leap && !do_leap)
return 0;

- if (clock->clkid != CLOCK_REALTIME &&
- node->master->clkid != CLOCK_REALTIME)
+ 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->clkid == CLOCK_REALTIME) {
+ if (node->master->is_utc) {
struct timespec tp;
- if (clock_gettime(CLOCK_REALTIME, &tp)) {
+ if (clock_gettime(node->master->clkid, &tp)) {
pr_err("failed to read clock: %m");
return -1;
}
@@ -575,11 +582,8 @@ static int clock_handle_leap(struct node *node, struct clock *clock,

/* 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->clkid == CLOCK_REALTIME &&
- clock->servo_state == SERVO_UNLOCKED) {
- ts -= offset + node->sync_offset * NS_PER_SEC *
- node->sync_offset_direction;
- }
+ if (clock->is_utc && clock->servo_state == SERVO_UNLOCKED)
+ ts -= get_sync_offset(node, clock);

/* Suspend clock updates in the last second before midnight. */
if (is_utc_ambiguous(ts)) {
@@ -610,10 +614,12 @@ static int clock_add(struct node *node, clockid_t clkid)
c->clkid = clkid;
c->servo_state = SERVO_UNLOCKED;

- if (c->clkid == CLOCK_REALTIME)
+ if (c->clkid == CLOCK_REALTIME) {
c->source_label = "sys";
- else
+ c->is_utc = 1;
+ } else {
c->source_label = "phc";
+ }

if (node->stats_max_count > 0) {
c->offset_stats = stats_create();
@@ -698,7 +704,7 @@ int main(int argc, char *argv[])
clockid_t src = CLOCK_INVALID;
clockid_t dst = CLOCK_REALTIME;
int c, domain_number = 0, pps_fd = -1;
- int r, wait_sync = 0, forced_sync_offset = 0;
+ int r, wait_sync = 0;
int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
double phc_rate;
struct node node = {
@@ -779,8 +785,7 @@ int main(int argc, char *argv[])
if (get_arg_val_i(c, optarg, &node.sync_offset,
INT_MIN, INT_MAX))
return -1;
- node.sync_offset_direction = -1;
- forced_sync_offset = 1;
+ node.forced_sync_offset = -1;
break;
case 'L':
if (get_arg_val_i(c, optarg, &node.sanity_freq_limit, 0, INT_MAX))
@@ -841,7 +846,7 @@ int main(int argc, char *argv[])
goto bad_usage;
}

- if (!wait_sync && !forced_sync_offset) {
+ if (!wait_sync && !node.forced_sync_offset) {
fprintf(stderr,
"time offset must be specified using -w or -O\n");
goto bad_usage;
@@ -870,24 +875,17 @@ int main(int argc, char *argv[])
pr_notice("Waiting for ptp4l...");
}

- if (!forced_sync_offset) {
+ if (!node.forced_sync_offset) {
r = run_pmc_get_utc_offset(&node, 1000);
if (r <= 0) {
pr_err("failed to get UTC offset");
return -1;
}
-
- if (src != CLOCK_REALTIME &&
- dst == CLOCK_REALTIME)
- node.sync_offset_direction = 1;
- else if (src == CLOCK_REALTIME &&
- dst != CLOCK_REALTIME)
- node.sync_offset_direction = -1;
- else
- node.sync_offset_direction = 0;
}

- if (forced_sync_offset || !node.sync_offset_direction)
+ if (node.forced_sync_offset ||
+ (src != CLOCK_REALTIME && dst != CLOCK_REALTIME) ||
+ src == CLOCK_INVALID)
close_pmc(&node);
}
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:15 UTC
Permalink
Add automatic configuration option (-a).

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 250 insertions(+), 15 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 731c2bb1af35..d102ca8e8d93 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -22,6 +22,7 @@
#include <float.h>
#include <inttypes.h>
#include <limits.h>
+#include <net/if.h>
#include <poll.h>
#include <stdint.h>
#include <stdio.h>
@@ -105,9 +106,11 @@ struct node {
struct clock *master;
};

-static int update_sync_offset(struct node *node);
+static int update_pmc(struct node *node, int subscribe);
static int clock_handle_leap(struct node *node, struct clock *clock,
int64_t offset, uint64_t ts, int do_leap);
+static int run_pmc_get_utc_offset(struct node *node, int timeout);
+static void run_pmc_events(struct node *node);

static clockid_t clock_open(char *device)
{
@@ -262,6 +265,78 @@ static struct port *port_add(struct node *node, unsigned int number,
return p;
}

+static void clock_reinit(struct clock *clock)
+{
+ servo_reset(clock->servo);
+ clock->servo_state = SERVO_UNLOCKED;
+
+ if (clock->offset_stats) {
+ stats_reset(clock->offset_stats);
+ stats_reset(clock->freq_stats);
+ stats_reset(clock->delay_stats);
+ }
+}
+
+static void reconfigure(struct node *node)
+{
+ struct clock *c, *rt, *src;
+ int src_cnt = 0, dst_cnt = 0;
+
+ pr_info("reconfiguring after port state change");
+ node->state_changed = 0;
+
+ src = rt = NULL;
+ LIST_FOREACH(c, &node->clocks, list) {
+ if (c->clkid == CLOCK_REALTIME) {
+ rt = c;
+ continue;
+ }
+
+ if (c->new_state == PS_MASTER)
+ clock_reinit(c);
+
+ c->state = c->new_state;
+ c->new_state = 0;
+
+ if (c->state == PS_SLAVE) {
+ src = c;
+ src_cnt++;
+ } else if (c->state == PS_UNCALIBRATED) {
+ src_cnt++;
+ } else if (c->state == PS_MASTER) {
+ pr_info("selecting %s for synchronization", c->device);
+ dst_cnt++;
+ }
+ }
+ if (src_cnt > 1) {
+ pr_info("multiple master clocks available, postponing sync...");
+ node->master = NULL;
+ return;
+ }
+ if (src_cnt > 0 && !src) {
+ pr_info("master clock not ready, waiting...");
+ node->master = NULL;
+ return;
+ }
+ if (!src_cnt && !dst_cnt) {
+ pr_info("no PHC ready, waiting...");
+ node->master = NULL;
+ return;
+ }
+ if (!src_cnt) {
+ src = rt;
+ rt->state = PS_SLAVE;
+ } else {
+ if (rt->state != PS_MASTER) {
+ rt->state = PS_MASTER;
+ clock_reinit(rt);
+ }
+ pr_info("selecting %s for synchronization", rt->device);
+ }
+ node->master = src;
+ pr_info("selecting %s as the master clock", src->device);
+}
+
static int read_phc(clockid_t clkid, clockid_t sysclk, int readings,
int64_t *offset, uint64_t *ts, int64_t *delay)
{
@@ -460,7 +535,7 @@ static int do_pps_loop(struct node *node, struct clock *clock, int fd)
pps_offset = pps_ts - phc_ts;
}

- do_leap = update_sync_offset(node);
+ do_leap = update_pmc(node, 0);
if (do_leap < 0)
continue;
update_clock(node, clock, pps_offset, pps_ts, -1, do_leap);
@@ -469,13 +544,12 @@ static int do_pps_loop(struct node *node, struct clock *clock, int fd)
return 0;
}

-static int do_loop(struct node *node)
+static int do_loop(struct node *node, int subscriptions)
{
struct timespec interval;
struct clock *clock;
uint64_t ts;
int64_t offset, delay;
- int src_fd = CLOCKID_TO_FD(node->master->clkid);
int do_leap;

interval.tv_sec = node->phc_interval;
@@ -483,18 +557,34 @@ static int do_loop(struct node *node)

while (1) {
clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL);
- do_leap = update_sync_offset(node);
+ do_leap = update_pmc(node, 0);
if (do_leap < 0)
continue;

+ if (subscriptions) {
+ run_pmc_events(node);
+ if (node->state_changed) {
+ /* force getting offset, as it may have
+ * changed after the port state change */
+ if (run_pmc_get_utc_offset(node, 1000) <= 0) {
+ pr_err("failed to get UTC offset");
+ continue;
+ }
+ reconfigure(node);
+ }
+ }
+ if (!node->master)
+ continue;
+
LIST_FOREACH(clock, &node->clocks, list) {
- if (clock == node->master)
+ if (clock->state != PS_MASTER)
continue;

if (clock->clkid == CLOCK_REALTIME &&
node->master->sysoff_supported) {
/* use sysoff */
- if (sysoff_measure(src_fd, node->phc_readings,
+ if (sysoff_measure(CLOCKID_TO_FD(node->master->clkid),
+ node->phc_readings,
&offset, &ts, &delay))
return -1;
} else {
@@ -753,6 +843,22 @@ static int run_pmc_get_utc_offset(struct node *node, int timeout)
return 1;
}

+static int run_pmc_get_number_ports(struct node *node, int timeout)
+{
+ struct ptp_message *msg;
+ int res;
+ struct defaultDS *dds;
+
+ res = run_pmc(node, timeout, DEFAULT_DATA_SET, &msg);
+ if (res <= 0)
+ return res;
+
+ dds = (struct defaultDS *)get_mgt_data(msg);
+ res = dds->numberPorts;
+ msg_put(msg);
+ return res;
+}
+
static int run_pmc_subscribe(struct node *node, int timeout)
{
struct ptp_message *msg;
@@ -772,14 +878,117 @@ static void run_pmc_events(struct node *node)
run_pmc(node, 0, -1, &msg);
}

+static int run_pmc_port_properties(struct node *node, int timeout,
+ unsigned int port,
+ int *state, int *tstamping, char *iface)
+{
+ struct ptp_message *msg;
+ int res, len;
+ struct port_properties_np *ppn;
+
+ pmc_target_port(node->pmc, port);
+ while (1) {
+ res = run_pmc(node, timeout, PORT_PROPERTIES_NP, &msg);
+ if (res <= 0)
+ goto out;
+
+ ppn = get_mgt_data(msg);
+ if (ppn->portIdentity.portNumber != port) {
+ msg_put(msg);
+ continue;
+ }
+
+ *state = ppn->port_state;
+ *tstamping = ppn->timestamping;
+ len = ppn->interface.length;
+ if (len > IFNAMSIZ - 1)
+ len = IFNAMSIZ - 1;
+ memcpy(iface, ppn->interface.text, len);
+ iface[len] = '\0';
+
+ msg_put(msg);
+ res = 1;
+ break;
+ }
+out:
+ pmc_target_all(node->pmc);
+ return res;
+}
+
static void close_pmc(struct node *node)
{
pmc_destroy(node->pmc);
node->pmc = NULL;
}

+static int auto_init_ports(struct node *node)
+{
+ struct port *port;
+ struct clock *clock;
+ int number_ports, res;
+ unsigned int i;
+ int state, timestamping;
+ char iface[IFNAMSIZ];
+
+ while (1) {
+ res = run_pmc_get_number_ports(node, 1000);
+ if (res < 0)
+ return -1;
+ if (res > 0)
+ break;
+ /* res == 0, timeout */
+ pr_notice("Waiting for ptp4l...");
+ }
+ number_ports = res;
+
+ res = run_pmc_subscribe(node, 1000);
+ if (res <= 0) {
+ pr_err("failed to subscribe");
+ return -1;
+ }
+
+ for (i = 1; i <= number_ports; i++) {
+ res = run_pmc_port_properties(node, 1000, i, &state,
+ &timestamping, iface);
+ if (res == -1) {
+ /* port does not exist, ignore the port */
+ continue;
+ }
+ if (res <= 0) {
+ pr_err("failed to get port properties");
+ return -1;
+ }
+ if (timestamping == TS_SOFTWARE) {
+ /* ignore ports with software time stamping */
+ continue;
+ }
+ port = port_add(node, i, iface);
+ if (!port)
+ return -1;
+ port->state = normalize_state(state);
+ }
+ if (LIST_EMPTY(&node->clocks)) {
+ pr_err("no suitable ports available");
+ return -1;
+ }
+ LIST_FOREACH(clock, &node->clocks, list) {
+ clock->new_state = clock_compute_state(node, clock);
+ }
+ node->state_changed = 1;
+
+ if (!clock_add(node, "CLOCK_REALTIME"))
+ return -1;
+
+ /* get initial offset */
+ if (run_pmc_get_utc_offset(node, 1000) <= 0) {
+ pr_err("failed to get UTC offset");
+ return -1;
+ }
+ return 0;
+}
+
/* Returns: -1 in case of error, 0 for normal sync, 1 to leap clock */
-static int update_sync_offset(struct node *node)
+static int update_pmc(struct node *node, int subscribe)
{
struct timespec tp;
uint64_t ts;
@@ -794,6 +1003,8 @@ static int update_sync_offset(struct node *node)
if (node->pmc &&
!(ts > node->pmc_last_update &&
ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) {
+ if (subscribe)
+ run_pmc_subscribe(node, 0);
if (run_pmc_get_utc_offset(node, 0) > 0)
node->pmc_last_update = ts;
}
@@ -858,9 +1069,16 @@ static void usage(char *progname)
fprintf(stderr,
"\n"
"usage: %s [options]\n\n"
+ "\n"
+ " automatic configuration:\n"
+ " -a turn on autoconfiguration\n"
+ " manual configuration:\n"
" -c [dev|name] slave clock (CLOCK_REALTIME)\n"
" -d [dev] master PPS device\n"
" -s [dev|name] master clock\n"
+ " -O [offset] slave-master time offset (0)\n"
+ " -w wait for ptp4l\n"
+ " common options:\n"
" -E [pi|linreg] clock servo (pi)\n"
" -P [kp] proportional constant (0.7)\n"
" -I [ki] integration constant (0.3)\n"
@@ -868,10 +1086,8 @@ static void usage(char *progname)
" -F [step] step threshold only on start (0.00002)\n"
" -R [rate] slave clock update rate in HZ (1.0)\n"
" -N [num] number of master clock readings per update (5)\n"
- " -O [offset] slave-master time offset (0)\n"
" -L [limit] sanity frequency limit in ppb (200000000)\n"
" -u [num] number of clock updates in summary stats (0)\n"
- " -w wait for ptp4l\n"
" -n [num] domain number (0)\n"
" -x apply leap seconds by servo instead of kernel\n"
" -l [num] set the logging level to 'num' (6)\n"
@@ -888,6 +1104,7 @@ int main(int argc, char *argv[])
char *progname;
char *src_name = NULL, *dst_name = NULL;
struct clock *src, *dst;
+ int autocfg = 0;
int c, domain_number = 0, pps_fd = -1;
int r, wait_sync = 0;
int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
@@ -907,8 +1124,11 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
while (EOF != (c = getopt(argc, argv,
- "c:d:s:E:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
+ "ac:d:s:E:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
switch (c) {
+ case 'a':
+ autocfg = 1;
+ break;
case 'c':
dst_name = strdup(optarg);
break;
@@ -1013,13 +1233,18 @@ int main(int argc, char *argv[])
}
}

- if (pps_fd < 0 && !src_name) {
+ if (autocfg && (src_name || dst_name || pps_fd >= 0 || wait_sync || node.forced_sync_offset)) {
fprintf(stderr,
- "valid source clock must be selected.\n");
+ "autoconfiguration cannot be mixed with manual config options.\n");
+ goto bad_usage;
+ }
+ if (!autocfg && pps_fd < 0 && !src_name) {
+ fprintf(stderr,
+ "autoconfiguration or valid source clock must be selected.\n");
goto bad_usage;
}

- if (!wait_sync && !node.forced_sync_offset) {
+ if (!autocfg && !wait_sync && !node.forced_sync_offset) {
fprintf(stderr,
"time offset must be specified using -w or -O\n");
goto bad_usage;
@@ -1030,10 +1255,20 @@ int main(int argc, char *argv[])
print_set_syslog(use_syslog);
print_set_level(print_level);

+ if (autocfg) {
+ if (init_pmc(&node, domain_number))
+ return -1;
+ if (auto_init_ports(&node) < 0)
+ return -1;
+ return do_loop(&node, 1);
+ }
+
src = clock_add(&node, src_name);
+ src->state = PS_SLAVE;
free(src_name);
node.master = src;
dst = clock_add(&node, dst_name ? dst_name : "CLOCK_REALTIME");
+ dst->state = PS_MASTER;
free(dst_name);

if (!dst) {
@@ -1089,7 +1324,7 @@ int main(int argc, char *argv[])
return do_pps_loop(&node, dst, pps_fd);
}

- return do_loop(&node);
+ return do_loop(&node, 0);

bad_usage:
usage(progname);
--
1.7.6.5
Miroslav Lichvar
2014-05-12 15:25:51 UTC
Permalink
Post by Jiri Benc
Add automatic configuration option (-a).
This seems to be working, but only when phc2sys is running for short
time. If it runs for more than few minutes, it seems to stop reacting
to port state changes.
Post by Jiri Benc
---
phc2sys.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 250 insertions(+), 15 deletions(-)
+static int do_loop(struct node *node, int subscriptions)
@@ -483,18 +557,34 @@ static int do_loop(struct node *node)
while (1) {
clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL);
- do_leap = update_sync_offset(node);
+ do_leap = update_pmc(node, 0);
Shouldn't the second parameter be "subscriptions > 0"?

Also, are you planning to implement some check that would abort/reinit
phc2sys when ptp4l is killed (update_pmc fails to update the UTC
offset or the resubscription fails)? I'm worried it's too easy to get
into a state where phc2sys is not receiving updates and the user is
wondering why it's not working as expected.

Thanks,
--
Miroslav Lichvar
Jiri Benc
2014-05-14 13:25:45 UTC
Permalink
Post by Miroslav Lichvar
Post by Jiri Benc
+ do_leap = update_pmc(node, 0);
Shouldn't the second parameter be "subscriptions > 0"?
Yes. Seems it got incorrect during rework of the "Subscription time
limit" patch and apparently I haven't tested the new version long
enough (unlike the original version).

I'll fix it, thanks for catching this.
Post by Miroslav Lichvar
Also, are you planning to implement some check that would abort/reinit
phc2sys when ptp4l is killed (update_pmc fails to update the UTC
offset or the resubscription fails)? I'm worried it's too easy to get
into a state where phc2sys is not receiving updates and the user is
wondering why it's not working as expected.
Yes. I have it on a todo list but as the problem exists with the
current code, too (albeit it's much less visible) I don't consider it
to be part of this set.

Jiri
--
Jiri Benc
Jiri Benc
2014-05-07 16:23:16 UTC
Permalink
By default, do not synchronize CLOCK_REALTIME. To do it, -r option is
needed. That will only consider CLOCK_REALTIME as the destination. To
consider it also as a possible time source, use -rr.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 31 ++++++++++++++++++++++++-------
1 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index d102ca8e8d93..29916a59f2a5 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -67,6 +67,7 @@ struct clock {
clockid_t clkid;
int sysoff_supported;
int is_utc;
+ int dest_only;
int state;
int new_state;
struct servo *servo;
@@ -323,10 +324,16 @@ static void reconfigure(struct node *node)
node->master = NULL;
return;
}
+ if ((!src_cnt && (!rt || rt->dest_only)) ||
+ (!dst_cnt && !rt)) {
+ pr_info("nothing to synchronize");
+ node->master = NULL;
+ return;
+ }
if (!src_cnt) {
src = rt;
rt->state = PS_SLAVE;
- } else {
+ } else if (rt) {
if (rt->state != PS_MASTER) {
rt->state = PS_MASTER;
clock_reinit(rt);
@@ -921,7 +928,7 @@ static void close_pmc(struct node *node)
node->pmc = NULL;
}

-static int auto_init_ports(struct node *node)
+static int auto_init_ports(struct node *node, int add_rt)
{
struct port *port;
struct clock *clock;
@@ -976,8 +983,13 @@ static int auto_init_ports(struct node *node)
}
node->state_changed = 1;

- if (!clock_add(node, "CLOCK_REALTIME"))
- return -1;
+ if (add_rt) {
+ clock = clock_add(node, "CLOCK_REALTIME");
+ if (!clock)
+ return -1;
+ if (add_rt == 1)
+ clock->dest_only = 1;
+ }

/* get initial offset */
if (run_pmc_get_utc_offset(node, 1000) <= 0) {
@@ -1072,6 +1084,8 @@ static void usage(char *progname)
"\n"
" automatic configuration:\n"
" -a turn on autoconfiguration\n"
+ " -r synchronize system (realtime) clock\n"
+ " repeat -r to consider it also as a time source\n"
" manual configuration:\n"
" -c [dev|name] slave clock (CLOCK_REALTIME)\n"
" -d [dev] master PPS device\n"
@@ -1104,7 +1118,7 @@ int main(int argc, char *argv[])
char *progname;
char *src_name = NULL, *dst_name = NULL;
struct clock *src, *dst;
- int autocfg = 0;
+ int autocfg = 0, rt = 0;
int c, domain_number = 0, pps_fd = -1;
int r, wait_sync = 0;
int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
@@ -1124,11 +1138,14 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
while (EOF != (c = getopt(argc, argv,
- "ac:d:s:E:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
+ "arc:d:s:E:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
switch (c) {
case 'a':
autocfg = 1;
break;
+ case 'r':
+ rt++;
+ break;
case 'c':
dst_name = strdup(optarg);
break;
@@ -1258,7 +1275,7 @@ int main(int argc, char *argv[])
if (autocfg) {
if (init_pmc(&node, domain_number))
return -1;
- if (auto_init_ports(&node) < 0)
+ if (auto_init_ports(&node, rt) < 0)
return -1;
return do_loop(&node, 1);
}
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:14 UTC
Permalink
Recognize errors returned in MANAGEMENT_ERROR_STATUS TLV and return
a distinct value from run_pmc in case such error is received.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 34 +++++++++++++++++++++++++++-------
1 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index f7af1b26451b..731c2bb1af35 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -521,9 +521,11 @@ static int is_msg_mgt(struct ptp_message *msg)
if (msg->tlv_count != 1)
return 0;
tlv = (struct TLV *) msg->management.suffix;
- if (tlv->type != TLV_MANAGEMENT)
- return 0;
- return 1;
+ if (tlv->type == TLV_MANAGEMENT)
+ return 1;
+ if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS)
+ return -1;
+ return 0;
}

static int get_mgt_id(struct ptp_message *msg)
@@ -538,6 +540,14 @@ static void *get_mgt_data(struct ptp_message *msg)
return mgt->data;
}

+static int get_mgt_err_id(struct ptp_message *msg)
+{
+ struct management_error_status *mgt;
+
+ mgt = (struct management_error_status *)msg->management.suffix;
+ return mgt->id;
+}
+
static int normalize_state(int state)
{
if (state != PS_MASTER && state != PS_SLAVE &&
@@ -625,12 +635,18 @@ static int init_pmc(struct node *node, int domain_number)
return 0;
}

+/* Return values:
+ * 1: success
+ * 0: timeout
+ * -1: error reported by the other side
+ * -2: local error, fatal
+ */
static int run_pmc(struct node *node, int timeout, int ds_id,
struct ptp_message **msg)
{
#define N_FD 1
struct pollfd pollfd[N_FD];
- int cnt;
+ int cnt, res;

while (1) {
pollfd[0].fd = pmc_get_transport_fd(node->pmc);
@@ -641,7 +657,7 @@ static int run_pmc(struct node *node, int timeout, int ds_id,
cnt = poll(pollfd, N_FD, timeout);
if (cnt < 0) {
pr_err("poll failed");
- return -1;
+ return -2;
}
if (!cnt) {
/* Request the data set again in the next run. */
@@ -671,8 +687,12 @@ static int run_pmc(struct node *node, int timeout, int ds_id,
if (!*msg)
continue;

- if (!is_msg_mgt(*msg) ||
- recv_subscribed(node, *msg, ds_id) ||
+ res = is_msg_mgt(*msg);
+ if (res < 0 && get_mgt_err_id(*msg) == ds_id) {
+ node->pmc_ds_requested = 0;
+ return -1;
+ }
+ if (res <= 0 || recv_subscribed(node, *msg, ds_id) ||
get_mgt_id(*msg) != ds_id) {
msg_put(*msg);
*msg = NULL;
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:13 UTC
Permalink
Add support for subscribing to events (run_pmc_subscribe) and receiving and
handling of received events (run_pmc_events).

Add initial support for port status changes.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index ece4560e0c67..f7af1b26451b 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -41,6 +41,7 @@
#include "ds.h"
#include "fsm.h"
#include "missing.h"
+#include "notification.h"
#include "phc.h"
#include "pi.h"
#include "pmc_common.h"
@@ -65,6 +66,8 @@ struct clock {
clockid_t clkid;
int sysoff_supported;
int is_utc;
+ int state;
+ int new_state;
struct servo *servo;
enum servo_state servo_state;
char *device;
@@ -78,6 +81,7 @@ struct clock {
struct port {
LIST_ENTRY(port) list;
unsigned int number;
+ int state;
struct clock *clock;
};

@@ -95,6 +99,7 @@ struct node {
struct pmc *pmc;
int pmc_ds_requested;
uint64_t pmc_last_update;
+ int state_changed;
LIST_HEAD(port_head, port) ports;
LIST_HEAD(clock_head, clock) clocks;
struct clock *master;
@@ -533,6 +538,81 @@ static void *get_mgt_data(struct ptp_message *msg)
return mgt->data;
}

+static int normalize_state(int state)
+{
+ if (state != PS_MASTER && state != PS_SLAVE &&
+ state != PS_PRE_MASTER && state != PS_UNCALIBRATED) {
+ /* treat any other state as "not a master nor a slave" */
+ state = PS_DISABLED;
+ }
+ return state;
+}
+
+static int clock_compute_state(struct node *node, struct clock *clock)
+{
+ struct port *p;
+ int state = PS_DISABLED;
+
+ LIST_FOREACH(p, &node->ports, list) {
+ if (p->clock != clock)
+ continue;
+ /* PS_SLAVE takes the highest precedence, PS_UNCALIBRATED
+ * after that, PS_MASTER is third, PS_PRE_MASTER fourth and
+ * all of that overrides PS_DISABLED, which corresponds
+ * nicely with the numerical values */
+ if (p->state > state)
+ state = p->state;
+ }
+ return state;
+}
+
+static int recv_subscribed(struct node *node, struct ptp_message *msg,
+ int excluded)
+{
+ int mgt_id, state;
+ struct portDS *pds;
+ struct port *port;
+ struct clock *clock;
+
+ mgt_id = get_mgt_id(msg);
+ if (mgt_id == excluded)
+ return 0;
+ switch (mgt_id) {
+ case PORT_DATA_SET:
+ pds = get_mgt_data(msg);
+ port = port_get(node, pds->portIdentity.portNumber);
+ if (!port) {
+ pr_info("received data for unknown port %s",
+ pid2str(&pds->portIdentity));
+ return 1;
+ }
+ state = normalize_state(pds->portState);
+ if (port->state != state) {
+ pr_info("port %s changed state",
+ pid2str(&pds->portIdentity));
+ port->state = state;
+ clock = port->clock;
+ state = clock_compute_state(node, clock);
+ if (clock->state != state) {
+ clock->new_state = state;
+ node->state_changed = 1;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static void send_subscription(struct node *node)
+{
+ struct subscribe_events_np sen;
+
+ memset(&sen, 0, sizeof(sen));
+ sen.duration = 180; /* 3 minutes */
+ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE;
+ pmc_send_set_action(node->pmc, SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen));
+}
+
static int init_pmc(struct node *node, int domain_number)
{
node->pmc = pmc_create(TRANS_UDS, "/var/run/phc2sys", 0,
@@ -555,7 +635,7 @@ static int run_pmc(struct node *node, int timeout, int ds_id,
while (1) {
pollfd[0].fd = pmc_get_transport_fd(node->pmc);
pollfd[0].events = POLLIN|POLLPRI;
- if (!node->pmc_ds_requested)
+ if (!node->pmc_ds_requested && ds_id >= 0)
pollfd[0].events |= POLLOUT;

cnt = poll(pollfd, N_FD, timeout);
@@ -572,7 +652,14 @@ static int run_pmc(struct node *node, int timeout, int ds_id,
/* Send a new request if there are no pending messages. */
if ((pollfd[0].revents & POLLOUT) &&
!(pollfd[0].revents & (POLLIN|POLLPRI))) {
- pmc_send_get_action(node->pmc, ds_id);
+ switch (ds_id) {
+ case SUBSCRIBE_EVENTS_NP:
+ send_subscription(node);
+ break;
+ default:
+ pmc_send_get_action(node->pmc, ds_id);
+ break;
+ }
node->pmc_ds_requested = 1;
}

@@ -585,6 +672,7 @@ static int run_pmc(struct node *node, int timeout, int ds_id,
continue;

if (!is_msg_mgt(*msg) ||
+ recv_subscribed(node, *msg, ds_id) ||
get_mgt_id(*msg) != ds_id) {
msg_put(*msg);
*msg = NULL;
@@ -645,6 +733,25 @@ static int run_pmc_get_utc_offset(struct node *node, int timeout)
return 1;
}

+static int run_pmc_subscribe(struct node *node, int timeout)
+{
+ struct ptp_message *msg;
+ int res;
+
+ res = run_pmc(node, timeout, SUBSCRIBE_EVENTS_NP, &msg);
+ if (res <= 0)
+ return res;
+ msg_put(msg);
+ return 1;
+}
+
+static void run_pmc_events(struct node *node)
+{
+ struct ptp_message *msg;
+
+ run_pmc(node, 0, -1, &msg);
+}
+
static void close_pmc(struct node *node)
{
pmc_destroy(node->pmc);
--
1.7.6.5
Miroslav Lichvar
2014-05-12 15:35:18 UTC
Permalink
Post by Jiri Benc
Add support for subscribing to events (run_pmc_subscribe) and receiving and
handling of received events (run_pmc_events).
Add initial support for port status changes.
---
phc2sys.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/phc2sys.c b/phc2sys.c
+static void send_subscription(struct node *node)
+{
+ struct subscribe_events_np sen;
+
+ memset(&sen, 0, sizeof(sen));
+ sen.duration = 180; /* 3 minutes */
+ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE;
+ pmc_send_set_action(node->pmc, SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen));
+}
One more comment, could you please define the 180 second constant as a
macro near PMC_UPDATE_INTERVAL so it's more clear the two are related
and changing one without other could break things.

Thanks,
--
Miroslav Lichvar
Jiri Benc
2014-05-14 13:34:55 UTC
Permalink
Post by Miroslav Lichvar
One more comment, could you please define the 180 second constant as a
macro near PMC_UPDATE_INTERVAL so it's more clear the two are related
and changing one without other could break things.
Good idea.

Thanks,

Jiri
--
Jiri Benc
Jiri Benc
2014-05-07 16:23:17 UTC
Permalink
Make sure that we handle only one PTP clock (node). This is for an extra
safety.

Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/phc2sys.c b/phc2sys.c
index 29916a59f2a5..d71422047df2 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -102,6 +102,8 @@ struct node {
int pmc_ds_requested;
uint64_t pmc_last_update;
int state_changed;
+ int clock_identity_set;
+ struct ClockIdentity clock_identity;
LIST_HEAD(port_head, port) ports;
LIST_HEAD(clock_head, clock) clocks;
struct clock *master;
@@ -607,6 +609,15 @@ static int do_loop(struct node *node, int subscriptions)
return 0; /* unreachable */
}

+static int check_clock_identity(struct node *node, struct ptp_message *msg)
+{
+ if (!node->clock_identity_set)
+ return 1;
+ return !memcmp(&node->clock_identity,
+ &msg->header.sourcePortIdentity.clockIdentity,
+ sizeof(struct ClockIdentity));
+}
+
static int is_msg_mgt(struct ptp_message *msg)
{
struct TLV *tlv;
@@ -784,6 +795,12 @@ static int run_pmc(struct node *node, int timeout, int ds_id,
if (!*msg)
continue;

+ if (!check_clock_identity(node, *msg)) {
+ msg_put(*msg);
+ *msg = NULL;
+ continue;
+ }
+
res = is_msg_mgt(*msg);
if (res < 0 && get_mgt_err_id(*msg) == ds_id) {
node->pmc_ds_requested = 0;
@@ -922,6 +939,24 @@ out:
return res;
}

+static int run_pmc_clock_identity(struct node *node, int timeout)
+{
+ struct ptp_message *msg;
+ struct defaultDS *dds;
+ int res;
+
+ res = run_pmc(node, timeout, DEFAULT_DATA_SET, &msg);
+ if (res <= 0)
+ return res;
+
+ dds = (struct defaultDS *)get_mgt_data(msg);
+ memcpy(&node->clock_identity, &dds->clockIdentity,
+ sizeof(struct ClockIdentity));
+ node->clock_identity_set = 1;
+ msg_put(msg);
+ return 1;
+}
+
static void close_pmc(struct node *node)
{
pmc_destroy(node->pmc);
@@ -938,7 +973,7 @@ static int auto_init_ports(struct node *node, int add_rt)
char iface[IFNAMSIZ];

while (1) {
- res = run_pmc_get_number_ports(node, 1000);
+ res = run_pmc_clock_identity(node, 1000);
if (res < 0)
return -1;
if (res > 0)
@@ -946,7 +981,12 @@ static int auto_init_ports(struct node *node, int add_rt)
/* res == 0, timeout */
pr_notice("Waiting for ptp4l...");
}
- number_ports = res;
+
+ number_ports = run_pmc_get_number_ports(node, 1000);
+ if (number_ports <= 0) {
+ pr_err("failed to get number of ports");
+ return -1;
+ }

res = run_pmc_subscribe(node, 1000);
if (res <= 0) {
--
1.7.6.5
Jiri Benc
2014-05-07 16:23:18 UTC
Permalink
Signed-off-by: Jiri Benc <***@redhat.com>
---
phc2sys.8 | 115 ++++++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 79 insertions(+), 36 deletions(-)

diff --git a/phc2sys.8 b/phc2sys.8
index fa3ae206f3df..37a39d0ba60f 100644
--- a/phc2sys.8
+++ b/phc2sys.8
@@ -1,12 +1,17 @@
.TH PHC2SYS 8 "November 2012" "linuxptp"
.SH NAME
-phc2sys \- synchronize two clocks
+phc2sys \- synchronize two or more clocks

.SH SYNOPSIS
-.B phc2sys
+.B phc2sys \-a
[
-.B \-wmqvx
+.B \-r
] [
+.B \-r
+] [ options ]
+.br
+.B phc2sys
+[
.BI \-d " pps-device"
] [
.BI \-s " device"
@@ -15,45 +20,53 @@ phc2sys \- synchronize two clocks
] [
.BI \-O " offset"
] [
-.BI \-E " servo"
-] [
-.BI \-P " kp"
-] [
-.BI \-I " ki"
-] [
-.BI \-S " step"
-] [
-.BI \-F " step"
-] [
-.BI \-R " update-rate"
-] [
-.BI \-N " clock-readings"
-] [
-.BI \-L " freq-limit"
-] [
-.BI \-u " summary-updates"
-] [
-.BI \-n " domain-number"
-] [
-.BI \-l " print-level"
-]
+.BI \-w
+] [ options ]

.SH DESCRIPTION
.B phc2sys
-is a program which synchronizes two clocks in the system. Typically, it is used
-to synchronize the system clock to a PTP hardware clock (PHC), which itself is
-synchronized by the
+is a program which synchronizes two or more clocks in the system. Typically,
+it is used to synchronize the system clock to a PTP hardware clock (PHC),
+which itself is synchronized by the
.BR ptp4l (8)
program.

-Two synchronization modes are supported, one uses a pulse per second (PPS)
+With the
+.B \-a
+option, the clocks to synchronize are fetched from the running
+.B ptp4l
+daemon and the direction of synchronization automatically follows changes of
+the PTP port states.
+
+Manual configuration is also possible. When using manual configuration, two
+synchronization modes are supported, one uses a pulse per second (PPS)
signal provided by the source clock and the other mode reads time from the
source clock directly. Some clocks can be used in both modes, the mode which
-will synchronize the slave clock with better accuracy depends on hardware and
-driver implementation.
+will synchronize the slave clock with better accuracy depends on hardware
+and driver implementation.

.SH OPTIONS
.TP
+.BI \-a
+Read the clocks to synchronize from running
+.B ptp4l
+and follow changes in the port states, adjusting the synchronization
+direction automatically. The system clock (CLOCK_REALTIME) is not
+synchronized, unless the
+.B \-r
+option is also specified.
+.TP
+.BI \-r
+Only valid together with the
+.B \-a
+option. Instructs
+.B phc2sys
+to also synchronize the system clock (CLOCK_REALTIME). By default, the
+system clock is not considered as a possible time source. If you want the
+system clock to be eligible to become a time source, specify the
+.B \-r
+option twice.
+.TP
.BI \-d " pps-device"
Specify the PPS device of the master clock (e.g. /dev/pps0). With this option
the PPS synchronization mode is used instead of the direct mode. As the PPS
@@ -68,7 +81,10 @@ option the PPS signal of the master clock is enabled automatically, otherwise
it has to be enabled before
.B phc2sys
is started (e.g. by running \f(CWecho 1 > /sys/class/ptp/ptp0/pps_enable\fP).
-This option can be used only with the system clock as the slave clock.
+This option can be used only with the system clock as the slave clock. Not
+compatible with the
+.B \-a
+option.
.TP
.BI \-s " device"
Specify the master clock by device (e.g. /dev/ptp0) or interface (e.g. eth0) or
@@ -76,7 +92,9 @@ by name (e.g. CLOCK_REALTIME for the system clock). When this option is used
together with the
.B \-d
option, the master clock is used only to correct the offset by whole number of
-seconds, which cannot be fixed with PPS alone.
+seconds, which cannot be fixed with PPS alone. Not compatible with the
+.B \-a
+option.
.TP
.BI \-i " interface"
Performs the exact same function as
@@ -89,7 +107,10 @@ should no longer be used.
.TP
.BI \-c " device"
Specify the slave clock by device (e.g. /dev/ptp1) or interface (e.g. eth1) or
-by name. The default is CLOCK_REALTIME (the system clock).
+by name. The default is CLOCK_REALTIME (the system clock). Not compatible
+with the
+.B \-a
+option.
.TP
.BI \-E " servo"
Specify which clock servo should be used. Valid values are pi for a PI
@@ -128,7 +149,10 @@ minimize the error caused by random delays in scheduling and bus utilization.
The default is 5.
.TP
.BI \-O " offset"
-Specify the offset between the slave and master times in seconds. See
+Specify the offset between the slave and master times in seconds. Not
+compatible with the
+.B \-a
+option. See
.SM
.B TIME SCALE USAGE
below.
@@ -154,7 +178,9 @@ Wait until ptp4l is in a synchronized state. If the
.B \-O
option is not used, also keep the offset between the slave and master
times updated according to the currentUtcOffset value obtained from ptp4l and
-the direction of the clock synchronization.
+the direction of the clock synchronization. Not compatible with the
+.B \-a
+option.
.TP
.BI \-n " domain-number"
Specify the domain number used by ptp4l. The default is 0.
@@ -198,6 +224,8 @@ clock follows UTC. Time offset between these two is maintained by

.B Phc2sys
acquires the offset value either by reading it from ptp4l when
+.B \-a
+or
.B \-w
is in effect or from command line when
.B \-O
@@ -208,6 +236,21 @@ master.

.SH EXAMPLES

+Synchronize time automatically according to the current
+.B ptp4l
+state, synchronize the system clock to the remote master.
+
+.RS
+\f(CWphc2sys \-a \-r\fP
+.RE
+
+Same as above, but when the host becomes the domain master, synchronize time
+in the domain to its system clock.
+
+.RS
+\f(CWphc2sys \-a \-rr\fP
+.RE
+
The host is a domain master, PTP clock is synchronized to system clock and the
time offset is obtained from
.BR ptp4l .
--
1.7.6.5
Richard Cochran
2014-05-12 06:56:11 UTC
Permalink
Post by Jiri Benc
phc2sys.8 | 115 +++++--
phc2sys.c | 1076 ++++++++++++++++++++++++++++++++++++++++++++--------------
pmc_common.c | 12 +-
pmc_common.h | 2 +
4 files changed, 908 insertions(+), 297 deletions(-)
This series only really touches phc2sys. It is a substantial change to
that program. Miroslav, that is mostly your work. Do you have any
comments before I merge this in?

Thanks,
Richard
Miroslav Lichvar
2014-05-12 15:29:01 UTC
Permalink
Post by Richard Cochran
Post by Jiri Benc
phc2sys.8 | 115 +++++--
phc2sys.c | 1076 ++++++++++++++++++++++++++++++++++++++++++++--------------
pmc_common.c | 12 +-
pmc_common.h | 2 +
4 files changed, 908 insertions(+), 297 deletions(-)
This series only really touches phc2sys. It is a substantial change to
that program. Miroslav, that is mostly your work. Do you have any
comments before I merge this in?
Beside that problem with phc2sys subscription timing out it looks good
to me.

Thanks,
--
Miroslav Lichvar
Loading...