On 7/4/2023 3:30 AM, Stephan Wurm wrote:
> Standard IEC 21439-3:2016 Appendix A extends the PTPv2 standard by the
> definition of doubly attached clocks (DAC) via redundant ports (either
> connected by HSR or PRP). Therefore, the state machine is extended by
> state PASSIVE_SLAVE and transition PSLAVE.
>
> In order to take advantage of the DAC feature, two interfaces need to
> be configured as redundant port by explicitly selecting the respective
> other interface via the `paired_interface` configuration option.
>
> The new state is reported as PASSIVE via the management interface,
> remaining compatible with the PTPv2 standard.
>
> Signed-off-by: Stephan Wurm <stephan.w...@a-eberle.de>
> ---
> bmc.c | 10 ++++++++
> clock.c | 4 ++++
> config.c | 1 +
> e2e_tc.c | 1 +
> fsm.c | 71
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> fsm.h | 2 ++
> p2p_tc.c | 2 ++
> port.c | 52 ++++++++++++++++++++++++++++++++++++----
> port.h | 17 +++++++++++++
> port_private.h | 4 ++++
> port_signaling.c | 1 +
> tc.c | 2 ++
> unicast_service.c | 1 +
> util.c | 4 +++-
> 14 files changed, 167 insertions(+), 5 deletions(-)
>
> diff --git a/bmc.c b/bmc.c
> index ebc0789..1e1d83f 100644
> --- a/bmc.c
> +++ b/bmc.c
> @@ -130,12 +130,14 @@ enum port_state bmc_state_decision(struct clock *c,
> struct port *r,
> int (*compare)(struct dataset *a, struct
> dataset *b))
> {
> struct dataset *clock_ds, *clock_best, *port_best;
> + struct port *paired_port;
> enum port_state ps;
>
> clock_ds = clock_default_ds(c);
> clock_best = clock_best_foreign(c);
> port_best = port_best_foreign(r);
> ps = port_state(r);
> + paired_port = port_paired_port(r);
>
> /*
> * This scenario is particularly important in the designated_slave_fsm
> @@ -167,6 +169,14 @@ enum port_state bmc_state_decision(struct clock *c,
> struct port *r,
> return PS_SLAVE; /*S1*/
> }
>
> + /*
> + * This scenario handles the PASSIVE_SLAVE transition according to
> + * IEC 62439-3 standard in case of a doubly attached clock.
> + */
> + if (paired_port && (clock_best_port(c) == paired_port)) {
> + return PS_PASSIVE_SLAVE;
> + }
> +
> if (compare(clock_best, port_best) == A_BETTER_TOPO) {
> return PS_PASSIVE; /*P2*/
> } else {
> diff --git a/clock.c b/clock.c
> index 5a64613..cff2475 100644
> --- a/clock.c
> +++ b/clock.c
> @@ -1009,6 +1009,7 @@ static int clock_add_port(struct clock *c, const char
> *phc_device,
> return -1;
> }
> LIST_FOREACH(piter, &c->ports, list) {
> + port_pair(piter, p);
> lastp = piter;
> }
> if (lastp) {
> @@ -2242,6 +2243,9 @@ static void handle_state_decision_event(struct clock *c)
> clock_update_slave(c);
> event = EV_RS_SLAVE;
> break;
> + case PS_PASSIVE_SLAVE:
> + event = EV_RS_PSLAVE;
> + break;
> default:
> event = EV_FAULT_DETECTED;
> break;
> diff --git a/config.c b/config.c
> index b104f1b..aadd3d9 100644
> --- a/config.c
> +++ b/config.c
> @@ -298,6 +298,7 @@ struct config_item config_tab[] = {
> GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX),
> PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
> PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_MAX),
> + PORT_ITEM_STR("paired_interface", ""),
> PORT_ITEM_INT("path_trace_enabled", 0, 0, 1),
> PORT_ITEM_INT("phc_index", -1, -1, INT_MAX),
> GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX),
> diff --git a/e2e_tc.c b/e2e_tc.c
> index 2f8e821..94099eb 100644
> --- a/e2e_tc.c
> +++ b/e2e_tc.c
> @@ -69,6 +69,7 @@ void e2e_dispatch(struct port *p, enum fsm_event event, int
> mdiff)
> flush_delay_req(p);
> /* fall through */
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> port_set_announce_tmo(p);
> break;
> };
> diff --git a/fsm.c b/fsm.c
> index ce6efad..9679fea 100644
> --- a/fsm.c
> +++ b/fsm.c
> @@ -80,6 +80,9 @@ enum port_state ptp_fsm(enum port_state state, enum
> fsm_event event, int mdiff)
> case EV_RS_PASSIVE:
> next = PS_PASSIVE;
> break;
> + case EV_RS_PSLAVE:
> + next = PS_PASSIVE_SLAVE;
> + break;
> default:
> break;
> }
> @@ -102,6 +105,9 @@ enum port_state ptp_fsm(enum port_state state, enum
> fsm_event event, int mdiff)
> case EV_RS_PASSIVE:
> next = PS_PASSIVE;
> break;
> + case EV_RS_PSLAVE:
> + next = PS_PASSIVE_SLAVE;
> + break;
> default:
> break;
> }
> @@ -122,6 +128,9 @@ enum port_state ptp_fsm(enum port_state state, enum
> fsm_event event, int mdiff)
> case EV_RS_PASSIVE:
> next = PS_PASSIVE;
> break;
> + case EV_RS_PSLAVE:
> + next = PS_PASSIVE_SLAVE;
> + break;
> default:
> break;
> }
> @@ -210,6 +219,40 @@ enum port_state ptp_fsm(enum port_state state, enum
> fsm_event event, int mdiff)
> case EV_RS_PASSIVE:
> next = PS_PASSIVE;
> break;
> + case EV_RS_PSLAVE:
> + next = PS_PASSIVE_SLAVE;
> + break;
> + default:
> + break;
> + }
> + break;
> +
> + case PS_PASSIVE_SLAVE:
> + switch (event) {
> + case EV_DESIGNATED_DISABLED:
> + next = PS_DISABLED;
> + break;
> + case EV_FAULT_DETECTED:
> + next = PS_FAULTY;
> + break;
> + case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> + next = PS_MASTER;
> + break;
> + case EV_SYNCHRONIZATION_FAULT:
> + next = PS_LISTENING;
> + break;
> + case EV_RS_MASTER:
> + next = PS_PRE_MASTER;
> + break;
> + case EV_RS_GRAND_MASTER:
> + next = PS_GRAND_MASTER;
> + break;
> + case EV_RS_PASSIVE:
> + next = PS_PASSIVE;
> + break;
> + case EV_RS_SLAVE:
> + next = PS_SLAVE;
> + break;
> default:
> break;
> }
> @@ -276,6 +319,9 @@ enum port_state ptp_slave_fsm(enum port_state state, enum
> fsm_event event,
> case EV_RS_SLAVE:
> next = PS_UNCALIBRATED;
> break;
> + case EV_RS_PSLAVE:
> + next = PS_PASSIVE_SLAVE;
> + break;
> default:
> break;
> }
> @@ -324,6 +370,31 @@ enum port_state ptp_slave_fsm(enum port_state state,
> enum fsm_event event,
> if (mdiff)
> next = PS_UNCALIBRATED;
> break;
> + case EV_RS_PSLAVE:
> + next = PS_PASSIVE_SLAVE;
> + break;
> + default:
> + break;
> + }
> + break;
> +
> + case PS_PASSIVE_SLAVE:
> + switch (event) {
> + case EV_DESIGNATED_DISABLED:
> + next = PS_DISABLED;
> + break;
> + case EV_FAULT_DETECTED:
> + next = PS_FAULTY;
> + break;
> + case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES:
> + case EV_RS_MASTER:
> + case EV_RS_GRAND_MASTER:
> + case EV_RS_PASSIVE:
> + next = PS_LISTENING;
> + break;
> + case EV_RS_SLAVE:
> + next = PS_SLAVE;
> + break;
> default:
> break;
> }
> diff --git a/fsm.h b/fsm.h
> index 857af05..919e934 100644
> --- a/fsm.h
> +++ b/fsm.h
> @@ -31,6 +31,7 @@ enum port_state {
> PS_PASSIVE,
> PS_UNCALIBRATED,
> PS_SLAVE,
> + PS_PASSIVE_SLAVE, /*according to IEC 62439-3 doubly attached clocks*/
> PS_GRAND_MASTER, /*non-standard extension*/
> };
>
> @@ -53,6 +54,7 @@ enum fsm_event {
> EV_RS_GRAND_MASTER,
> EV_RS_SLAVE,
> EV_RS_PASSIVE,
> + EV_RS_PSLAVE, /*according to IEC 62439-3 doubly attached clocks*/
> };
>
> enum bmca_select {
> diff --git a/p2p_tc.c b/p2p_tc.c
> index 75cb3b9..2aabba8 100644
> --- a/p2p_tc.c
> +++ b/p2p_tc.c
> @@ -37,6 +37,7 @@ static int p2p_delay_request(struct port *p)
> case PS_PASSIVE:
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> case PS_GRAND_MASTER:
> break;
> }
> @@ -85,6 +86,7 @@ void p2p_dispatch(struct port *p, enum fsm_event event, int
> mdiff)
> break;
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> port_set_announce_tmo(p);
> break;
> };
> diff --git a/port.c b/port.c
> index 5803cd3..a739f58 100644
> --- a/port.c
> +++ b/port.c
> @@ -540,7 +540,13 @@ static int net_sync_resp_append(struct port *p, struct
> ptp_message *m)
> head = (struct nsm_resp_tlv_head *) extra->tlv;
> head->type = TLV_PTPMON_RESP;
> head->length = tlv_len - sizeof(head->type) - sizeof(head->length);
> - head->port_state = p->state == PS_GRAND_MASTER ? PS_MASTER : p->state;
> + if (p->state == PS_GRAND_MASTER) {
> + head->port_state = PS_MASTER;
> + } else if (p->state == PS_PASSIVE_SLAVE) {
> + head->port_state = PS_PASSIVE;
> + } else {
> + head->port_state = p->state;
> + }
> head->parent_addr.networkProtocol = paddr->networkProtocol;
> head->parent_addr.addressLength = paddr->addressLength;
> memcpy(head->parent_addr.address, paddr->address, paddr->addressLength);
> @@ -989,6 +995,8 @@ static int port_management_fill_response(struct port
> *target,
> pds->portIdentity = target->portIdentity;
> if (target->state == PS_GRAND_MASTER) {
> pds->portState = PS_MASTER;
> + } else if (target->state == PS_PASSIVE_SLAVE) {
> + pds->portState = PS_PASSIVE;
> } else {
> pds->portState = target->state;
> }
> @@ -1053,10 +1061,13 @@ static int port_management_fill_response(struct port
> *target,
> case MID_PORT_PROPERTIES_NP:
> ppn = (struct port_properties_np *)tlv->data;
> ppn->portIdentity = target->portIdentity;
> - if (target->state == PS_GRAND_MASTER)
> + if (target->state == PS_GRAND_MASTER) {
> ppn->port_state = PS_MASTER;
> - else
> + } else if (target->state == PS_PASSIVE_SLAVE) {
> + ppn->port_state = PS_PASSIVE;
> + } else {
> ppn->port_state = target->state;
> + }
> ppn->timestamping = target->timestamping;
> ts_label = interface_label(target->iface);
> ptp_text_set(&ppn->interface, ts_label);
> @@ -1365,6 +1376,7 @@ static void port_synchronize(struct port *p,
> switch (p->state) {
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> monitor_sync(p->slave_event_monitor,
> clock_parent_identity(p->clock), seqid,
> t1, tmv_add(c1, c2), t2);
> @@ -1816,6 +1828,7 @@ int port_is_enabled(struct port *p)
> case PS_PASSIVE:
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> break;
> }
> return 1;
> @@ -2089,6 +2102,7 @@ int process_announce(struct port *p, struct ptp_message
> *m)
> case PS_PASSIVE:
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> result = update_current_master(p, m);
> break;
> }
> @@ -2228,6 +2242,7 @@ void process_follow_up(struct port *p, struct
> ptp_message *m)
> return;
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> break;
> }
>
> @@ -2453,7 +2468,8 @@ calc:
>
> p->peerMeanPathDelay = tmv_to_TimeInterval(p->peer_delay);
>
> - if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE) {
> + if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE ||
> + p->state == PS_PASSIVE_SLAVE) {
> clock_peer_delay(p->clock, p->peer_delay, t1, t2,
> p->nrate.ratio);
> }
> @@ -2536,6 +2552,7 @@ void process_sync(struct port *p, struct ptp_message *m)
> return;
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> break;
> }
>
> @@ -2676,6 +2693,9 @@ static void port_e2e_transition(struct port *p, enum
> port_state next)
> port_set_announce_tmo(p);
> port_set_delay_tmo(p);
> break;
> + case PS_PASSIVE_SLAVE:
> + port_set_announce_tmo(p);
> + break;
> };
> }
>
> @@ -2718,6 +2738,7 @@ static void port_p2p_transition(struct port *p, enum
> port_state next)
> flush_peer_delay(p);
> /* fall through */
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> port_set_announce_tmo(p);
> break;
> };
> @@ -3406,6 +3427,10 @@ struct port *port_open(const char *phc_device,
> config_get_int(cfg, p->name,
> "power_profile.2017.totalTimeInaccuracy");
> p->slave_event_monitor = clock_slave_monitor(clock);
>
> + p->paired_interface = config_get_string(cfg, p->name,
> "paired_interface");
> + p->prpPairedPort = UINT16_MAX;
> + p->paired_port = NULL;
> +
> if (!port_is_uds(p) && unicast_client_initialize(p)) {
> goto err_transport;
> }
> @@ -3548,3 +3573,22 @@ void port_update_unicast_state(struct port *p)
> p->unicast_state_dirty = false;
> }
> }
> +
> +void port_pair(struct port *p, struct port *o)
> +{
> + if ((strncmp(p->paired_interface, interface_name(o->iface),
> + MAX_IFNAME_SIZE) == 0) &&
> + (strncmp(o->paired_interface, interface_name(p->iface),
> + MAX_IFNAME_SIZE) == 0)) {
> + p->paired_port = o;
> + p->prpPairedPort = portnum(o);
> + o->paired_port = p;
> + o->prpPairedPort = portnum(p);
> + pr_info("Created redundancy pair from ports %s and %s",
> p->name, o->name);
> + }
Given the name "doubly-attached clock" I would have assumed we would
need to check that both ports are tied to the same PTP hardware clock.
Is that not the case?
If they are not tied to the same clock, how do you ensure the two
separate clocks are actually in sync with each other? I guess if both
clocks are synchronized simultaneously with the GM then it would be
within some tolerance, so perhaps thats ok.
Thanks,
Jake
> +}
> +
> +struct port *port_paired_port(struct port *p)
> +{
> + return p->paired_port;
> +}
> diff --git a/port.h b/port.h
> index 57c8c2f..591b377 100644
> --- a/port.h
> +++ b/port.h
> @@ -364,4 +364,21 @@ void tc_cleanup(void);
> */
> void port_update_unicast_state(struct port *p);
>
> +/**
> + * Pair redundant ports according to IEC 62439-3 standard.
> + *
> + * @param port A port instance.
> + * @param port Another port instance.
> + */
> +void port_pair(struct port *p, struct port *o);
> +
> +/**
> + * Get the associated paired port according to IEC 62439-3 standard.
> + *
> + * @param port A port instance.
> + * @return Pointer to the paired port instance,
> + * or NULL if not a doubly attached clock.
> + */
> +struct port *port_paired_port(struct port *p);
> +
> #endif
> diff --git a/port_private.h b/port_private.h
> index 3b02d2f..c99d972 100644
> --- a/port_private.h
> +++ b/port_private.h
> @@ -150,6 +150,10 @@ struct port {
> Integer64 portAsymmetry;
> struct PortStats stats;
> struct PortServiceStats service_stats;
> + /* IEC 62439-3 portDS additions */
> + char *paired_interface;
> + UInteger16 prpPairedPort;
> + struct port *paired_port;
> /* foreignMasterDS */
> LIST_HEAD(fm, foreign_clock) foreign_masters;
> /* TC book keeping */
> diff --git a/port_signaling.c b/port_signaling.c
> index ca4202f..d8b8a2c 100644
> --- a/port_signaling.c
> +++ b/port_signaling.c
> @@ -147,6 +147,7 @@ int process_signaling(struct port *p, struct ptp_message
> *m)
> case PS_PASSIVE:
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> break;
> }
>
> diff --git a/tc.c b/tc.c
> index 1847041..280da0c 100644
> --- a/tc.c
> +++ b/tc.c
> @@ -87,6 +87,7 @@ static int tc_blocked(struct port *q, struct port *p,
> struct ptp_message *m)
> break;
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> break;
> }
> /* Egress state */
> @@ -101,6 +102,7 @@ static int tc_blocked(struct port *q, struct port *p,
> struct ptp_message *m)
> return 1;
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> /* Delay_Req swims against the stream. */
> if (msg_type(m) != DELAY_REQ) {
> return 1;
> diff --git a/unicast_service.c b/unicast_service.c
> index 687468c..3e57806 100644
> --- a/unicast_service.c
> +++ b/unicast_service.c
> @@ -532,6 +532,7 @@ int unicast_service_timer(struct port *p)
> case PS_PASSIVE:
> case PS_UNCALIBRATED:
> case PS_SLAVE:
> + case PS_PASSIVE_SLAVE:
> break;
> case PS_MASTER:
> case PS_GRAND_MASTER:
> diff --git a/util.c b/util.c
> index e204c9c..a3de8c7 100644
> --- a/util.c
> +++ b/util.c
> @@ -45,9 +45,10 @@ const char *ps_str[] = {
> "LISTENING",
> "PRE_MASTER",
> "MASTER",
> - "PASSIVE",
> + "PASSIVE", /*PASSIVE_MASTER*/
> "UNCALIBRATED",
> "SLAVE",
> + "PASSIVE", /*PASSIVE_SLAVE*/
> "GRAND_MASTER",
> };
>
> @@ -69,6 +70,7 @@ const char *ev_str[] = {
> "RS_GRAND_MASTER",
> "RS_SLAVE",
> "RS_PASSIVE",
> + "RS_PSLAVE",
> };
>
> const char *ts_str(enum timestamp_type ts)
>
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel