Skip to content

Commit 3426c25

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 45b06dd commit 3426c25

2 files changed

Lines changed: 84 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: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,80 @@ static int write_instance_conf(struct lyd_node *inst, json_t *root)
305305
return SR_ERR_OK;
306306
}
307307

308+
/*
309+
* True when a PTP instance needs a phc2sys companion to keep all its
310+
* PHC devices in sync. Required for BC and TC instances on hardware
311+
* with multiple switch chips, where each chip has its own /dev/ptpN.
312+
* On single-chip hardware the function is a no-op: phc2sys -a finds
313+
* no second PHC and exits immediately. OC has one port → one PHC,
314+
* so no sync is ever needed.
315+
*/
316+
static bool needs_phc2sys(struct lyd_node *inst, json_t *root)
317+
{
318+
struct lyd_node *default_ds = lydx_get_child(inst, "default-ds");
319+
const char *type = lydx_get_cattr(default_ds, "instance-type");
320+
321+
if (!type)
322+
return false;
323+
if (strcmp(type, "bc") && strcmp(type, "p2p-tc") && strcmp(type, "e2e-tc"))
324+
return false;
325+
326+
return !strcmp(instance_time_stamping(inst, root), "hardware");
327+
}
328+
329+
/*
330+
* Enable the phc2sys@ companion service for a multi-port HW instance.
331+
* phc2sys -a subscribes to ptp4l's UDS, discovers the active slave
332+
* port via BMCA, and disciplines all other PHCs to match it.
333+
* No config file is needed — the UDS path is passed on the command line.
334+
*/
335+
static void activate_phc2sys(uint16_t idx)
336+
{
337+
finit_enablef("phc2sys@%u", idx);
338+
finit_reloadf("phc2sys@%u", idx);
339+
}
340+
341+
static void deactivate_phc2sys(uint16_t idx)
342+
{
343+
finit_disablef("phc2sys@%u", idx);
344+
}
345+
346+
/*
347+
* Disable any phc2sys@ services whose index is no longer configured.
348+
*/
349+
static void cleanup_stale_phc2sys(struct lyd_node *config)
350+
{
351+
const struct dirent *ent;
352+
struct lyd_node *inst;
353+
DIR *d;
354+
int idx;
355+
356+
d = opendir(FINIT_RCSD "/enabled");
357+
if (!d)
358+
return;
359+
360+
while ((ent = readdir(d))) {
361+
bool found = false;
362+
363+
if (sscanf(ent->d_name, "phc2sys@%d.conf", &idx) != 1)
364+
continue;
365+
366+
LYX_LIST_FOR_EACH(lydx_get_descendant(config, "ptp", "instances", "instance", NULL),
367+
inst, "instance") {
368+
const char *v = lydx_get_cattr(inst, "instance-index");
369+
if (v && atoi(v) == idx) {
370+
found = true;
371+
break;
372+
}
373+
}
374+
375+
if (!found)
376+
deactivate_phc2sys((uint16_t)idx);
377+
}
378+
379+
closedir(d);
380+
}
381+
308382
/*
309383
* Remove staging config for one instance.
310384
*/
@@ -423,12 +497,18 @@ static int change(sr_session_ctx_t *session, struct lyd_node *config,
423497
const char *v = lydx_get_cattr(inst, "instance-index");
424498
if (!v)
425499
continue;
426-
if ((rc = activate_instance((uint16_t)atoi(v))))
500+
uint16_t idx = (uint16_t)atoi(v);
501+
if ((rc = activate_instance(idx)))
427502
return rc;
503+
if (needs_phc2sys(inst, confd->root))
504+
activate_phc2sys(idx);
505+
else
506+
deactivate_phc2sys(idx);
428507
}
429508

430509
/* Disable stale services not in current config */
431510
cleanup_stale_instances(config);
511+
cleanup_stale_phc2sys(config);
432512

433513
if (!instances)
434514
return SR_ERR_OK;

0 commit comments

Comments
 (0)