This patch adds support for synchronization between clocks controlled by different ptp4l instances. phc2sys can now communicate with multiple ptp4l instances (specified with multiple -z options). Clocks from one instance are considered a domain. If the domain doesn't have a source clock (port in the slave state), the clocks in the domain can be synchronized to a source clock from a different domain. The real-time clock is a separate domain, which can be a source for other domains if the -r option is specified twice.
This allows the system clock or a PHC to be switched to a backup source on some (e.g. network) failures. The selection of the source could include a configurable priority or compare specific PTP attributes. Currently, it's the first synchronized source in the order of specified sockets. TODO: - fix non-automatic modes - extend configuration to support domain-specific settings - split into multiple patches - ... Signed-off-by: Miroslav Lichvar <mlich...@redhat.com> --- phc2sys.c | 594 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 349 insertions(+), 245 deletions(-) diff --git a/phc2sys.c b/phc2sys.c index 19e8012..46499a2 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -96,7 +96,7 @@ struct port { struct clock *clock; }; -struct phc2sys_private { +struct domain { unsigned int stats_max_count; int sanity_freq_limit; enum servo_type servo_type; @@ -111,15 +111,16 @@ struct phc2sys_private { LIST_HEAD(clock_head, clock) clocks; LIST_HEAD(dst_clock_head, clock) dst_clocks; struct clock *master; + int src_priority; }; static struct config *phc2sys_config; -static int clock_handle_leap(struct phc2sys_private *priv, +static int clock_handle_leap(struct domain *domain, struct clock *clock, int64_t offset, uint64_t ts); -static struct servo *servo_add(struct phc2sys_private *priv, +static struct servo *servo_add(struct domain *domain, struct clock *clock) { double ppb; @@ -139,19 +140,19 @@ static struct servo *servo_add(struct phc2sys_private *priv, } } - servo = servo_create(phc2sys_config, priv->servo_type, + servo = servo_create(phc2sys_config, domain->servo_type, -ppb, max_ppb, 0); if (!servo) { pr_err("Failed to create servo"); return NULL; } - servo_sync_interval(servo, priv->phc_interval); + servo_sync_interval(servo, domain->phc_interval); return servo; } -static struct clock *clock_add(struct phc2sys_private *priv, const char *device, +static struct clock *clock_add(struct domain *domain, const char *device, int phc_index) { struct clock *c; @@ -187,7 +188,7 @@ static struct clock *clock_add(struct phc2sys_private *priv, const char *device, c->source_label = "phc"; } - if (priv->stats_max_count > 0) { + if (domain->stats_max_count > 0) { c->offset_stats = stats_create(); c->freq_stats = stats_create(); c->delay_stats = stats_create(); @@ -198,8 +199,8 @@ static struct clock *clock_add(struct phc2sys_private *priv, const char *device, return NULL; } } - if (priv->sanity_freq_limit) { - c->sanity_check = clockcheck_create(priv->sanity_freq_limit); + if (domain->sanity_freq_limit) { + c->sanity_check = clockcheck_create(domain->sanity_freq_limit); if (!c->sanity_check) { pr_err("failed to create clock check"); return NULL; @@ -208,17 +209,17 @@ static struct clock *clock_add(struct phc2sys_private *priv, const char *device, if (clkid != CLOCK_INVALID && clkid != CLOCK_REALTIME) c->sysoff_method = sysoff_probe(CLOCKID_TO_FD(clkid), - priv->phc_readings); + domain->phc_readings); - LIST_INSERT_HEAD(&priv->clocks, c, list); + LIST_INSERT_HEAD(&domain->clocks, c, list); return c; } -static void clock_cleanup(struct phc2sys_private *priv) +static void clock_cleanup(struct domain *domain) { struct clock *c, *tmp; - LIST_FOREACH_SAFE(c, &priv->clocks, list, tmp) { + LIST_FOREACH_SAFE(c, &domain->clocks, list, tmp) { if (c->servo) { servo_destroy(c->servo); } @@ -241,45 +242,46 @@ static void clock_cleanup(struct phc2sys_private *priv) } } -static void port_cleanup(struct phc2sys_private *priv) +static void port_cleanup(struct domain *domain) { struct port *p, *tmp; - LIST_FOREACH_SAFE(p, &priv->ports, list, tmp) { + LIST_FOREACH_SAFE(p, &domain->ports, list, tmp) { free(p); } } -static struct port *port_get(struct phc2sys_private *priv, unsigned int number) +static struct port *port_get(struct domain *domain, unsigned int number) { struct port *p; - LIST_FOREACH(p, &priv->ports, list) { + LIST_FOREACH(p, &domain->ports, list) { if (p->number == number) return p; } return NULL; } -static struct port *port_add(struct phc2sys_private *priv, unsigned int number, +static struct port *port_add(struct domain *domain, struct pmc_agent *agent, + unsigned int number, char *device, int phc_index) { struct port *p; struct clock *c = NULL, *tmp; - p = port_get(priv, number); + p = port_get(domain, number); if (p) return p; /* port is a new one, look whether we have the device already on * a different port */ - LIST_FOREACH(tmp, &priv->clocks, list) { + LIST_FOREACH(tmp, &domain->clocks, list) { if (!strcmp(tmp->device, device)) { c = tmp; break; } } if (!c) { - c = clock_add(priv, device, phc_index); + c = clock_add(domain, device, phc_index); if (!c) return NULL; } @@ -290,11 +292,11 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, } p->number = number; p->clock = c; - LIST_INSERT_HEAD(&priv->ports, p, list); + LIST_INSERT_HEAD(&domain->ports, p, list); return p; } -static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, +static void clock_reinit(struct domain *domain, struct clock *clock, int new_state) { int err = -1, phc_index = -1, phc_switched = 0, timestamping; @@ -303,11 +305,11 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, struct port *p; clockid_t clkid = CLOCK_INVALID; - LIST_FOREACH(p, &priv->ports, list) { + LIST_FOREACH(p, &domain->ports, list) { if (p->clock != clock) { continue; } - err = pmc_agent_query_port_properties(priv->agent, 1000, + err = pmc_agent_query_port_properties(domain->agent, 1000, p->number, &state, ×tamping, &phc_index, iface); @@ -360,11 +362,11 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, ps_str[clock->state], ps_str[new_state]); } -static struct clock *find_dst_clock(struct phc2sys_private *priv, +static struct clock *find_dst_clock(struct domain *domain, int phc_index) { struct clock *c = NULL; - LIST_FOREACH(c, &priv->dst_clocks, dst_list) { + LIST_FOREACH(c, &domain->dst_clocks, dst_list) { if (c->phc_index == phc_index) { break; } @@ -372,26 +374,27 @@ static struct clock *find_dst_clock(struct phc2sys_private *priv, return c; } -static void reconfigure(struct phc2sys_private *priv) +static int reconfigure_domain(struct domain *domain) { - struct clock *c, *rt = NULL, *src = NULL, *last = NULL, *dup = NULL; + struct clock *c, *src = NULL, *dup = NULL; int src_cnt = 0, dst_cnt = 0; - pr_info("reconfiguring after port state change"); - priv->state_changed = 0; + domain->state_changed = 0; - while (priv->dst_clocks.lh_first != NULL) { - LIST_REMOVE(priv->dst_clocks.lh_first, dst_list); + while (domain->dst_clocks.lh_first != NULL) { + LIST_REMOVE(LIST_FIRST(&domain->dst_clocks), dst_list); } - LIST_FOREACH(c, &priv->clocks, list) { + LIST_FOREACH(c, &domain->clocks, list) { if (c->clkid == CLOCK_REALTIME) { - rt = c; - continue; + /* If present, it can always be a sink clock */ + LIST_INSERT_HEAD(&domain->dst_clocks, c, dst_list); + domain->master = c->dest_only ? NULL : c; + return 0; } if (c->new_state) { - clock_reinit(priv, c, c->new_state); + clock_reinit(domain, c, c->new_state); c->state = c->new_state; c->new_state = 0; } @@ -403,12 +406,12 @@ static void reconfigure(struct phc2sys_private *priv) case PS_PRE_MASTER: case PS_MASTER: case PS_PASSIVE: - dup = find_dst_clock(priv, c->phc_index); + dup = find_dst_clock(domain, c->phc_index); if (!dup) { pr_info("selecting %s for synchronization", c->device); dst_cnt++; - LIST_INSERT_HEAD(&priv->dst_clocks, + LIST_INSERT_HEAD(&domain->dst_clocks, c, dst_list); } else { pr_info("skipping %s: %s has the same clock " @@ -424,24 +427,24 @@ static void reconfigure(struct phc2sys_private *priv) src_cnt++; break; } - last = c; } if (src_cnt > 1) { pr_info("multiple master clocks available, postponing sync..."); - priv->master = NULL; - return; + domain->master = NULL; + return -1; } if (src_cnt > 0 && !src) { pr_info("master clock not ready, waiting..."); - priv->master = NULL; - return; + domain->master = NULL; + return -1; } if (!src_cnt && !dst_cnt) { pr_info("no PHC ready, waiting..."); - priv->master = NULL; - return; + domain->master = NULL; + return -1; } +#if 0 if (dst_cnt > 1 && !src) { if (!rt || rt->dest_only) { priv->master = last; @@ -455,34 +458,73 @@ static void reconfigure(struct phc2sys_private *priv) return; } } +#endif - if ((!src_cnt && (!rt || rt->dest_only)) || - (!dst_cnt && !rt)) { - pr_info("nothing to synchronize"); - priv->master = NULL; - return; + if (!src) { + domain->master = NULL; + return 0; } - if (!src_cnt) { - src = rt; - rt->state = PS_SLAVE; - } else if (rt) { - if (rt->state != PS_MASTER) { - rt->state = PS_MASTER; - clock_reinit(priv, rt, rt->state); + + domain->master = src; + pr_info("selecting %s as domain source clock", src->device); + return 0; +} + +static int compare_domains(struct domain *a, struct domain *b) +{ + if (!a || !b) { + if (a && a->master) + return -1; + if (b && b->master) + return 1; + return 0; + } + + if (!a->master != !b->master) + return !!b->master - !!a->master; + + return b->src_priority - a->src_priority; +} + +static void reconfigure(struct domain *domains, int n_domains) +{ + struct domain *src_domain = NULL; + int i; + + pr_info("reconfiguring after port state change"); + + for (i = 0; i < n_domains; i++) { + while (!LIST_EMPTY(&domains[i].dst_clocks)) + LIST_REMOVE(LIST_FIRST(&domains[i].dst_clocks), dst_list); + + if (reconfigure_domain(&domains[i])) + return; + + if (compare_domains(src_domain, &domains[i]) > 0) { + src_domain = &domains[i]; } - LIST_INSERT_HEAD(&priv->dst_clocks, rt, dst_list); - pr_info("selecting %s for synchronization", rt->device); } - priv->master = src; - pr_info("selecting %s as the master clock", src->device); + + if (n_domains <= 1 || !src_domain) { + return; + } + + pr_info("selecting %s as out-of-domain source clock", + src_domain->master->device); + + for (i = 0; i < n_domains; i++) { + if (domains[i].master && domains[i].src_priority > 0) + continue; + domains[i].master = src_domain->master; + } } -static int64_t get_sync_offset(struct phc2sys_private *priv, struct clock *dst) +static int64_t get_sync_offset(struct domain *domain, struct clock *dst) { - int direction = priv->forced_sync_offset; + int direction = domain->forced_sync_offset; if (!direction) - direction = dst->is_utc - priv->master->is_utc; + direction = dst->is_utc - domain->master->is_utc; return (int64_t)dst->sync_offset * NS_PER_SEC * direction; } @@ -525,24 +567,24 @@ static void update_clock_stats(struct clock *clock, unsigned int max_count, stats_reset(clock->delay_stats); } -static void update_clock(struct phc2sys_private *priv, struct clock *clock, +static void update_clock(struct domain *domain, struct clock *clock, int64_t offset, uint64_t ts, int64_t delay) { enum servo_state state = SERVO_UNLOCKED; double ppb = 0.0; if (!clock->servo) { - clock->servo = servo_add(priv, clock); + clock->servo = servo_add(domain, clock); if (!clock->servo) return; } - if (clock_handle_leap(priv, clock, offset, ts)) + if (clock_handle_leap(domain, clock, offset, ts)) return; - offset += get_sync_offset(priv, clock); + offset += get_sync_offset(domain, clock); - if (priv->free_running) + if (domain->free_running) goto report; if (clock->sanity_check && clockcheck_sample(clock->sanity_check, ts)) @@ -578,16 +620,16 @@ static void update_clock(struct phc2sys_private *priv, struct clock *clock, report: if (clock->offset_stats) { - update_clock_stats(clock, priv->stats_max_count, offset, ppb, delay); + update_clock_stats(clock, domain->stats_max_count, offset, ppb, delay); } else { if (delay >= 0) { pr_info("%s %s offset %9" PRId64 " s%d freq %+7.0f " "delay %6" PRId64, - clock->device, priv->master->source_label, + clock->device, domain->master->source_label, offset, state, ppb, delay); } else { pr_info("%s %s offset %9" PRId64 " s%d freq %+7.0f", - clock->device, priv->master->source_label, + clock->device, domain->master->source_label, offset, state, ppb); } } @@ -630,21 +672,21 @@ static int read_pps(int fd, int64_t *offset, uint64_t *ts) return 1; } -static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, +static int do_pps_loop(struct domain *domain, struct clock *clock, int fd) { int64_t pps_offset, phc_offset, phc_delay; uint64_t pps_ts, phc_ts; - clockid_t src = priv->master->clkid; + clockid_t src = domain->master->clkid; int err; - priv->master->source_label = "pps"; + domain->master->source_label = "pps"; if (src == CLOCK_INVALID) { /* The sync offset can't be applied with PPS alone. */ - pmc_agent_set_sync_offset(priv->agent, 0); + pmc_agent_set_sync_offset(domain->agent, 0); } else { - enable_pps_output(priv->master->clkid); + enable_pps_output(domain->master->clkid); } while (is_running()) { @@ -656,7 +698,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, of seconds in the offset and PPS for the rest. */ if (src != CLOCK_INVALID) { err = clockadj_compare(src, clock->clkid, - priv->phc_readings, + domain->phc_readings, &phc_offset, &phc_ts, &phc_delay); if (err == -EBUSY) @@ -678,9 +720,9 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, pps_offset = pps_ts - phc_ts; } - if (pmc_agent_update(priv->agent) < 0) + if (pmc_agent_update(domain->agent) < 0) continue; - update_clock(priv, clock, pps_offset, pps_ts, -1); + update_clock(domain, clock, pps_offset, pps_ts, -1); } close(fd); return 0; @@ -688,6 +730,9 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, static int update_needed(struct clock *c) { + if (c->clkid == CLOCK_REALTIME) + return 1; + switch (c->state) { case PS_FAULTY: case PS_DISABLED: @@ -703,88 +748,94 @@ static int update_needed(struct clock *c) return 0; } -static int do_loop(struct phc2sys_private *priv) +static int do_loop(struct domain *domains, int n_domains) { struct timespec interval; + struct domain *domain; struct clock *clock; uint64_t ts; int64_t offset, delay; - int err; + int i, err; - interval.tv_sec = priv->phc_interval; - interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; + //TODO: find minimum and skip updates if necessary + interval.tv_sec = domains[0].phc_interval; + interval.tv_nsec = (domains[0].phc_interval - interval.tv_sec) * 1e9; while (is_running()) { clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); - if (pmc_agent_update(priv->agent) < 0) { - continue; - } - if (priv->state_changed) { - /* force getting offset, as it may have - * changed after the port state change */ - if (pmc_agent_query_utc_offset(priv->agent, 1000)) { - pr_err("failed to get UTC offset"); - continue; + for (i = 0; i < n_domains; i++) { + domain = &domains[i]; + if (pmc_agent_update(domain->agent) < 0) { + //TODO? continue; } - reconfigure(priv); - } - if (!priv->master) - continue; - LIST_FOREACH(clock, &priv->dst_clocks, dst_list) { - if (!update_needed(clock)) - continue; - - /* don't try to synchronize the clock to itself */ - if (clock->clkid == priv->master->clkid || - (clock->phc_index >= 0 && - clock->phc_index == priv->master->phc_index) || - !strcmp(clock->device, priv->master->device)) + if (domain->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ + if (pmc_agent_query_utc_offset(domain->agent, 1000)) { + pr_err("failed to get UTC offset"); + continue; + } + reconfigure(domains, n_domains); + } + if (!domain->master) continue; - if (clock->clkid == CLOCK_REALTIME && - priv->master->sysoff_method >= 0) { - /* use sysoff */ - err = sysoff_measure(CLOCKID_TO_FD(priv->master->clkid), - priv->master->sysoff_method, - priv->phc_readings, - &offset, &ts, &delay); - } else if (priv->master->clkid == CLOCK_REALTIME && - clock->sysoff_method >= 0) { - /* use reversed sysoff */ - err = sysoff_measure(CLOCKID_TO_FD(clock->clkid), - clock->sysoff_method, - priv->phc_readings, - &offset, &ts, &delay); - if (!err) { - offset = -offset; - ts += offset; + LIST_FOREACH(clock, &domain->dst_clocks, dst_list) { + if (!update_needed(clock)) + continue; + + /* don't try to synchronize the clock to itself */ + if (clock->clkid == domain->master->clkid || + (clock->phc_index >= 0 && + clock->phc_index == domain->master->phc_index) || + !strcmp(clock->device, domain->master->device)) + continue; + + if (clock->clkid == CLOCK_REALTIME && + domain->master->sysoff_method >= 0) { + /* use sysoff */ + err = sysoff_measure(CLOCKID_TO_FD(domain->master->clkid), + domain->master->sysoff_method, + domain->phc_readings, + &offset, &ts, &delay); + } else if (domain->master->clkid == CLOCK_REALTIME && + clock->sysoff_method >= 0) { + /* use reversed sysoff */ + err = sysoff_measure(CLOCKID_TO_FD(clock->clkid), + clock->sysoff_method, + domain->phc_readings, + &offset, &ts, &delay); + if (!err) { + offset = -offset; + ts += offset; + } + } else { + /* use phc */ + err = clockadj_compare(domain->master->clkid, + clock->clkid, + domain->phc_readings, + &offset, &ts, &delay); } - } else { - /* use phc */ - err = clockadj_compare(priv->master->clkid, - clock->clkid, - priv->phc_readings, - &offset, &ts, &delay); + if (err == -EBUSY) + continue; + if (err) + return -1; + update_clock(domain, clock, offset, ts, delay); } - if (err == -EBUSY) - continue; - if (err) - return -1; - update_clock(priv, clock, offset, ts, delay); } } return 0; } -static int clock_compute_state(struct phc2sys_private *priv, +static int clock_compute_state(struct domain *domain, struct clock *clock) { struct port *p; int state = PS_DISABLED; - LIST_FOREACH(p, &priv->ports, list) { + LIST_FOREACH(p, &domain->ports, list) { if (p->clock != clock) continue; /* PS_SLAVE takes the highest precedence, PS_UNCALIBRATED @@ -800,7 +851,7 @@ static int clock_compute_state(struct phc2sys_private *priv, static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, int excluded) { - struct phc2sys_private *priv = (struct phc2sys_private *) context; + struct domain *domain = (struct domain *) context; int mgt_id, state; struct portDS *pds; struct port *port; @@ -812,7 +863,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, switch (mgt_id) { case MID_PORT_DATA_SET: pds = management_tlv_data(msg); - port = port_get(priv, pds->portIdentity.portNumber); + port = port_get(domain, pds->portIdentity.portNumber); if (!port) { pr_info("received data for unknown port %s", pid2str(&pds->portIdentity)); @@ -824,10 +875,10 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, pid2str(&pds->portIdentity)); port->state = state; clock = port->clock; - state = clock_compute_state(priv, clock); + state = clock_compute_state(domain, clock); if (clock->state != state || clock->new_state) { clock->new_state = state; - priv->state_changed = 1; + domain->state_changed = 1; } } return 1; @@ -835,7 +886,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, return 0; } -static int auto_init_ports(struct phc2sys_private *priv, int add_rt) +static int auto_init_ports(struct domain *domain) { int err, number_ports, phc_index, timestamping; enum port_state state; @@ -848,7 +899,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) if (!is_running()) { return -1; } - err = pmc_agent_query_dds(priv->agent, 1000); + err = pmc_agent_query_dds(domain->agent, 1000); if (!err) { break; } @@ -859,20 +910,20 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) } } - number_ports = pmc_agent_get_number_ports(priv->agent); + number_ports = pmc_agent_get_number_ports(domain->agent); if (number_ports <= 0) { pr_err("failed to get number of ports"); return -1; } - err = pmc_agent_subscribe(priv->agent, 1000); + err = pmc_agent_subscribe(domain->agent, 1000); if (err) { pr_err("failed to subscribe"); return -1; } for (i = 1; i <= number_ports; i++) { - err = pmc_agent_query_port_properties(priv->agent, 1000, i, + err = pmc_agent_query_port_properties(domain->agent, 1000, i, &state, ×tamping, &phc_index, iface); if (err == -ENODEV) { @@ -887,51 +938,56 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) /* ignore ports with software time stamping */ continue; } - port = port_add(priv, i, iface, phc_index); + port = port_add(domain, domain->agent, i, iface, phc_index); if (!port) return -1; port->state = port_state_normalize(state); } - if (LIST_EMPTY(&priv->clocks)) { + + if (LIST_EMPTY(&domain->clocks)) { pr_err("no suitable ports available"); return -1; } - LIST_FOREACH(clock, &priv->clocks, list) { - clock->new_state = clock_compute_state(priv, clock); - } - priv->state_changed = 1; - - if (add_rt) { - clock = clock_add(priv, "CLOCK_REALTIME", -1); - if (!clock) - return -1; - if (add_rt == 1) - clock->dest_only = 1; + LIST_FOREACH(clock, &domain->clocks, list) { + clock->new_state = clock_compute_state(domain, clock); } + domain->state_changed = 1; + domain->src_priority = 1; /* get initial offset */ - if (pmc_agent_query_utc_offset(priv->agent, 1000)) { + if (pmc_agent_query_utc_offset(domain->agent, 1000)) { pr_err("failed to get UTC offset"); return -1; } return 0; } +static int auto_init_rt(struct domain *domain, int dest_only) { + struct clock *clock; + + clock = clock_add(domain, "CLOCK_REALTIME", -1); + if (!clock) + return -1; + clock->dest_only = dest_only; + domain->src_priority = 0; + return 0; +} + /* Returns: non-zero to skip clock update */ -static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, +static int clock_handle_leap(struct domain *domain, struct clock *clock, int64_t offset, uint64_t ts) { - int clock_leap, node_leap = pmc_agent_get_leap(priv->agent); + int clock_leap, node_leap = pmc_agent_get_leap(domain->agent); - clock->sync_offset = pmc_agent_get_sync_offset(priv->agent); + clock->sync_offset = pmc_agent_get_sync_offset(domain->agent); if ((node_leap || clock->leap_set) && - clock->is_utc != priv->master->is_utc) { + clock->is_utc != domain->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 (priv->master->is_utc) { + if (domain->master->is_utc) { struct timespec tp; - if (clock_gettime(priv->master->clkid, &tp)) { + if (clock_gettime(domain->master->clkid, &tp)) { pr_err("failed to read clock: %m"); return -1; } @@ -941,7 +997,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, /* 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(priv, clock); + ts -= offset + get_sync_offset(domain, clock); /* Suspend clock updates in the last second before midnight. */ if (is_utc_ambiguous(ts)) { @@ -956,7 +1012,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, if (clock->leap_set != clock_leap) { /* Only the system clock can leap. */ if (clock->clkid == CLOCK_REALTIME && - priv->kernel_leap) + domain->kernel_leap) sysclk_set_leap(clock_leap); else servo_leap(clock->servo, clock_leap); @@ -964,7 +1020,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, } } - if (pmc_agent_utc_offset_traceable(priv->agent) && + if (pmc_agent_utc_offset_traceable(domain->agent) && clock->utc_offset_set != clock->sync_offset) { if (clock->clkid == CLOCK_REALTIME) sysclk_set_tai_offset(clock->sync_offset); @@ -979,43 +1035,43 @@ static bool hardpps_configured(int fd) return fd >= 0; } -static int phc2sys_static_src_configuration(struct phc2sys_private *priv, +static int phc2sys_static_src_configuration(struct domain *domain, const char *src_name) { struct clock *src; - src = clock_add(priv, src_name, -1); + src = clock_add(domain, src_name, -1); if (!src) { fprintf(stderr, "valid source clock must be selected.\n"); return -1; } src->state = PS_SLAVE; - priv->master = src; + domain->master = src; return 0; } -static int phc2sys_static_dst_configuration(struct phc2sys_private *priv, +static int phc2sys_static_dst_configuration(struct domain *domain, const char *dst_name) { struct clock *dst; - dst = clock_add(priv, dst_name, -1); + dst = clock_add(domain, dst_name, -1); if (!dst) { fprintf(stderr, "valid destination clock must be selected.\n"); return -1; } dst->state = PS_MASTER; - LIST_INSERT_HEAD(&priv->dst_clocks, dst, dst_list); + LIST_INSERT_HEAD(&domain->dst_clocks, dst, dst_list); return 0; } -static bool phc2sys_using_systemclock(struct phc2sys_private *priv) +static bool phc2sys_using_systemclock(struct domain *domain) { struct clock *c; - LIST_FOREACH(c, &priv->clocks, list) { + LIST_FOREACH(c, &domain->clocks, list) { if (c->clkid == CLOCK_REALTIME) { return true; } @@ -1064,21 +1120,28 @@ static void usage(char *progname) progname); } +#define MAX_DOMAINS 16 + int main(int argc, char *argv[]) { char *config = NULL, *progname, *src_name = NULL; - const char *dst_names[MAX_DST_CLOCKS]; + const char *dst_names[MAX_DST_CLOCKS], *uds_remotes[MAX_DOMAINS]; char uds_local[MAX_IFNAME_SIZE + 1]; - int i, autocfg = 0, c, domain_number = 0, index, ntpshm_segment, offset; + int i, j, autocfg = 0, c, domain_number = 0, index, ntpshm_segment, offset; int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0; - int wait_sync = 0, dst_cnt = 0; + int wait_sync = 0, dst_cnt = 0, uds_remote_cnt = 0; struct config *cfg; struct option *opts; double phc_rate, tmp; - struct phc2sys_private priv = { + struct domain domains[MAX_DOMAINS]; + struct domain settings = { .phc_readings = 5, .phc_interval = 1.0, }; + int n_domains = 0; + + memset(domains, 0, sizeof (domains)); + offset = 0; handle_term_signals(); @@ -1086,10 +1149,6 @@ int main(int argc, char *argv[]) if (!cfg) { return -1; } - priv.agent = pmc_agent_create(); - if (!priv.agent) { - return -1; - } opts = config_long_options(cfg); @@ -1181,22 +1240,21 @@ int main(int argc, char *argv[]) case 'R': if (get_arg_val_d(c, optarg, &phc_rate, 1e-9, DBL_MAX)) goto end; - priv.phc_interval = 1.0 / phc_rate; + settings.phc_interval = 1.0 / phc_rate; break; case 'N': - if (get_arg_val_i(c, optarg, &priv.phc_readings, 1, INT_MAX)) + if (get_arg_val_i(c, optarg, &settings.phc_readings, 1, INT_MAX)) goto end; break; case 'O': if (get_arg_val_i(c, optarg, &offset, INT_MIN, INT_MAX)) { goto end; } - pmc_agent_set_sync_offset(priv.agent, offset); - priv.forced_sync_offset = -1; + settings.forced_sync_offset = -1; break; case 'L': - if (get_arg_val_i(c, optarg, &priv.sanity_freq_limit, 0, INT_MAX) || - config_set_int(cfg, "sanity_freq_limit", priv.sanity_freq_limit)) { + if (get_arg_val_i(c, optarg, &settings.sanity_freq_limit, 0, INT_MAX) || + config_set_int(cfg, "sanity_freq_limit", settings.sanity_freq_limit)) { goto end; } break; @@ -1206,7 +1264,7 @@ int main(int argc, char *argv[]) goto end; break; case 'u': - if (get_arg_val_ui(c, optarg, &priv.stats_max_count, + if (get_arg_val_ui(c, optarg, &settings.stats_max_count, 0, UINT_MAX)) goto end; break; @@ -1230,9 +1288,12 @@ int main(int argc, char *argv[]) optarg, MAX_IFNAME_SIZE); goto end; } - if (config_set_string(cfg, "uds_address", optarg)) { - goto end; + if (uds_remote_cnt == MAX_DOMAINS) { + fprintf(stderr, "too many domains\n"); + goto bad_usage; } + uds_remotes[uds_remote_cnt++] = optarg; + n_domains++; break; case 'l': if (get_arg_val_i(c, optarg, &print_level, @@ -1278,7 +1339,7 @@ int main(int argc, char *argv[]) } if (autocfg && (src_name || dst_cnt > 0 || hardpps_configured(pps_fd) || - wait_sync || priv.forced_sync_offset)) { + wait_sync /*|| domain.forced_sync_offset */)) { fprintf(stderr, "autoconfiguration cannot be mixed with manual config options.\n"); goto bad_usage; @@ -1289,7 +1350,7 @@ int main(int argc, char *argv[]) goto bad_usage; } - if (!autocfg && !wait_sync && !priv.forced_sync_offset) { + if (!autocfg && !wait_sync /*&& *!domain.forced_sync_offset*/) { fprintf(stderr, "time offset must be specified using -w or -O\n"); goto bad_usage; @@ -1308,36 +1369,72 @@ int main(int argc, char *argv[]) print_set_syslog(config_get_int(cfg, NULL, "use_syslog")); print_set_level(config_get_int(cfg, NULL, "logging_level")); - priv.free_running = config_get_int(cfg, NULL, "free_running"); - priv.servo_type = config_get_int(cfg, NULL, "clock_servo"); - if (priv.free_running || priv.servo_type == CLOCK_SERVO_NTPSHM) { - config_set_int(cfg, "kernel_leap", 0); - config_set_int(cfg, "sanity_freq_limit", 0); + if (autocfg) { + if (n_domains == 0) + n_domains = 1; + if (rt) + n_domains += 1; + if (n_domains > MAX_DOMAINS) { + fprintf(stderr, "too many domains\n"); + goto bad_usage; + } + } else { + n_domains = 1; } - priv.kernel_leap = config_get_int(cfg, NULL, "kernel_leap"); - priv.sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit"); - for (i = 0; i < dst_cnt; i++) { - r = phc2sys_static_dst_configuration(&priv, dst_names[i]); - if (r) { - goto end; + for (i = 0; i < n_domains; i++) { + domains[i] = settings; + domains[i].free_running = config_get_int(cfg, NULL, "free_running"); + domains[i].servo_type = config_get_int(cfg, NULL, "clock_servo"); + if (domains[i].free_running || domains[i].servo_type == CLOCK_SERVO_NTPSHM) { + config_set_int(cfg, "kernel_leap", 0); + config_set_int(cfg, "sanity_freq_limit", 0); + } + domains[i].kernel_leap = config_get_int(cfg, NULL, "kernel_leap"); + domains[i].sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit"); + + for (j = 0; j < dst_cnt; j++) { + r = phc2sys_static_dst_configuration(&domains[i], + dst_names[j]); + if (r) { + goto end; + } } - } - snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d", - getpid()); + domains[i].agent = pmc_agent_create(); + if (!domains[i].agent) { + return -1; + } + + pmc_agent_set_sync_offset(domains[i].agent, offset); + } if (autocfg) { - if (init_pmc_node(cfg, priv.agent, uds_local, - phc2sys_recv_subscribed, &priv)) - goto end; - if (auto_init_ports(&priv, rt) < 0) - goto end; - r = do_loop(&priv); + for (i = 0; i < n_domains; i++) { + if (rt && i + 1 == n_domains) { + if (auto_init_rt(&domains[n_domains - 1], + rt == 1) < 0) + goto end; + continue; + } + + snprintf(uds_local, sizeof(uds_local), + "/var/run/phc2sys.%d.%d", getpid(), i); + + if (uds_remote_cnt > i) + config_set_string(cfg, "uds_address", uds_remotes[i]); + + if (init_pmc_node(cfg, domains[i].agent, uds_local, + phc2sys_recv_subscribed, &domains[i])) + goto end; + if (auto_init_ports(&domains[i]) < 0) + goto end; + } + r = do_loop(domains, n_domains); goto end; } - r = phc2sys_static_src_configuration(&priv, src_name); + r = phc2sys_static_src_configuration(&domains[0], src_name); if (r) { goto end; } @@ -1345,51 +1442,58 @@ int main(int argc, char *argv[]) r = -1; if (wait_sync) { - if (init_pmc_node(cfg, priv.agent, uds_local, - phc2sys_recv_subscribed, &priv)) - goto end; + for (i = 0; i < n_domains; i++) { + snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d.%d", + getpid(), i); - while (is_running()) { - r = run_pmc_wait_sync(priv.agent, 1000); - if (r < 0) + if (init_pmc_node(cfg, domains[i].agent, uds_local, + phc2sys_recv_subscribed, &domains[i])) goto end; - if (r > 0) - break; - else - pr_notice("Waiting for ptp4l..."); - } - if (!priv.forced_sync_offset) { - r = pmc_agent_query_utc_offset(priv.agent, 1000); - if (r) { - pr_err("failed to get UTC offset"); - goto end; + while (is_running()) { + r = run_pmc_wait_sync(domains[i].agent, 1000); + if (r < 0) + goto end; + if (r > 0) + break; + else + pr_notice("Waiting for ptp4l..."); + } + + if (!domains[i].forced_sync_offset) { + r = pmc_agent_query_utc_offset(domains[i].agent, 1000); + if (r) { + pr_err("failed to get UTC offset"); + goto end; + } } - } - if (priv.forced_sync_offset || - !phc2sys_using_systemclock(&priv) || - hardpps_configured(pps_fd)) { - pmc_agent_disable(priv.agent); + if (domains[i].forced_sync_offset || + !phc2sys_using_systemclock(&domains[i]) || + hardpps_configured(pps_fd)) { + pmc_agent_disable(domains[i].agent); + } } } if (hardpps_configured(pps_fd)) { - struct clock *dst = LIST_FIRST(&priv.dst_clocks); + struct clock *dst = LIST_FIRST(&domains[0].dst_clocks); /* only one destination clock allowed with PPS until we * implement a mean to specify PTP port to PPS mapping */ - dst->servo = servo_add(&priv, dst); + dst->servo = servo_add(&domains[0], dst); servo_sync_interval(dst->servo, 1.0); - r = do_pps_loop(&priv, dst, pps_fd); + r = do_pps_loop(&domains[0], dst, pps_fd); } else { - r = do_loop(&priv); + r = do_loop(&domains[0], 1); } end: - pmc_agent_destroy(priv.agent); - clock_cleanup(&priv); - port_cleanup(&priv); + for (i = 0; i < n_domains; i++) { + pmc_agent_destroy(domains[i].agent); + clock_cleanup(&domains[i]); + port_cleanup(&domains[i]); + } config_destroy(cfg); msg_cleanup(); return r; -- 2.39.0 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel