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,
> + ×tamping, 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