clock, port: Fix BMCA oscillation and duplicate Sync in HSR/PRP mode
Three independent bugs cause a redundant OC slave to fail to sync when
connected to an HSR ring:
1. BMCA flip-flop between ring paths (clock.c):
The master has two GM ports (portNumber 1 and 2). HSR frame
duplication causes every slave port to receive Announce messages from
both master ports via both the direct path (stepsRemoved=0) and the
ring path (stepsRemoved=1). Whichever copy happens to arrive last is
stored as the active dataset, so stepsRemoved oscillates cycle-by-cycle.
dscmp2() uses stepsRemoved as a tiebreaker, so the globally-best
foreign_clock pointer alternates between eth1 and eth2 every Announce
period, firing "selected best master clock" twice per second and keeping
the application in perpetual UNCALIBRATED.
Fix: in handle_state_decision_event(), track whether c->best is still
returned by any port's port_compute_best() call (current_best_alive).
If the same grandmaster is still reachable and the current best FC is
still alive, keep it to prevent stepsRemoved-driven flip-flopping.
2. Rogue PDelayResp causing port FAULTY (port.c):
HSR duplicates all frames onto both ring legs. When eth1 completes a
PDelayReq/PDelayResp exchange, the PDelayResp is also forwarded to
eth2. Because eth2 has no pending peer_delay_req for that exchange,
ptp4l treats the message as a rogue peer delay response and raises a
FAULT_DETECTED event, forcing eth2 into FAULTY state.
Fix: in process_pdelay_resp(), when the port is one half of a redundant
pair (p->paired_port != NULL), silently discard PDelayResp messages
whose requestingPortIdentity does not match the receiving port's own
identity.
3. Duplicate Sync updates driving servo instability (port.c):
HSR duplicates Sync frames onto both ring legs. The same Sync message
(same sequenceId) arrives twice on the active slave port with slightly
different hardware RX timestamps. Each copy triggers clock_synchronize()
independently, so the servo runs twice per interval with conflicting t2
values, producing large oscillating offsets (hundreds of microseconds).
Fix: in process_sync(), when p->paired_port is set, track the last
processed Sync sequenceId (last_sync_seqid in port_private.h). Discard
any Sync with the same sequenceId as the last accepted one, ensuring
clock_synchronize() is called exactly once per Sync interval.
Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
3 files changed