phc2sys: don't over-control a destination clock

If ptp4l is running as a boundary clock, and multiple master ports
share a hardware clock that is different than the slave port's
hardware clock, then phc2sys in autocfg mode will synchronize that
clock multiple times per sync loop, once per-port.

Example output when this happens:
CLOCK_REALTIME phc offset        -9 s2 freq    -355 delay   3635
eth9 phc offset         9 s2 freq   +1762 delay   7488
eth8 phc offset        41 s2 freq   +1985 delay   7424
eth7 phc offset        15 s2 freq   +1952 delay   7440
eth6 phc offset        20 s2 freq   +1059 delay   7440
eth1 phc offset        22 s2 freq   +1783 delay   7424
eth12.400 phc offset        15 s2 freq -4207943 delay   7551
eth12.300 phc offset       388 s2 freq +2336645 delay   7536
eth12.200 phc offset       451 s2 freq +2516553 delay   7438
eth12.100 phc offset       268 s2 freq  +64274 delay   7405

This is easily reproducible by adding VLANs to a single network
interface and configuring ptp4l to use those VLAN interfaces as PTP
ports.

This patch prevents this problem by introducing a new deduplicated
destination clock list to phc2sys. Each time phc2sys reconfigures,
the destination clock list is re-populated. In the synchronization
loop, phc2sys will only use the clocks in this list.

Example output of reconfiguration with this patch:
reconfiguring after port state change
selecting eth9 for synchronization
selecting eth8 for synchronization
selecting eth7 for synchronization
selecting eth6 for synchronization
selecting eth1 for synchronization
selecting eth13.400 for synchronization
skipping eth13.300: eth13.400 has the same clock and is already selected
skipping eth13.200: eth13.400 has the same clock and is already selected
skipping eth13.100: eth13.400 has the same clock and is already selected
selecting eth12.400 for synchronization
skipping eth12.300: eth12.400 has the same clock and is already selected
skipping eth12.200: eth12.400 has the same clock and is already selected
skipping eth12.100: eth12.400 has the same clock and is already selected
skipping eth13.500: eth13.400 has the same clock and is already selected

This patch was tested with autocfg mode and also when specifying source
and destination manually via commandline.

Signed-off-by: Cliff Spradlin <cspradlin@google.com>
master
Cliff Spradlin via Linuxptp-devel 2018-06-14 11:23:32 -07:00 committed by Richard Cochran
parent dc0f6427bd
commit 4c5d180ab3
1 changed files with 32 additions and 4 deletions

View File

@ -71,6 +71,7 @@
struct clock { struct clock {
LIST_ENTRY(clock) list; LIST_ENTRY(clock) list;
LIST_ENTRY(clock) dst_list;
clockid_t clkid; clockid_t clkid;
int phc_index; int phc_index;
int sysoff_supported; int sysoff_supported;
@ -117,6 +118,7 @@ struct node {
struct ClockIdentity clock_identity; struct ClockIdentity clock_identity;
LIST_HEAD(port_head, port) ports; LIST_HEAD(port_head, port) ports;
LIST_HEAD(clock_head, clock) clocks; LIST_HEAD(clock_head, clock) clocks;
LIST_HEAD(dst_clock_head, clock) dst_clocks;
struct clock *master; struct clock *master;
}; };
@ -400,14 +402,28 @@ static void clock_reinit(struct node *node, struct clock *clock, int new_state)
} }
} }
static struct clock *find_dst_clock(struct node *node, int phc_index) {
struct clock *c = NULL;
LIST_FOREACH(c, &node->dst_clocks, dst_list) {
if (c->phc_index == phc_index) {
break;
}
}
return c;
}
static void reconfigure(struct node *node) static void reconfigure(struct node *node)
{ {
struct clock *c, *rt = NULL, *src = NULL, *last = NULL; struct clock *c, *rt = NULL, *src = NULL, *last = NULL, *dup = NULL;
int src_cnt = 0, dst_cnt = 0; int src_cnt = 0, dst_cnt = 0;
pr_info("reconfiguring after port state change"); pr_info("reconfiguring after port state change");
node->state_changed = 0; node->state_changed = 0;
while (node->dst_clocks.lh_first != NULL) {
LIST_REMOVE(node->dst_clocks.lh_first, dst_list);
}
LIST_FOREACH(c, &node->clocks, list) { LIST_FOREACH(c, &node->clocks, list) {
if (c->clkid == CLOCK_REALTIME) { if (c->clkid == CLOCK_REALTIME) {
rt = c; rt = c;
@ -427,8 +443,18 @@ static void reconfigure(struct node *node)
case PS_PRE_MASTER: case PS_PRE_MASTER:
case PS_MASTER: case PS_MASTER:
case PS_PASSIVE: case PS_PASSIVE:
pr_info("selecting %s for synchronization", c->device); dup = find_dst_clock(node, c->phc_index);
if (!dup) {
pr_info("selecting %s for synchronization",
c->device);
dst_cnt++; dst_cnt++;
LIST_INSERT_HEAD(&node->dst_clocks,
c, dst_list);
} else {
pr_info("skipping %s: %s has the same clock "
"and is already selected",
c->device, dup->device);
}
break; break;
case PS_UNCALIBRATED: case PS_UNCALIBRATED:
src_cnt++; src_cnt++;
@ -482,6 +508,7 @@ static void reconfigure(struct node *node)
rt->state = PS_MASTER; rt->state = PS_MASTER;
clock_reinit(node, rt, rt->state); clock_reinit(node, rt, rt->state);
} }
LIST_INSERT_HEAD(&node->dst_clocks, rt, dst_list);
pr_info("selecting %s for synchronization", rt->device); pr_info("selecting %s for synchronization", rt->device);
} }
node->master = src; node->master = src;
@ -745,7 +772,7 @@ static int do_loop(struct node *node, int subscriptions)
if (!node->master) if (!node->master)
continue; continue;
LIST_FOREACH(clock, &node->clocks, list) { LIST_FOREACH(clock, &node->dst_clocks, dst_list) {
if (!update_needed(clock)) if (!update_needed(clock))
continue; continue;
@ -1598,6 +1625,7 @@ int main(int argc, char *argv[])
goto bad_usage; goto bad_usage;
} }
dst->state = PS_MASTER; dst->state = PS_MASTER;
LIST_INSERT_HEAD(&node.dst_clocks, dst, dst_list);
if (pps_fd >= 0 && dst->clkid != CLOCK_REALTIME) { if (pps_fd >= 0 && dst->clkid != CLOCK_REALTIME) {
fprintf(stderr, fprintf(stderr,