Files
RPi5-PTP-server/boundaryClockAndClient.md
2026-09-07 00:27:05 +02:00

7.0 KiB
Raw Blame History

eno4            upstream toward RPi5 grandmaster
enp4s0/enp4s0d1 downstream toward N9K

This design is:

RPi5 GM
  -> ptp4l-client on eno4
  -> eno4 PHC
  -> phc2sys-client
  -> CLOCK_REALTIME on WOLF-HUNTER
  -> phc2sys-downstream
  -> enp4s0/enp4s0d1 PHC
  -> ptp4l-downstream
  -> N9K / downstream clients

ptp4l can use multiple -i interfaces, clientOnly is the client/slave mode, and serverOnly prevents a port from becoming a client. phc2sys is the tool that syncs PHCs and CLOCK_REALTIME; -w waits for ptp4l, while -O 37 provides the UTC/TAI offset explicitly. ([Linux PTP Project][1])

1. Upstream PTP client config: eno4

Create:

sudo nano /etc/linuxptp/ptp4l-upstream-client.conf
[global]
verbose                 1
time_stamping           hardware

domainNumber            0
network_transport       UDPv4
delay_mechanism         E2E

# Upstream side: only act as a PTP client/slave
clientOnly              1

summary_interval        1

# Separate UDS socket so multiple ptp4l instances do not collide
uds_address             /var/run/ptp4l-upstream

[eno4]

2. Downstream PTP server config: enp4s0/enp4s0d1

Create:

sudo nano /etc/linuxptp/ptp4l-downstream-server.conf
[global]
verbose                 1
time_stamping           hardware

domainNumber            0
network_transport       UDPv4
delay_mechanism         E2E

# Downstream side: serve time toward N9K/clients
serverOnly              1

# Make this preferred on the downstream PTP segment
priority1               128
priority2               128

# This host is fed by upstream PTP, not directly by GNSS
timeSource              0x40

# Conservative advertised quality for a PTP-fed downstream server.
# You can tune these later after measuring.
clockClass              6
clockAccuracy           0x23
offsetScaledLogVariance 0xFFFF

utc_offset              37
summary_interval        1

# Separate UDS socket
uds_address             /var/run/ptp4l-downstream

[enp4s0]

[enp4s0d1]

Small caveat: this split-service design works, but it is not a “pure” PTP boundary clock because the upstream and downstream ptp4l instances are separate. A single ptp4l boundary clock preserves PTP parent/stepsRemoved behavior better, but your split design avoids the mixed-PHC/JBOD issue.

3. ptp4l upstream client service

Create:

sudo nano /etc/systemd/system/ptp4l-upstream-client.service
[Unit]
Description=PTP upstream client on eno4
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/sbin/ptp4l -i eno4 -f /etc/linuxptp/ptp4l-upstream-client.conf -m
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

4. phc2sys upstream: eno4 PHC -> CLOCK_REALTIME

This syncs the boundary servers own Linux system clock.

Create:

sudo nano /etc/systemd/system/phc2sys-upstream-to-system.service
[Unit]
Description=Sync WOLF-HUNTER system clock from upstream eno4 PHC
After=ptp4l-upstream-client.service
Requires=ptp4l-upstream-client.service

[Service]
Type=simple
ExecStart=/usr/sbin/phc2sys -s eno4 -c CLOCK_REALTIME -w -z /var/run/ptp4l-upstream -m
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

5. phc2sys downstream: CLOCK_REALTIME -> enp4s0 PHC

First check whether enp4s0 and enp4s0d1 share the same PHC:

readlink -f /sys/class/net/enp4s0/device/ptp
readlink -f /sys/class/net/enp4s0d1/device/ptp
ethtool -T enp4s0 | grep 'Hardware timestamp provider'
ethtool -T enp4s0d1 | grep 'Hardware timestamp provider'

If both are the same PHC, one downstream phc2sys service is enough.

Create:

sudo nano /etc/systemd/system/phc2sys-system-to-downstream.service
[Unit]
Description=Sync downstream enp4s0 PHC from WOLF-HUNTER system clock
After=phc2sys-upstream-to-system.service ptp4l-downstream-server.service
Requires=phc2sys-upstream-to-system.service ptp4l-downstream-server.service

[Service]
Type=simple
ExecStart=/usr/sbin/phc2sys -s CLOCK_REALTIME -c enp4s0 -O 37 -m
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

If enp4s0d1 is on a different PHC from enp4s0, create a second downstream PHC sync service too:

sudo nano /etc/systemd/system/phc2sys-system-to-downstream-d1.service
[Unit]
Description=Sync downstream enp4s0d1 PHC from WOLF-HUNTER system clock
After=phc2sys-upstream-to-system.service ptp4l-downstream-server.service
Requires=phc2sys-upstream-to-system.service ptp4l-downstream-server.service

[Service]
Type=simple
ExecStart=/usr/sbin/phc2sys -s CLOCK_REALTIME -c enp4s0d1 -O 37 -m
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

6. ptp4l downstream server service

Create:

sudo nano /etc/systemd/system/ptp4l-downstream-server.service
[Unit]
Description=PTP downstream server on enp4s0 and enp4s0d1
After=network-online.target phc2sys-upstream-to-system.service
Wants=network-online.target
Requires=phc2sys-upstream-to-system.service

[Service]
Type=simple
ExecStart=/usr/sbin/ptp4l -i enp4s0 -i enp4s0d1 -f /etc/linuxptp/ptp4l-downstream-server.conf -m
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

7. Enable in order

sudo systemctl daemon-reload

sudo systemctl enable --now ptp4l-upstream-client.service
sudo systemctl enable --now phc2sys-upstream-to-system.service
sudo systemctl enable --now ptp4l-downstream-server.service
sudo systemctl enable --now phc2sys-system-to-downstream.service

Only enable this one if enp4s0d1 has a different PHC:

sudo systemctl enable --now phc2sys-system-to-downstream-d1.service

8. Disable other system-clock discipliners on WOLF-HUNTER

On this boundary host, do not let chrony/NTP/timesyncd also adjust CLOCK_REALTIME while phc2sys-upstream-to-system is doing it:

sudo systemctl disable --now chrony 2>/dev/null
sudo systemctl disable --now systemd-timesyncd 2>/dev/null
sudo systemctl disable --now ntp 2>/dev/null

9. Verify

systemctl status ptp4l-upstream-client.service
systemctl status phc2sys-upstream-to-system.service
systemctl status ptp4l-downstream-server.service
systemctl status phc2sys-system-to-downstream.service

Logs:

journalctl -u ptp4l-upstream-client.service -f
journalctl -u phc2sys-upstream-to-system.service -f
journalctl -u ptp4l-downstream-server.service -f
journalctl -u phc2sys-system-to-downstream.service -f

Expected behavior:

ptp4l-upstream-client:
  eno4 should go SLAVE / UNCALIBRATED -> SLAVE

phc2sys-upstream-to-system:
  CLOCK_REALTIME phc offset ... s2 ...

ptp4l-downstream-server:
  enp4s0/enp4s0d1 should assume MASTER/server role

phc2sys-system-to-downstream:
  enp4s0 sys offset ... s2 ...

Also useful:

pgrep -a ptp4l
pgrep -a phc2sys