Add BMCA support for IEEE 802.1AS-2011

According to IEEE 802.1AS-2011, the BMCA used in gPTP is the same
as that used in IEEE 1588 with the following exceptions:

(1) Announce messages received on a slave port that were not sent
by the receiving time-aware system are used immediately,
i.e., there is no foreign-master qualification.

(2) A port that the BMCA determines should be a master port enters
the master state immediately, i.e., there is no pre-master state.

(3) The uncalibrated state is not needed and, therefore, not used.

(4) All time-aware systems are required to participate in best master
selection (even if it is not grandmaster capable).

This patch is to support (1) by using a specific FOREIGN_MASTER_THRESHOLD
case. (Treat FOREIGN_MASTER_THRESHOLD as 1.)
To support (2) and (3), reuse ptp_fsm and drop pre-master/uncalibrated
states. The (4) item is supported since IEEE 802.1AS reuses OC/BC.

Signed-off-by: Erik Hons <erik.hons@ni.com>
Signed-off-by: Rodney Greenstreet <rodney.greenstreet@ni.com>
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
master
Yangbo Lu 2019-10-23 18:08:36 +08:00 committed by Vladimir Oltean
parent 1486660e95
commit f8ffb2cf5a
3 changed files with 62 additions and 18 deletions

13
fsm.c
View File

@ -335,3 +335,16 @@ enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event,
return next; return next;
} }
enum port_state ieee8021as_fsm(enum port_state state, enum fsm_event event,
int mdiff)
{
enum port_state next = ptp_fsm(state, event, mdiff);
if (next == PS_UNCALIBRATED)
return PS_SLAVE;
if (next == PS_PRE_MASTER)
return PS_MASTER;
return next;
}

10
fsm.h
View File

@ -79,4 +79,14 @@ enum port_state ptp_fsm(enum port_state state, enum fsm_event event, int mdiff);
enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event, enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event,
int mdiff); int mdiff);
/**
* Run the state machine for a ieee8021as port.
* @param state The current state of the port.
* @param event The event to be processed.
* @param mdiff Whether a new master has been selected.
* @return The new state for the port.
*/
enum port_state ieee8021as_fsm(enum port_state state, enum fsm_event event,
int mdiff);
#endif #endif

55
port.c
View File

@ -278,12 +278,16 @@ void fc_clear(struct foreign_clock *fc)
static void fc_prune(struct foreign_clock *fc) static void fc_prune(struct foreign_clock *fc)
{ {
int threshold = FOREIGN_MASTER_THRESHOLD;
struct timespec now; struct timespec now;
struct ptp_message *m; struct ptp_message *m;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
while (fc->n_messages > FOREIGN_MASTER_THRESHOLD) { if (port_is_ieee8021as(fc->port))
threshold = 1;
while (fc->n_messages > threshold) {
m = TAILQ_LAST(&fc->messages, messages); m = TAILQ_LAST(&fc->messages, messages);
TAILQ_REMOVE(&fc->messages, m, list); TAILQ_REMOVE(&fc->messages, m, list);
fc->n_messages--; fc->n_messages--;
@ -339,6 +343,7 @@ void ts_add(tmv_t *ts, Integer64 correction)
*/ */
static int add_foreign_master(struct port *p, struct ptp_message *m) static int add_foreign_master(struct port *p, struct ptp_message *m)
{ {
int threshold = FOREIGN_MASTER_THRESHOLD;
struct foreign_clock *fc; struct foreign_clock *fc;
struct ptp_message *tmp; struct ptp_message *tmp;
int broke_threshold = 0, diff = 0; int broke_threshold = 0, diff = 0;
@ -362,7 +367,8 @@ static int add_foreign_master(struct port *p, struct ptp_message *m)
LIST_INSERT_HEAD(&p->foreign_masters, fc, list); LIST_INSERT_HEAD(&p->foreign_masters, fc, list);
fc->port = p; fc->port = p;
fc->dataset.sender = m->header.sourcePortIdentity; fc->dataset.sender = m->header.sourcePortIdentity;
/* We do not count this first message, see 9.5.3(b) */ /* For 1588, we do not count this first message, see 9.5.3(b) */
if (!port_is_ieee8021as(fc->port))
return 0; return 0;
} }
@ -370,7 +376,11 @@ static int add_foreign_master(struct port *p, struct ptp_message *m)
* If this message breaks the threshold, that is an important change. * If this message breaks the threshold, that is an important change.
*/ */
fc_prune(fc); fc_prune(fc);
if (FOREIGN_MASTER_THRESHOLD - 1 == fc->n_messages) {
if (port_is_ieee8021as(fc->port))
threshold = 1;
if (threshold - 1 == fc->n_messages) {
broke_threshold = 1; broke_threshold = 1;
} }
@ -2445,6 +2455,7 @@ void port_close(struct port *p)
struct foreign_clock *port_compute_best(struct port *p) struct foreign_clock *port_compute_best(struct port *p)
{ {
int (*dscmp)(struct dataset *a, struct dataset *b); int (*dscmp)(struct dataset *a, struct dataset *b);
int threshold = FOREIGN_MASTER_THRESHOLD;
struct foreign_clock *fc; struct foreign_clock *fc;
struct ptp_message *tmp; struct ptp_message *tmp;
@ -2463,7 +2474,10 @@ struct foreign_clock *port_compute_best(struct port *p)
fc_prune(fc); fc_prune(fc);
if (fc->n_messages < FOREIGN_MASTER_THRESHOLD) if (port_is_ieee8021as(fc->port))
threshold = 1;
if (fc->n_messages < threshold)
continue; continue;
if (!p->best) if (!p->best)
@ -3104,19 +3118,6 @@ struct port *port_open(const char *phc_device,
p->master_only = config_get_int(cfg, interface_name(interface), "masterOnly"); p->master_only = config_get_int(cfg, interface_name(interface), "masterOnly");
p->bmca = config_get_int(cfg, interface_name(interface), "BMCA"); p->bmca = config_get_int(cfg, interface_name(interface), "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. */
} else if (!interface_tsinfo_valid(interface)) { } else if (!interface_tsinfo_valid(interface)) {
@ -3169,6 +3170,26 @@ struct port *port_open(const char *phc_device,
p->versionNumber = PTP_VERSION; p->versionNumber = PTP_VERSION;
p->slave_event_monitor = clock_slave_monitor(clock); p->slave_event_monitor = clock_slave_monitor(clock);
if (config_get_int(cfg, p->name, "asCapable") == AS_CAPABLE_TRUE) {
p->asCapable = ALWAYS_CAPABLE;
} else {
p->asCapable = NOT_CAPABLE;
}
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_transport;
}
} else {
p->state_machine = clock_slave_only(clock) ? ptp_slave_fsm :
port_is_ieee8021as(p) ? ieee8021as_fsm : ptp_fsm;
}
if (number && unicast_client_initialize(p)) { if (number && unicast_client_initialize(p)) {
goto err_transport; goto err_transport;
} }