On 8/1/2020 10:46 AM, Vladimir Oltean wrote:
> This introduces the '-a' option in ts2phc, an option inspired from
> phc2sys that puts the clocks in "automatic" mode. In this mode, ts2phc
> listens, as a PMC, to port state change events from ptp4l, and detects
> which port state machine, if any, has transitioned to PS_SLAVE. That
> port's clock will become the synchronization master for the hierarchy
> described by ts2phc.
> 
> The use case is a multi-switch DSA setup with boundary_clock_jbod, where
> there is only one grandmaster, connected to one switch's port. The other
> switches, connected together through a PPS signal, must adapt themselves
> to this new source of time, while the switch connected to the GM must
> not be synchronized by ts2phc because it is already synchronized by
> ptp4l.
> 
> Signed-off-by: Vladimir Oltean <olte...@gmail.com>
> ---
>  makefile |   3 +-
>  ts2phc.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  ts2phc.h |  11 +++
>  3 files changed, 220 insertions(+), 2 deletions(-)
> 
> diff --git a/makefile b/makefile
> index 27c4d7809553..f9fe12dde170 100644
> --- a/makefile
> +++ b/makefile
> @@ -27,7 +27,8 @@ FILTERS     = filter.o mave.o mmedian.o
>  SERVOS       = linreg.o ntpshm.o nullf.o pi.o servo.o
>  TRANSP       = raw.o transport.o udp.o udp6.o uds.o
>  TS2PHC       = ts2phc.o lstab.o nmea.o serial.o sock.o 
> ts2phc_generic_master.o \
> - ts2phc_master.o ts2phc_phc_master.o ts2phc_nmea_master.o ts2phc_slave.o
> + ts2phc_master.o ts2phc_phc_master.o ts2phc_nmea_master.o ts2phc_slave.o \
> + pmc_common.o transport.o msg.o tlv.o uds.o udp.o udp6.o raw.o
>  OBJ  = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
>   e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o monitor.o msg.o phc.o \
>   port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) \
> diff --git a/ts2phc.c b/ts2phc.c
> index 97e9718c999e..2c8a42c04d0c 100644
> --- a/ts2phc.c
> +++ b/ts2phc.c
> @@ -33,6 +33,91 @@ static void ts2phc_cleanup(struct ts2phc_private *priv)
>       if (priv->cfg) {
>               config_destroy(priv->cfg);
>       }
> +     close_pmc_node(&priv->node);
> +}
> +
> +/* FIXME: Copied from phc2sys */

I assume these FIXME comments are intended because you plan to share
these in the future?

> +static int normalize_state(int state)
> +{
> +     if (state != PS_MASTER && state != PS_SLAVE &&
> +         state != PS_PRE_MASTER && state != PS_UNCALIBRATED) {
> +             /* treat any other state as "not a master nor a slave" */
> +             state = PS_DISABLED;
> +     }
> +     return state;
> +}
> +
> +/* FIXME: Copied from phc2sys */
> +static struct port *port_get(struct ts2phc_private *priv, unsigned int 
> number)
> +{
> +     struct port *p;
> +
> +     LIST_FOREACH(p, &priv->ports, list) {
> +             if (p->number == number)
> +                     return p;
> +     }
> +     return NULL;
> +}
> +
> +/* FIXME: Copied from phc2sys */
> +static int clock_compute_state(struct ts2phc_private *priv,
> +                            struct clock *clock)
> +{
> +     int state = PS_DISABLED;
> +     struct port *p;
> +
> +     LIST_FOREACH(p, &priv->ports, list) {
> +             if (p->clock != clock)
> +                     continue;
> +             /* PS_SLAVE takes the highest precedence, PS_UNCALIBRATED
> +              * after that, PS_MASTER is third, PS_PRE_MASTER fourth and
> +              * all of that overrides PS_DISABLED, which corresponds
> +              * nicely with the numerical values */
> +             if (p->state > state)
> +                     state = p->state;
> +     }
> +     return state;
> +}
> +
> +#define node_to_ts2phc(node) \
> +     container_of(node, struct ts2phc_private, node)
> +
> +static int ts2phc_recv_subscribed(struct pmc_node *node,
> +                               struct ptp_message *msg, int excluded)
> +{
> +     struct ts2phc_private *priv = node_to_ts2phc(node);
> +     int mgt_id, state;
> +     struct portDS *pds;
> +     struct port *port;
> +     struct clock *clock;
> +
> +     mgt_id = get_mgt_id(msg);
> +     if (mgt_id == excluded)
> +             return 0;
> +     switch (mgt_id) {
> +     case TLV_PORT_DATA_SET:
> +             pds = get_mgt_data(msg);
> +             port = port_get(priv, pds->portIdentity.portNumber);
> +             if (!port) {
> +                     pr_info("received data for unknown port %s",
> +                             pid2str(&pds->portIdentity));
> +                     return 1;
> +             }
> +             state = normalize_state(pds->portState);
> +             if (port->state != state) {
> +                     pr_info("port %s changed state",
> +                             pid2str(&pds->portIdentity));
> +                     port->state = state;
> +                     clock = port->clock;
> +                     state = clock_compute_state(priv, clock);
> +                     if (clock->state != state || clock->new_state) {
> +                             clock->new_state = state;
> +                             priv->state_changed = 1;
> +                     }
> +             }
> +             return 1;
> +     }
> +     return 0;
>  }
>  
>  struct servo *servo_add(struct ts2phc_private *priv, struct clock *clock)
> @@ -98,6 +183,105 @@ struct clock *clock_add(struct ts2phc_private *priv, 
> const char *device)
>       return c;
>  }
>  
> +/* FIXME: Copied from phc2sys */
> +static struct port *port_add(struct ts2phc_private *priv, unsigned int 
> number,
> +                          char *device)
> +{
> +     struct clock *c = NULL;
> +     struct port *p, *tmp;
> +
> +     p = port_get(priv, number);
> +     if (p)
> +             return p;
> +     /* port is a new one, look whether we have the device already on
> +      * a different port */
> +     LIST_FOREACH(tmp, &priv->ports, list) {
> +             if (tmp->number == number) {
> +                     c = tmp->clock;
> +                     break;
> +             }
> +     }
> +     if (!c) {
> +             c = clock_add(priv, device);
> +             if (!c)
> +                     return NULL;
> +     }
> +     p = malloc(sizeof(*p));
> +     if (!p) {
> +             pr_err("failed to allocate memory for a port");
> +             return NULL;
> +     }
> +     p->number = number;
> +     p->clock = c;
> +     LIST_INSERT_HEAD(&priv->ports, p, list);
> +     return p;
> +}
> +
> +static int auto_init_ports(struct ts2phc_private *priv)
> +{
> +     int state, timestamping;
> +     int number_ports, res;
> +     char iface[IFNAMSIZ];
> +     struct clock *clock;
> +     struct port *port;
> +     unsigned int i;
> +
> +     while (1) {
> +             if (!is_running())
> +                     return -1;
> +             res = run_pmc_clock_identity(&priv->node, 1000);
> +             if (res < 0)
> +                     return -1;
> +             if (res > 0)
> +                     break;
> +             /* res == 0, timeout */
> +             pr_notice("Waiting for ptp4l...");
> +     }
> +
> +     number_ports = run_pmc_get_number_ports(&priv->node, 1000);
> +     if (number_ports <= 0) {
> +             pr_err("failed to get number of ports");
> +             return -1;
> +     }
> +
> +     res = run_pmc_subscribe(&priv->node, 1000);
> +     if (res <= 0) {
> +             pr_err("failed to subscribe");
> +             return -1;
> +     }
> +
> +     for (i = 1; i <= number_ports; i++) {
> +             res = run_pmc_port_properties(&priv->node, 1000, i, &state,
> +                                           &timestamping, iface);
> +             if (res == -1) {
> +                     /* port does not exist, ignore the port */
> +                     continue;
> +             }
> +             if (res <= 0) {
> +                     pr_err("failed to get port properties");
> +                     return -1;
> +             }
> +             if (timestamping == TS_SOFTWARE) {
> +                     /* ignore ports with software time stamping */
> +                     continue;
> +             }
> +             port = port_add(priv, i, iface);
> +             if (!port)
> +                     return -1;
> +             port->state = normalize_state(state);
> +     }
> +     if (LIST_EMPTY(&priv->clocks)) {
> +             pr_err("no suitable ports available");
> +             return -1;
> +     }
> +     LIST_FOREACH(clock, &priv->clocks, list) {
> +             clock->new_state = clock_compute_state(priv, clock);
> +     }
> +     priv->state_changed = 1;
> +
> +     return 0;
> +}
> +
>  static void usage(char *progname)
>  {
>       fprintf(stderr,
> @@ -124,6 +308,7 @@ static void usage(char *progname)
>  int main(int argc, char *argv[])
>  {
>       int c, err = 0, have_slave = 0, index, print_level;
> +     char uds_local[MAX_IFNAME_SIZE + 1];
>       enum ts2phc_master_type pps_type;
>       struct ts2phc_private priv = {0};
>       char *config = NULL, *progname;
> @@ -131,6 +316,7 @@ int main(int argc, char *argv[])
>       struct config *cfg = NULL;
>       struct interface *iface;
>       struct option *opts;
> +     int autocfg = 0;
>  
>       handle_term_signals();
>  
> @@ -145,7 +331,7 @@ int main(int argc, char *argv[])
>       /* Process the command line arguments. */
>       progname = strrchr(argv[0], '/');
>       progname = progname ? 1 + progname : argv[0];
> -     while (EOF != (c = getopt_long(argc, argv, "c:f:hi:l:mqs:v", opts, 
> &index))) {
> +     while (EOF != (c = getopt_long(argc, argv, "ac:f:hi:l:mqs:v", opts, 
> &index))) {
>               switch (c) {
>               case 0:
>                       if (config_parse_option(cfg, opts[index].name, optarg)) 
> {
> @@ -153,6 +339,9 @@ int main(int argc, char *argv[])
>                               return -1;
>                       }
>                       break;
> +             case 'a':
> +                     autocfg = 1;
> +                     break;
>               case 'c':
>                       if (!config_create_interface(optarg, cfg)) {
>                               fprintf(stderr, "failed to add slave\n");
> @@ -218,6 +407,23 @@ int main(int argc, char *argv[])
>       STAILQ_INIT(&priv.slaves);
>       priv.cfg = cfg;
>  
> +     snprintf(uds_local, sizeof(uds_local), "/var/run/ts2phc.%d",
> +              getpid());
> +
> +     if (autocfg) {
> +             err = init_pmc_node(cfg, &priv.node, uds_local,
> +                                 ts2phc_recv_subscribed);
> +             if (err) {
> +                     ts2phc_cleanup(&priv);
> +                     return -1;
> +             }
> +             err = auto_init_ports(&priv);
> +             if (err) {
> +                     ts2phc_cleanup(&priv);
> +                     return -1;
> +             }
> +     }
> +
>       STAILQ_FOREACH(iface, &cfg->interfaces, list) {
>               if (1 == config_get_int(cfg, interface_name(iface), 
> "ts2phc.master")) {
>                       if (pps_source) {
> diff --git a/ts2phc.h b/ts2phc.h
> index 135b795f11dc..51ac366c4145 100644
> --- a/ts2phc.h
> +++ b/ts2phc.h
> @@ -23,6 +23,7 @@
>  
>  #include <sys/queue.h>
>  #include <time.h>
> +#include "pmc_common.h"
>  #include "servo.h"
>  
>  struct ts2phc_slave_array;
> @@ -43,13 +44,23 @@ struct clock {
>       int is_destination;
>  };
>  
> +struct port {
> +     LIST_ENTRY(port) list;
> +     unsigned int number;
> +     int state;
> +     struct clock *clock;
> +};
> +
>  struct ts2phc_private {
>       struct ts2phc_master *master;
>       STAILQ_HEAD(slave_ifaces_head, ts2phc_slave) slaves;
>       unsigned int n_slaves;
>       struct ts2phc_slave_array *polling_array;
>       struct config *cfg;
> +     struct pmc_node node;
> +     int state_changed;
>       struct clock *source;
> +     LIST_HEAD(port_head, port) ports;
>       LIST_HEAD(clock_head, clock) clocks;
>  };
>  
> 


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

Reply via email to