Horloge matérielle (PHC) et phc2sys#

Pourquoi#

Même avec un noyau temps réel bien configuré, un timestamp logiciel (pris au moment où le kernel traite le paquet) accumule 10 à 100 µs de latence non-déterministe entre l’arrivée physique du signal sur le câble réseau et le moment où le timestamp est réellement enregistré : réception PHY, DMA vers la RAM, interruption, traversée de la pile réseau. Pour servir des clients avec une précision meilleure que la dizaine de microsecondes, il faut sortir cette latence de l’équation, en demandant à la carte réseau elle-même d’horodater le paquet, avant tout traitement logiciel.

Comment#

Le PHC (PTP Hardware Clock)#

Le contrôleur réseau du Raspberry Pi 5 expose une horloge matérielle dédiée accessible via /dev/ptp0, le PHC, standardisé par IEEE 1588. Chrony peut l’utiliser directement pour horodater les paquets NTP entrants et sortants au niveau du silicium, indépendamment de la latence du noyau.

# Dans chrony.conf
hwtimestamp end0 rxfilter all txcomp 0 rxcomp 0 minpoll 0

hwtimestamp end0 seul est incomplet : sans ces options explicites, chrony doit deviner les meilleurs réglages, ce qui ne fonctionne pas toujours de façon fiable. rxfilter all demande d’horodater tous les paquets reçus au niveau matériel, pas seulement ceux reconnus comme PTP par le filtre du contrôleur (sans quoi le trafic NTP ordinaire ne bénéficierait pas du timestamp matériel). txcomp 0 active l’horodatage matériel à la complétion de l’émission, sans compensation logicielle additionnelle (0). rxcomp 0 fait de même en réception : aucune correction n’est appliquée après coup, le timestamp matériel brut est pris tel quel. minpoll 0 n’impose aucun plancher artificiel à la fréquence de mesure au-delà du grain standard de 2⁰ = 1 seconde.

phc2sys : maintenir le PHC aligné#

Le PHC a besoin d’être maintenu synchronisé sur l’horloge système (qui, elle, est disciplinée par chrony depuis le PPS). C’est le rôle de phc2sys, du paquet linuxptp :

cat > /etc/systemd/system/phc2sys-chrony.service <<'EOF'
[Unit]
Description=Synchronize PHC to system clock
After=chrony.service
Wants=chrony.service

[Service]
Type=simple
ExecStart=/usr/sbin/phc2sys -s CLOCK_REALTIME -c /dev/ptp0 -O 0 -R 1 -q
Restart=always
RestartSec=5

CPUSchedulingPolicy=fifo
CPUSchedulingPriority=90
CPUAffinity=1

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now phc2sys-chrony.service

-s CLOCK_REALTIME désigne l’horloge système (disciplinée par chrony depuis le PPS) comme source de référence pour le PHC, -c /dev/ptp0 la cible à aligner. -O 0 fixe l’offset TAI-UTC à zéro : ce n’est pas un oubli des secondes intercalaires, c’est chrony (via leap-seconds.list, chapitre dédié) qui gère déjà cet aspect, phc2sys n’a pas à le refaire en double. -q (quiet) supprime la journalisation par défaut, sans quoi le tmpfs /var/log (chapitre Système de fichiers) se remplirait rapidement d’une ligne par mesure.

Erreurs à éviter#

Requires=chrony.service casse silencieusement phc2sys en production. Une dépendance systemd Requires= arrête automatiquement le service dépendant à chaque transition d’état de sa dépendance, y compris un simple redémarrage ou un échec temporaire de chrony. Un incident réel sur ce projet : phc2sys s’arrêtait silencieusement à chaque cycle de chrony, laissant le PHC dériver de 94 µs par seconde sans aucune alerte visible. La solution : Wants=chrony.service (une dépendance souple, qui n’entraîne pas d’arrêt en cascade), combiné à Restart=always pour la résilience en cas de crash réel.

La fréquence de mesure -R de phc2sys doit être réduite, pas maximisée. Le PHC est un compteur matériel unique, lu et écrit simultanément par deux processus concurrents : phc2sys qui l’écrit pour le maintenir aligné, et chrony qui le lit pour horodater les paquets entrants. Le pilote réseau ne sérialise pas explicitement ces accès. Mesures en conditions réelles : à -R 16 (valeur par défaut de linuxptp), seulement 63% des paquets reçoivent un timestamp matériel (le reste bascule silencieusement sur un timestamp logiciel, moins précis) ; à -R 1, ce taux monte à 86%. Réduire la fréquence d’écriture réduit la contention, au prix d’un offset PHC marginalement plus élevé, mais qui reste largement sous la microseconde.

  • S’attendre à un horodatage matériel TX proche de 100%. Sur Pi 5, le pilote réseau ne pose le flag matériel nécessaire à l’horodatage TX que pour les paquets reconnus comme PTP par le filtre matériel, les paquets NTP “normaux” n’en bénéficient pas. C’est un bug documenté du pilote (raspberrypi/linux, issue #5904), pas une erreur de configuration : le taux d’horodatage matériel TX reste proche de 0% quelle que soit la configuration. L’impact réel côté client est modeste (quelques dizaines de microsecondes au pire, largement filtré par la PLL du client).

Vérifier#

# Le PHC doit être détecté sur l'interface
ethtool -T end0
# Doit afficher "PTP Hardware Clock: 0" (ou un autre numéro, pas "none")

# Offset instantané entre le PHC et l'horloge système
phc_ctl /dev/ptp0 cmp
# ex : "offset from CLOCK_REALTIME is 14ns"

Une lecture unique de phc_ctl cmp est un instantané non filtré, donc parfois sujet à un pic ponctuel d’ordonnancement (quelques centaines de ns à quelques µs). Pour un diagnostic fiable, mieux vaut répéter la lecture plusieurs fois avec une priorité temps réel (chrt -f 99) et prendre la médiane, exactement la même logique de filtrage statistique qu’utilise chrony pour le PPS (chapitre Chrony).

# Taux d'horodatage matériel effectif, côté chrony
chronyc -h /run/chrony/chronyd.sock serverstats | grep -i hardware

📚 Pour aller plus loin

Le timestamping matériel est standardisé par IEEE 1588 (Precision Time Protocol, dernière révision 2019). Linux l’expose via l’option de socket SO_TIMESTAMPING (depuis le noyau 2.6.30) : quand chrony active hwtimestamp end0, il demande au noyau, via setsockopt(), que les timestamps de réception et d’émission soient pris directement dans le silicium de la carte réseau plutôt que dans la pile logicielle, une précision typique inférieure à 100 ns une fois la contention PHC/chrony maîtrisée (mesures publiées par M. Lichvar, mainteneur de chrony, Linux.conf.au 2020).