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 ++
 pmc.c             |  4 ++--
 port.c            | 44 +++++++++++++++++++++++++++++++---
 port.h            | 47 ++++++++++++++++++++++++------------
 port_private.h    |  4 ++++
 port_signaling.c  |  1 +
 tc.c              |  2 ++
 unicast_service.c |  1 +
 util.c            |  4 +++-
 15 files changed, 177 insertions(+), 21 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 75d7c40..d52e826 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) {
@@ -2235,6 +2236,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 cb4421f..28beb3b 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/pmc.c b/pmc.c
index 00e691f..af797bb 100644
--- a/pmc.c
+++ b/pmc.c
@@ -463,7 +463,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
                break;
        case MID_PORT_DATA_SET:
                p = (struct portDS *) mgt->data;
-               if (p->portState > PS_SLAVE) {
+               if (p->portState > PS_PASSIVE_SLAVE) {
                        p->portState = 0;
                }
                fprintf(fp, "PORT_DATA_SET "
@@ -494,7 +494,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
                break;
        case MID_PORT_PROPERTIES_NP:
                ppn = (struct port_properties_np *) mgt->data;
-               if (ppn->port_state > PS_SLAVE) {
+               if (ppn->port_state > PS_PASSIVE_SLAVE) {
                        ppn->port_state = 0;
                }
                fprintf(fp, "PORT_PROPERTIES_NP "
diff --git a/port.c b/port.c
index 3453716..d05797e 100644
--- a/port.c
+++ b/port.c
@@ -989,6 +989,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 +1055,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 +1370,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);
@@ -1821,6 +1827,7 @@ int port_is_enabled(struct port *p)
        case PS_PASSIVE:
        case PS_UNCALIBRATED:
        case PS_SLAVE:
+       case PS_PASSIVE_SLAVE:
                break;
        }
        return 1;
@@ -2094,6 +2101,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;
        }
@@ -2234,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;
        }
 
@@ -2455,7 +2464,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);
        }
@@ -2538,6 +2548,7 @@ void process_sync(struct port *p, struct ptp_message *m)
                return;
        case PS_UNCALIBRATED:
        case PS_SLAVE:
+       case PS_PASSIVE_SLAVE:
                break;
        }
 
@@ -2678,6 +2689,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;
        };
 }
 
@@ -2720,6 +2734,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;
        };
@@ -3408,6 +3423,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;
        }
@@ -3550,3 +3569,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);
+       }
+}
+
+struct port *port_paired_port(struct port *p)
+{
+       return p->paired_port;
+}
diff --git a/port.h b/port.h
index 57c8c2f..f1deaf1 100644
--- a/port.h
+++ b/port.h
@@ -205,7 +205,7 @@ void port_notify_event(struct port *p, enum notification 
event);
  * @param phc_device    The name of PHC device as found on the command line.
  * @param phc_index     The PHC device index for the network device.
  * @param timestamping  The timestamping mode for this port.
- * @param number       An arbitrary number assigned to this port.
+ * @param number        An arbitrary number assigned to this port.
  * @param interface     The interface data
  * @param clock         A pointer to the system PTP clock.
  * @return A pointer to an open port on success, or NULL otherwise.
@@ -229,8 +229,8 @@ enum port_state port_state(struct port *port);
 
 /**
  * Return  port's delay mechanism method.
- * @param port A port instance.
- * @return     one of the @ref delay_mechanism values.
+ * @param port  A port instance.
+ * @return      one of the @ref delay_mechanism values.
  */
 enum delay_mechanism port_delay_mechanism(struct port *port);
 
@@ -246,16 +246,16 @@ int port_state_update(struct port *p, enum fsm_event 
event, int mdiff);
 /**
  * Return array of file descriptors for this port. The fault fd is not
  * included.
- * @param port A port instance
- * @return     Array of file descriptors. Unused descriptors are guranteed
- *             to be set to -1.
+ * @param port  A port instance
+ * @return      Array of file descriptors. Unused descriptors are guranteed
+ *              to be set to -1.
  */
 struct fdarray *port_fda(struct port *port);
 
 /**
  * Return file descriptor of the port.
- * @param port A port instance.
- * @return     File descriptor or -1 if not applicable.
+ * @param port  A port instance.
+ * @return      File descriptor or -1 if not applicable.
  */
 int port_fault_fd(struct port *port);
 
@@ -307,10 +307,10 @@ int set_tmo_lin(int fd, int seconds);
  * Sets port's fault file descriptor timer.
  * Passing both 'scale' and 'log_seconds' as zero disables the timer.
  *
- * @param fd           A port instance.
- * @param scale                The multiplicative factor for the timer.
- * @param log_seconds  The exponential factor for the timer.
- * @return             Zero on success, non-zero otherwise.
+ * @param fd            A port instance.
+ * @param scale         The multiplicative factor for the timer.
+ * @param log_seconds   The exponential factor for the timer.
+ * @return              Zero on success, non-zero otherwise.
  */
 int port_set_fault_timer_log(struct port *port,
                             unsigned int scale, int log_seconds);
@@ -319,9 +319,9 @@ int port_set_fault_timer_log(struct port *port,
  * Sets port's fault file descriptor timer.
  * Passing 'seconds' as zero disables the timer.
  *
- * @param fd           A port instance.
- * @param seconds      The timeout value for the timer.
- * @return             Zero on success, non-zero otherwise.
+ * @param fd            A port instance.
+ * @param seconds       The timeout value for the timer.
+ * @return              Zero on success, non-zero otherwise.
  */
 int port_set_fault_timer_lin(struct port *port, int seconds);
 
@@ -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 5a764d6..20a0369 100644
--- a/port_signaling.c
+++ b/port_signaling.c
@@ -148,6 +148,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 2e3830c..789a0bb 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)

-- 
2.34.1



_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to