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
parent
dc0f6427bd
commit
4c5d180ab3
36
phc2sys.c
36
phc2sys.c
|
@ -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);
|
||||||
dst_cnt++;
|
if (!dup) {
|
||||||
|
pr_info("selecting %s for synchronization",
|
||||||
|
c->device);
|
||||||
|
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,
|
||||||
|
|
Loading…
Reference in New Issue