Skip to content

Commit 880f440

Browse files
committed
Add phc2sys companion service for BC/TC instances
On multi-chip DSA hardware (e.g., boards with multiple mv88e6xxx chips), each switch chip has its own independent PHC device. With boundary_clock_jbod enabled, ptp4l starts but only disciplines the active slave port's PHC — the others drift. Automatically start phc2sys -a alongside any BC or TC instance using hardware timestamping. It subscribes to ptp4l's UDS socket, tracks BMCA, and disciplines all non-active PHCs to match the active one. On single-chip hardware it is a harmless no-op. CLOCK_REALTIME is intentionally left untouched. Syncing the system clock to PTP (phc2sys -rr), feeding the PHC from GPS/NTP (ts2phc, phc2sys reverse), and full multi-source coordination (timemaster) are planned as follow-on phases; see the issue tracker for the roadmap. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
1 parent bf194bb commit 880f440

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
service <!> name:phc2sys :%i log:prio:daemon,tag:phc2sys-%i \
2+
[2345] phc2sys -a -z /var/run/ptp4l-%i \
3+
-- PHC synchronization for PTP instance %i

src/confd/src/ptp.c

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,79 @@ static int write_instance_conf(struct lyd_node *inst, json_t *root)
291291
return SR_ERR_OK;
292292
}
293293

294+
/*
295+
* True when a PTP instance needs a phc2sys companion to keep all its
296+
* PHC devices in sync. Required for BC and TC instances on hardware
297+
* with multiple switch chips, where each chip has its own /dev/ptpN.
298+
* On single-chip hardware the function is a no-op: phc2sys -a finds
299+
* no second PHC and exits immediately. OC has one port → one PHC,
300+
* so no sync is ever needed.
301+
*/
302+
static bool needs_phc2sys(struct lyd_node *inst, json_t *root)
303+
{
304+
struct lyd_node *default_ds = lydx_get_child(inst, "default-ds");
305+
const char *type = lydx_get_cattr(default_ds, "instance-type");
306+
307+
if (!type)
308+
return false;
309+
if (strcmp(type, "bc") && strcmp(type, "p2p-tc") && strcmp(type, "e2e-tc"))
310+
return false;
311+
return !strcmp(instance_time_stamping(inst, root), "hardware");
312+
}
313+
314+
/*
315+
* Enable the phc2sys@ companion service for a multi-port HW instance.
316+
* phc2sys -a subscribes to ptp4l's UDS, discovers the active slave
317+
* port via BMCA, and disciplines all other PHCs to match it.
318+
* No config file is needed — the UDS path is passed on the command line.
319+
*/
320+
static void activate_phc2sys(uint16_t idx)
321+
{
322+
finit_enablef("phc2sys@%u", idx);
323+
finit_reloadf("phc2sys@%u", idx);
324+
}
325+
326+
static void deactivate_phc2sys(uint16_t idx)
327+
{
328+
finit_disablef("phc2sys@%u", idx);
329+
}
330+
331+
/*
332+
* Disable any phc2sys@ services whose index is no longer configured.
333+
*/
334+
static void cleanup_stale_phc2sys(struct lyd_node *config)
335+
{
336+
struct lyd_node *inst;
337+
struct dirent *ent;
338+
DIR *d;
339+
int idx;
340+
341+
d = opendir(FINIT_RCSD "/enabled");
342+
if (!d)
343+
return;
344+
345+
while ((ent = readdir(d))) {
346+
bool found = false;
347+
348+
if (sscanf(ent->d_name, "phc2sys@%d.conf", &idx) != 1)
349+
continue;
350+
351+
LYX_LIST_FOR_EACH(lydx_get_descendant(config, "ptp", "instances", "instance", NULL),
352+
inst, "instance") {
353+
const char *v = lydx_get_cattr(inst, "instance-index");
354+
if (v && atoi(v) == idx) {
355+
found = true;
356+
break;
357+
}
358+
}
359+
360+
if (!found)
361+
deactivate_phc2sys((uint16_t)idx);
362+
}
363+
364+
closedir(d);
365+
}
366+
294367
/*
295368
* Remove staging config for one instance.
296369
*/
@@ -410,12 +483,18 @@ static int change(sr_session_ctx_t *session, struct lyd_node *config,
410483
const char *v = lydx_get_cattr(inst, "instance-index");
411484
if (!v)
412485
continue;
413-
if ((rc = activate_instance((uint16_t)atoi(v))))
486+
uint16_t idx = (uint16_t)atoi(v);
487+
if ((rc = activate_instance(idx)))
414488
return rc;
489+
if (needs_phc2sys(inst, confd->root))
490+
activate_phc2sys(idx);
491+
else
492+
deactivate_phc2sys(idx);
415493
}
416494

417495
/* Disable stale services not in current config */
418496
cleanup_stale_instances(config);
497+
cleanup_stale_phc2sys(config);
419498

420499
if (!instances)
421500
return SR_ERR_OK;

0 commit comments

Comments
 (0)