Add BMCA config option.

This adds config option to specify static roles for master and slave
in the Best Master Clock Algorithm. This is the case for Automotive
Profile where networks are mostly static and role for each device is
known in advance.

masterOnly and slaveOnly will be used to determine the roles for the
devices. Since masterOnly is a per-port config and slaveOnly is a global
config option, role assignment will be slightly odd in case of bridges.
If slaveOnly is set to 1, all the ports will be in slave roles except
for the ones where masterOnly is set to 1. These ports will assume the
master role.

Two new FSMs which will be used for master and slave roles for this
config option have also been added.

Signed-off-by: Vedang Patel <vedang.patel@intel.com>
master
Vedang Patel 2018-10-03 09:41:50 -07:00 committed by Richard Cochran
parent 3f764aec6a
commit 83be05256b
11 changed files with 241 additions and 11 deletions

11
bmc.c
View File

@ -126,6 +126,17 @@ enum port_state bmc_state_decision(struct clock *c, struct port *r,
port_best = port_best_foreign(r); port_best = port_best_foreign(r);
ps = port_state(r); ps = port_state(r);
/*
* This scenario is particularly important in the designated_slave_fsm
* when it is in PS_SLAVE state. In this scenario, there is no other
* foreign master and it will elect itself as master ultimately
* resulting in printing out some unnecessary warnings (see
* port_slave_priority_warning()).
*/
if (!port_best && port_bmca(r) == BMCA_NOOP) {
return ps;
}
if (!port_best && PS_LISTENING == ps) if (!port_best && PS_LISTENING == ps)
return ps; return ps;

View File

@ -202,11 +202,18 @@ static struct config_enum as_capable_enu[] = {
{ NULL, 0 }, { NULL, 0 },
}; };
static struct config_enum bmca_enu[] = {
{ "ptp", BMCA_PTP },
{ "noop", BMCA_NOOP },
{ NULL, 0 },
};
struct config_item config_tab[] = { struct config_item config_tab[] = {
PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX), PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX),
PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu), PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu),
GLOB_ITEM_INT("assume_two_step", 0, 0, 1), GLOB_ITEM_INT("assume_two_step", 0, 0, 1),
PORT_ITEM_INT("boundary_clock_jbod", 0, 0, 1), PORT_ITEM_INT("boundary_clock_jbod", 0, 0, 1),
PORT_ITEM_ENU("BMCA", BMCA_PTP, bmca_enu),
GLOB_ITEM_INT("check_fup_sync", 0, 0, 1), GLOB_ITEM_INT("check_fup_sync", 0, 0, 1),
GLOB_ITEM_INT("clockAccuracy", 0xfe, 0, UINT8_MAX), GLOB_ITEM_INT("clockAccuracy", 0xfe, 0, UINT8_MAX),
GLOB_ITEM_INT("clockClass", 248, 0, UINT8_MAX), GLOB_ITEM_INT("clockClass", 248, 0, UINT8_MAX),

View File

@ -33,6 +33,7 @@ neighborPropDelayThresh 20000000
masterOnly 0 masterOnly 0
G.8275.portDS.localPriority 128 G.8275.portDS.localPriority 128
asCapable auto asCapable auto
BMCA ptp
# #
# Run time options # Run time options
# #

107
designated_fsm.c 100644
View File

@ -0,0 +1,107 @@
/**
* @file designated_fsm.c
* @brief Implements designated Finite State Machines.
* @note Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA.
*/
#include "fsm.h"
#include "designated_fsm.h"
enum port_state designated_master_fsm(enum port_state state,
enum fsm_event event,
int mdiff)
{
enum port_state next = state;
if (EV_INITIALIZE == event || EV_POWERUP == event)
return PS_INITIALIZING;
switch (state) {
case PS_INITIALIZING:
switch (event) {
case EV_FAULT_DETECTED:
next = PS_FAULTY;
break;
case EV_INIT_COMPLETE:
next = PS_MASTER;
break;
default:
break;
}
break;
case PS_FAULTY:
if (event == EV_FAULT_CLEARED) {
next = PS_INITIALIZING;
}
break;
case PS_MASTER:
if (event == EV_FAULT_DETECTED) {
next = PS_FAULTY;
}
break;
default:
break;
}
return next;
}
enum port_state designated_slave_fsm(enum port_state state,
enum fsm_event event,
int mdiff)
{
enum port_state next = state;
if (EV_INITIALIZE == event || EV_POWERUP == event)
return PS_INITIALIZING;
switch (state) {
case PS_INITIALIZING:
switch (event) {
case EV_FAULT_DETECTED:
next = PS_FAULTY;
break;
case EV_INIT_COMPLETE:
next = PS_SLAVE;
break;
default:
break;
}
break;
case PS_FAULTY:
if (event == EV_FAULT_CLEARED) {
next = PS_INITIALIZING;
}
break;
case PS_SLAVE:
switch (event) {
case EV_FAULT_DETECTED:
next = PS_FAULTY;
break;
default:
break;
}
break;
default:
break;
}
return next;
}

43
designated_fsm.h 100644
View File

@ -0,0 +1,43 @@
/**
* @file designated_fsm.c
* @brief Implements designated Finite State Machines.
* @note Copyright (C) 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA.
*/
#ifndef HAVE_DESIGNATED_FSM_H
#define HAVE_DESIGNATED_FSM_H
/**
* Run the state machine for a clock which is designated as master port.
* @param state The current state of the port.
* @param event The event to be processed.
* @param mdiff This param is not used by this function.
* @return The new state for the port.
*/
enum port_state designated_master_fsm(enum port_state state,
enum fsm_event event,
int mdiff);
/**
* Run the state machine for a clock designated as slave port.
* @param state The current state of the port.
* @param event The event to be processed.
* @param mdiff This param is not used by this function.
* @return The new state for the port.
*/
enum port_state designated_slave_fsm(enum port_state state,
enum fsm_event event,
int mdiff);
#endif

5
fsm.h
View File

@ -55,6 +55,11 @@ enum fsm_event {
EV_RS_PASSIVE, EV_RS_PASSIVE,
}; };
enum bmca_select {
BMCA_PTP,
BMCA_NOOP,
};
/** /**
* Run the state machine for a BC or OC port. * Run the state machine for a BC or OC port.
* @param state The current state of the port. * @param state The current state of the port.

View File

@ -23,11 +23,12 @@ VER = -DVER=$(version)
CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS)
LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) LDLIBS = -lm -lrt $(EXTRA_LDFLAGS)
PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster
OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o e2e_tc.o fault.o \ OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \
filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o nullf.o phc.o \ e2e_tc.o fault.o filter.o fsm.o hash.o linreg.o mave.o mmedian.o msg.o ntpshm.o \
pi.o port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o raw.o rtnl.o \ nullf.o phc.o pi.o port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o \
servo.o sk.o stats.o tc.o telecom.o tlv.o transport.o tsproc.o udp.o udp6.o \ raw.o rtnl.o servo.o sk.o stats.o tc.o telecom.o tlv.o transport.o tsproc.o \
uds.o unicast_client.o unicast_fsm.o unicast_service.o util.o version.o udp.o udp6.o uds.o unicast_client.o unicast_fsm.o unicast_service.o util.o \
version.o
OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \
sysoff.o timemaster.o sysoff.o timemaster.o

41
port.c
View File

@ -27,6 +27,7 @@
#include "bmc.h" #include "bmc.h"
#include "clock.h" #include "clock.h"
#include "designated_fsm.h"
#include "filter.h" #include "filter.h"
#include "missing.h" #include "missing.h"
#include "msg.h" #include "msg.h"
@ -1596,7 +1597,6 @@ int port_initialize(struct port *p)
p->transportSpecific = config_get_int(cfg, p->name, "transportSpecific"); p->transportSpecific = config_get_int(cfg, p->name, "transportSpecific");
p->transportSpecific <<= 4; p->transportSpecific <<= 4;
p->match_transport_specific = !config_get_int(cfg, p->name, "ignore_transport_specific"); p->match_transport_specific = !config_get_int(cfg, p->name, "ignore_transport_specific");
p->master_only = config_get_int(cfg, p->name, "masterOnly");
p->localPriority = config_get_int(cfg, p->name, "G.8275.portDS.localPriority"); p->localPriority = config_get_int(cfg, p->name, "G.8275.portDS.localPriority");
p->logSyncInterval = config_get_int(cfg, p->name, "logSyncInterval"); p->logSyncInterval = config_get_int(cfg, p->name, "logSyncInterval");
p->logMinPdelayReqInterval = config_get_int(cfg, p->name, "logMinPdelayReqInterval"); p->logMinPdelayReqInterval = config_get_int(cfg, p->name, "logMinPdelayReqInterval");
@ -1635,6 +1635,14 @@ int port_initialize(struct port *p)
/* No need to open rtnl socket on UDS port. */ /* No need to open rtnl socket on UDS port. */
if (transport_type(p->trp) != TRANS_UDS) { if (transport_type(p->trp) != TRANS_UDS) {
/*
* The delay timer is usually started when the device
* transitions to PS_LISTENING. But, we are skipping the state
* when BMCA == 'noop'. So, start the timer here.
*/
if (p->bmca == BMCA_NOOP) {
port_set_delay_tmo(p);
}
if (p->fda.fd[FD_RTNL] == -1) if (p->fda.fd[FD_RTNL] == -1)
p->fda.fd[FD_RTNL] = rtnl_open(); p->fda.fd[FD_RTNL] = rtnl_open();
if (p->fda.fd[FD_RTNL] >= 0) if (p->fda.fd[FD_RTNL] >= 0)
@ -2469,6 +2477,16 @@ static enum fsm_event bc_event(struct port *p, int fd_index)
if (p->best) if (p->best)
fc_clear(p->best); fc_clear(p->best);
port_set_announce_tmo(p); port_set_announce_tmo(p);
/*
* Clear out the event returned by poll(). It is only cleared
* in port_*_transition(). But, when BMCA == 'noop', there is no
* state transition. So, it won't be cleared anywhere else.
*/
if (p->bmca == BMCA_NOOP) {
port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
}
delay_req_prune(p); delay_req_prune(p);
if (clock_slave_only(p->clock) && p->delayMechanism != DM_P2P && if (clock_slave_only(p->clock) && p->delayMechanism != DM_P2P &&
port_renew_transport(p)) { port_renew_transport(p)) {
@ -2859,10 +2877,24 @@ struct port *port_open(int phc_index,
goto err_port; goto err_port;
} }
p->state_machine = clock_slave_only(clock) ? ptp_slave_fsm : ptp_fsm;
p->phc_index = phc_index; p->phc_index = phc_index;
p->jbod = config_get_int(cfg, interface->name, "boundary_clock_jbod"); p->jbod = config_get_int(cfg, interface->name, "boundary_clock_jbod");
transport = config_get_int(cfg, interface->name, "network_transport"); transport = config_get_int(cfg, interface->name, "network_transport");
p->master_only = config_get_int(cfg, p->name, "masterOnly");
p->bmca = config_get_int(cfg, p->name, "BMCA");
if (p->bmca == BMCA_NOOP && transport != TRANS_UDS) {
if (p->master_only) {
p->state_machine = designated_master_fsm;
} else if (clock_slave_only(clock)) {
p->state_machine = designated_slave_fsm;
} else {
pr_err("Please enable at least one of masterOnly or slaveOnly when BMCA == noop.\n");
goto err_port;
}
} else {
p->state_machine = clock_slave_only(clock) ? ptp_slave_fsm : ptp_fsm;
}
if (transport == TRANS_UDS) { if (transport == TRANS_UDS) {
; /* UDS cannot have a PHC. */ ; /* UDS cannot have a PHC. */
@ -3020,3 +3052,8 @@ int port_state_update(struct port *p, enum fsm_event event, int mdiff)
return 0; return 0;
} }
enum bmca_select port_bmca(struct port *p)
{
return p->bmca;
}

8
port.h
View File

@ -322,6 +322,14 @@ enum fault_type last_fault_type(struct port *port);
void fault_interval(struct port *port, enum fault_type ft, void fault_interval(struct port *port, enum fault_type ft,
struct fault_interval *i); struct fault_interval *i);
/**
* Obtain the BMCA type of the port.
*
* @param port A port instance.
* @return bmca type.
*/
enum bmca_select port_bmca(struct port *p);
/** /**
* Release all of the memory in the TC transmit descriptor cache. * Release all of the memory in the TC transmit descriptor cache.
*/ */

View File

@ -97,6 +97,7 @@ struct port {
unsigned int multiple_pdr_detected; unsigned int multiple_pdr_detected;
enum port_state (*state_machine)(enum port_state state, enum port_state (*state_machine)(enum port_state state,
enum fsm_event event, int mdiff); enum fsm_event event, int mdiff);
int bmca;
/* portDS */ /* portDS */
struct PortIdentity portIdentity; struct PortIdentity portIdentity;
enum port_state state; /*portState*/ enum port_state state; /*portState*/

17
ptp4l.8
View File

@ -363,10 +363,7 @@ hardware time stamping.
The default is 1 (enabled). The default is 1 (enabled).
.TP .TP
.B slaveOnly .B slaveOnly
The local clock is a slave-only clock if enabled. The local clock is a slave-only clock if enabled. The default is 0 (disabled).
This option is only for use with 1588 clocks and should not be enabled
for 802.1AS clocks.
The default is 0 (disabled).
.TP .TP
.B gmCapable .B gmCapable
If this option is enabled, then the local clock is able to become grand master. If this option is enabled, then the local clock is able to become grand master.
@ -692,6 +689,18 @@ If set to 'true', all the checks which can unset asCapable variable (as
described in Section 10.2.4.1 of 802.1AS) are skipped. If set to 'auto', described in Section 10.2.4.1 of 802.1AS) are skipped. If set to 'auto',
asCapable is initialized to 'false' and will be set to 'true' after the asCapable is initialized to 'false' and will be set to 'true' after the
relevant checks have passed. The default value is 'auto'. relevant checks have passed. The default value is 'auto'.
.TP
.B BMCA
This option enables use of static roles for master and slave devices instead of
running the best master clock algorithm (BMCA) described in 1588 profile. This
is useful when you know the roles of the devices in advance. When set to
\'noop', the traditional BMCA algorithm used by 1588 is skipped. masterOnly and
slaveOnly will be used to determine master or slave role for the device. In a
bridge, slaveOnly (which is a global option) can be set to make all ports
assume the slave role. masterOnly (which is a per-port config option) can then
be used to set individual ports to take master role. BMCA is used in the
Automotive profile to speed up the start time for grand master and slaves. The
default value is 'ptp' which runs the BMCA related state machines.
.SH UNICAST DISCOVERY OPTIONS .SH UNICAST DISCOVERY OPTIONS