Commit cdf616d8 authored by Sven Meier's avatar Sven Meier Committed by Adam Wujek

bmc: add agging, call periodically and data sets

BMC fixed to be called periodically and data sets comparisments fixed
also changed the foreign master table adding and agging and some
minor adpatations in state pre-master
parent ed394bc1
......@@ -55,7 +55,7 @@ static int run_all_state_machines(struct pp_globals *ppg)
else {
ppi->n_ops->exit(ppi);
ppi->frgn_rec_num = 0;
ppi->frgn_rec_best = -1;
ppi->frgn_rec_best = 0;
if (ppg->ebest_idx == ppi->port_idx)
wr_servo_reset(ppi);
}
......
......@@ -167,7 +167,7 @@ static int type_length[__PP_NR_MESSAGES_TYPES] = {
[PPM_DELAY_RESP] = PP_DELAY_RESP_LENGTH,
[PPM_PDELAY_R_FUP] = PP_PDELAY_R_FUP_LENGTH,
[PPM_ANNOUNCE] = PP_ANNOUNCE_LENGTH,
[PPM_SIGNALING] = PP_HEADER_LENGTH,
[PPM_SIGNALING] = PP_HEADER_LENGTH,
[PPM_MANAGEMENT] = PP_MANAGEMENT_LENGTH,
};
......@@ -253,11 +253,18 @@ int pp_state_machine(struct pp_instance *ppi, uint8_t *packet, int plen)
plen = 0;
}
}
/* run bmc independent of state, and since not message driven do this
* here 9.2.6.8 */
if (pp_timeout(ppi, PP_TO_BMC))
ppi->next_state = bmc(ppi);
if (ppi->state != ppi->next_state)
return leave_current_state(ppi);
if (!plen)
ppi->received_ptp_header.messageType = PPM_NO_MESSAGE;
err = ip->f1(ppi, packet, plen);
if (err)
pp_printf("fsm for %s: Error %i in %s\n",
......@@ -268,5 +275,10 @@ int pp_state_machine(struct pp_instance *ppi, uint8_t *packet, int plen)
return leave_current_state(ppi);
pp_diag_fsm(ppi, ip->name, STATE_LOOP, 0);
/* check if the BMC timeout is the next to run */
if (pp_next_delay_1(ppi, PP_TO_BMC) < ppi->next_delay)
ppi->next_delay = pp_next_delay_1(ppi, PP_TO_BMC);
return ppi->next_delay;
}
......@@ -23,11 +23,11 @@
#define PP_DEFAULT_AP 10
#define PP_DEFAULT_AI 1000
#define PP_DEFAULT_DELAY_S 6
#define PP_DEFAULT_ANNOUNCE_INTERVAL 1 /* 0 in 802.1AS */
#define PP_DEFAULT_ANNOUNCE_INTERVAL 1 /* 0 in 802.1AS */
#define PP_DEFAULT_DELAYREQ_INTERVAL 0
#define PP_DEFAULT_SYNC_INTERVAL 0 /* -7 in 802.1AS */
#define PP_DEFAULT_SYNC_INTERVAL 0 /* -7 in 802.1AS */
#define PP_DEFAULT_SYNC_RECEIPT_TIMEOUT 3
#define PP_DEFAULT_ANNOUNCE_RECEIPT_TIMEOUT 20 /* 3 by default */
#define PP_DEFAULT_ANNOUNCE_RECEIPT_TIMEOUT 3 /* 3 by default */
/* Clock classes (pag 55, PTP-2008). See ppsi-manual for an explanation */
#define PP_CLASS_SLAVE_ONLY 255
......@@ -43,12 +43,15 @@
* same value as in ptpdv1
*/
#define PP_NR_FOREIGN_RECORDS 5
#define PP_FOREIGN_MASTER_TIME_WINDOW 4
#define PP_FOREIGN_MASTER_THRESHOLD 2
#define PP_DEFAULT_TTL 1
/* We use an array of timeouts, with these indexes */
enum pp_timeouts {
PP_TO_REQUEST = 0,
PP_TO_SYNC_SEND,
PP_TO_BMC,
PP_TO_ANN_RECEIPT,
PP_TO_ANN_SEND,
PP_TO_FAULT,
......
......@@ -45,7 +45,7 @@ typedef struct Integer64 {
typedef struct UInteger64 {
uint32_t lsb;
uint32_t msb;
uint32_t msb;
} UInteger64;
struct TimeInterval { /* page 12 (32) -- never used */
......@@ -54,7 +54,7 @@ struct TimeInterval { /* page 12 (32) -- never used */
/* White Rabbit extension */
typedef struct FixedDelta {
UInteger64 scaledPicoseconds;
UInteger64 scaledPicoseconds;
} FixedDelta;
typedef struct Timestamp { /* page 13 (33) -- no typedef expected */
......@@ -217,7 +217,7 @@ typedef struct DSCurrent { /* page 67 */
typedef struct DSParent { /* page 68 */
/* Dynamic */
PortIdentity parentPortIdentity;
/* Boolean parentStats; -- not used */
/* Boolean parentStats; -- not used */
UInteger16 observedParentOffsetScaledLogVariance;
Integer32 observedParentClockPhaseChangeRate;
ClockIdentity grandmasterIdentity;
......
......@@ -65,6 +65,11 @@ struct pp_channel {
*/
struct pp_frgn_master {
PortIdentity port_id; /* used to identify old/new masters */
PortIdentity source_id; /* used for the dataset comparisment */
/* how many announce messages from this port where received in the
* interval */
int ann_cnt[PP_FOREIGN_MASTER_TIME_WINDOW];
/* We don't need all fields of the following ones */
MsgAnnounce ann;
......
......@@ -231,6 +231,7 @@ extern struct pp_time_operations unix_time_ops;
*/
extern void pp_timeout_init(struct pp_instance *ppi);
extern void __pp_timeout_set(struct pp_instance *ppi, int index, int millisec);
extern void pp_timeout_clear(struct pp_instance *ppi, int index);
extern void pp_timeout_set(struct pp_instance *ppi, int index);
extern void pp_timeout_setall(struct pp_instance *ppi);
extern int pp_timeout(struct pp_instance *ppi, int index)
......@@ -360,7 +361,9 @@ extern void pp_servo_got_presp(struct pp_instance *ppi); /* got all t3..t6 */
/* bmc.c */
extern void m1(struct pp_instance *ppi);
extern int bmc(struct pp_instance *ppi);
extern int bmc_dataset_cmp(struct pp_instance *ppi,
struct pp_frgn_master *a,
struct pp_frgn_master *b);
/* msg.c */
extern void msg_init_header(struct pp_instance *ppi, void *buf);
extern int __attribute__((warn_unused_result))
......@@ -383,7 +386,7 @@ extern void msg_unpack_pdelay_req(void *buf, MsgPDelayReq * pdelay_req);
#define PP_SEND_ERROR -1
#define PP_SEND_NO_STAMP 1
#define PP_SEND_DROP -2
#define PP_RECV_DROP PP_SEND_DROP
#define PP_RECV_DROP PP_SEND_DROP
extern void *msg_copy_header(MsgHeader *dest, MsgHeader *src); /* REMOVE ME!! */
extern int msg_issue_announce(struct pp_instance *ppi);
......
......@@ -11,8 +11,8 @@
/* Please increment WRS_PPSI_SHMEM_VERSION if you change any exported data
* structure */
#define WRS_PPSI_SHMEM_VERSION 20 /* Replace cField, t4_cf and t6_cf with
syncCF */
#define WRS_PPSI_SHMEM_VERSION 21 /* Added source_id and ann_cnt to struct
* pp_frgn_master */
/* Don't include the Following when this file is included in assembler. */
#ifndef __ASSEMBLY__
......
......@@ -43,6 +43,40 @@ void m1(struct pp_instance *ppi)
DSPRO(ppi)->timeSource = INTERNAL_OSCILLATOR;
}
/* ppi->port_idx port is becoming Master. Table 13 (9.3.5) of the spec. */
static void m2(struct pp_instance *ppi)
{
struct DSParent *parent = DSPAR(ppi);
struct DSDefault *defds = DSDEF(ppi);
/* Current data set update */
DSCUR(ppi)->stepsRemoved = 0;
clear_time(&DSCUR(ppi)->offsetFromMaster);
clear_time(&DSCUR(ppi)->meanPathDelay);
/* Parent data set: we are the parent */
memset(parent, 0, sizeof(*parent));
parent->parentPortIdentity.clockIdentity = defds->clockIdentity;
parent->parentPortIdentity.portNumber = 0;
/* Copy grandmaster params from our defds (FIXME: is ir right?) */
parent->grandmasterIdentity = defds->clockIdentity;
parent->grandmasterClockQuality = defds->clockQuality;
parent->grandmasterPriority1 = defds->priority1;
parent->grandmasterPriority2 = defds->priority2;
/* Time Properties data set */
DSPRO(ppi)->ptpTimescale = TRUE;
DSPRO(ppi)->timeSource = INTERNAL_OSCILLATOR;
}
/* ppi->port_idx port is becoming Master. Table 14 (9.3.5) of the spec. */
static void m3(struct pp_instance *ppi)
{
/* In the default implementation, nothing should be done when a port
* goes to master state at m3. This empty function is a placeholder for
* extension-specific needs, to be implemented as a hook */
}
/* ppi->port_idx port is synchronized to Ebest Table 16 (9.3.5) of the spec. */
static void s1(struct pp_instance *ppi, MsgHeader *hdr, MsgAnnounce *ann)
......@@ -68,9 +102,7 @@ static void s1(struct pp_instance *ppi, MsgHeader *hdr, MsgAnnounce *ann)
prop->currentUtcOffset = ann->currentUtcOffset;
ppi->t_ops->set(ppi, NULL);
}
/* FIXME: can't we just copy the bit keeping values? */
prop->currentUtcOffsetValid = ((hdr->flagField[1] & FFB_UTCV) != 0);
prop->currentUtcOffsetValid = ((hdr->flagField[1] & FFB_UTCV) != 0);
prop->leap59 = ((hdr->flagField[1] & FFB_LI59) != 0);
prop->leap61 = ((hdr->flagField[1] & FFB_LI61) != 0);
prop->timeTraceable = ((hdr->flagField[1] & FFB_TTRA) != 0);
......@@ -81,25 +113,44 @@ static void s1(struct pp_instance *ppi, MsgHeader *hdr, MsgAnnounce *ann)
pp_hooks.s1(ppi, hdr, ann);
}
static void p1(struct pp_instance *ppi, MsgHeader *hdr, MsgAnnounce *ann)
static void p1(struct pp_instance *ppi)
{
/* In the default implementation, nothing should be done when a port
* goes to passive state. This empty function is a placeholder for
* extension-specific needs, to be implemented as a hook */
}
static void p2(struct pp_instance *ppi)
{
/* In the default implementation, nothing should be done when a port goes
* to passive state. This empty function is a placeholder for
/* In the default implementation, nothing should be done when a port
* goes to passive state. This empty function is a placeholder for
* extension-specific needs, to be implemented as a hook */
}
/* Copy local data set into header and ann message. 9.3.4 table 12. */
static void copy_d0(struct pp_instance *ppi, struct pp_frgn_master *m)
{
int i;
struct DSDefault *defds = DSDEF(ppi);
struct PortIdentity *port_id = &m->port_id;
struct PortIdentity *source_id = &m->source_id;
int *ann_cnt = m->ann_cnt;
struct MsgHeader *hdr = &m->hdr;
struct MsgAnnounce *ann = &m->ann;
*port_id = DSPOR(ppi)->portIdentity;
*source_id = DSPOR(ppi)->portIdentity;
/* this shall be always qualified */
for (i = 0; i < PP_FOREIGN_MASTER_TIME_WINDOW; i++)
ann_cnt[i] = 1;
ann->grandmasterIdentity = defds->clockIdentity;
ann->grandmasterClockQuality = defds->clockQuality;
ann->grandmasterPriority1 = defds->priority1;
ann->grandmasterPriority2 = defds->priority2;
ann->stepsRemoved = 0;
hdr->sourcePortIdentity.clockIdentity = defds->clockIdentity;
}
......@@ -108,63 +159,54 @@ static int idcmp(struct ClockIdentity *a, struct ClockIdentity *b)
return memcmp(a, b, sizeof(*a));
}
/*
* Data set comparison between two foreign masters. Return similar to
* memcmp(). However, lower values take precedence, so in A-B (like
* in comparisons, > 0 means B wins (and < 0 means A wins).
*/
static int bmc_dataset_cmp(struct pp_instance *ppi,
static int pidcmp(struct PortIdentity *a, struct PortIdentity *b)
{
return memcmp(a, b, sizeof(*a));
}
/* compare part2 of the datasets which is the topology, fig 27, page 89 */
static int bmc_gm_cmp(struct pp_instance *ppi,
struct pp_frgn_master *a,
struct pp_frgn_master *b)
{
int i;
struct ClockQuality *qa, *qb;
struct MsgAnnounce *aa = &a->ann;
struct MsgAnnounce *ab = &b->ann;
struct ClockIdentity *ida = &a->hdr.sourcePortIdentity.clockIdentity;
struct ClockIdentity *idb = &b->hdr.sourcePortIdentity.clockIdentity;
struct ClockIdentity *idparent;
int diff;
int *ca = a->ann_cnt;
int *cb = b->ann_cnt;
int qualifieda = 0;
int qualifiedb = 0;
/* dataset_cmp is called several times, so report only at level 2 */
pp_diag(ppi, bmc, 2,"%s\n", __func__);
if (!idcmp(&aa->grandmasterIdentity, &ab->grandmasterIdentity)) {
/* bmc_gm_cmp is called several times, so report only at level 2 */
pp_diag(ppi, bmc, 2, "%s\n", __func__);
/* The grandmaster is the same: part 2, fig 28, page 90. */
diff = aa->stepsRemoved - ab->stepsRemoved;
if (diff > 1 || diff < -1)
return diff;
for (i = 0; i < PP_FOREIGN_MASTER_TIME_WINDOW; i++) {
qualifieda += ca[i];
qualifiedb += cb[i];
}
idparent = &DSPAR(ppi)->parentPortIdentity.clockIdentity;
/* if B is not qualified 9.3.2.5 c) & 9.3.2.3 a) & b)*/
if ((qualifieda >= PP_FOREIGN_MASTER_THRESHOLD)
&& (qualifiedb < PP_FOREIGN_MASTER_THRESHOLD)) {
pp_diag(ppi, bmc, 2, "Dataset B not qualified\n");
return -1;
}
if (diff > 0) {
if (!idcmp(ida, idparent)) {
pp_diag(ppi, bmc, 1,"%s:%i: Error 1\n",
__func__, __LINE__);
return 0;
}
return 1;
/* if a is not qualified 9.3.2.5 c) & 9.3.2.3 a) & b) */
if ((qualifiedb >= PP_FOREIGN_MASTER_THRESHOLD)
&& (qualifieda < PP_FOREIGN_MASTER_THRESHOLD)) {
pp_diag(ppi, bmc, 2, "Dataset A not qualified\n");
return 1;
}
}
if (diff < 0) {
if (!idcmp(idb, idparent)) {
pp_diag(ppi, bmc, 1,"%s:%i: Error 1\n",
__func__, __LINE__);
return 0;
}
return -1;
}
/* stepsRemoved is equal, compare identities */
diff = idcmp(ida, idb);
if (!diff) {
pp_diag(ppi, bmc, 1,"%s:%i: Error 2\n", __func__, __LINE__);
return 0;
}
return diff;
/* if both are not qualified 9.3.2.5 c) & 9.3.2.3 a) & b) */
if ((qualifieda < PP_FOREIGN_MASTER_THRESHOLD)
&& (qualifiedb < PP_FOREIGN_MASTER_THRESHOLD)) {
pp_diag(ppi, bmc, 2, "Dataset A & B not qualified\n");
return 0;
}
/* The grandmasters are different: part 1, fig 27, page 89. */
qa = &aa->grandmasterClockQuality;
qb = &ab->grandmasterClockQuality;
......@@ -178,7 +220,8 @@ static int bmc_dataset_cmp(struct pp_instance *ppi,
return qa->clockAccuracy - qb->clockAccuracy;
if (qa->offsetScaledLogVariance != qb->offsetScaledLogVariance)
return qa->clockClass - qb->clockClass;
return qa->offsetScaledLogVariance
- qb->offsetScaledLogVariance;
if (aa->grandmasterPriority2 != ab->grandmasterPriority2)
return aa->grandmasterPriority2 - ab->grandmasterPriority2;
......@@ -186,103 +229,440 @@ static int bmc_dataset_cmp(struct pp_instance *ppi,
return idcmp(&aa->grandmasterIdentity, &ab->grandmasterIdentity);
}
/* State decision algorithm 9.3.3 Fig 26 */
static int bmc_state_decision(struct pp_instance *ppi,
struct pp_frgn_master *m)
/* compare part2 of the datasets which is the topology, fig 28, page 90 */
static int bmc_topology_cmp(struct pp_instance *ppi,
struct pp_frgn_master *a,
struct pp_frgn_master *b)
{
int cmpres, ret;
struct pp_frgn_master myself;
int i;
struct MsgAnnounce *aa = &a->ann;
struct MsgAnnounce *ab = &b->ann;
struct PortIdentity *pidtxa = &a->hdr.sourcePortIdentity;
struct PortIdentity *pidtxb = &b->hdr.sourcePortIdentity;
struct PortIdentity *pidrxa = &a->source_id;
struct PortIdentity *pidrxb = &b->source_id;
int *ca = a->ann_cnt;
int *cb = b->ann_cnt;
int qualifieda = 0;
int qualifiedb = 0;
int diff;
/* bmc_topology_cmp is called several times, so report only at level 2
*/
pp_diag(ppi, bmc, 2, "%s\n", __func__);
for (i = 0; i < PP_FOREIGN_MASTER_TIME_WINDOW; i++) {
qualifieda += ca[i];
qualifiedb += cb[i];
}
/* if B is not qualified 9.3.2.5 c) & 9.3.2.3 a) & b)*/
if ((qualifieda >= PP_FOREIGN_MASTER_THRESHOLD)
&& (qualifiedb < PP_FOREIGN_MASTER_THRESHOLD)) {
pp_diag(ppi, bmc, 2, "Dataset B not qualified\n");
return -1;
}
/* if a is not qualified 9.3.2.5 c) & 9.3.2.3 a) & b) */
if ((qualifiedb >= PP_FOREIGN_MASTER_THRESHOLD)
&& (qualifieda < PP_FOREIGN_MASTER_THRESHOLD)) {
pp_diag(ppi, bmc, 2, "Dataset A not qualified\n");
return 1;
}
/* if both are not qualified 9.3.2.5 c) & 9.3.2.3 a) & b) */
if ((qualifieda < PP_FOREIGN_MASTER_THRESHOLD)
&& (qualifiedb < PP_FOREIGN_MASTER_THRESHOLD)) {
pp_diag(ppi, bmc, 2, "Dataset A & B not qualified\n");
return 0;
}
diff = aa->stepsRemoved - ab->stepsRemoved;
if (diff > 1 || diff < -1)
return diff;
if (diff > 0) {
if (!pidcmp(pidtxa, pidrxa)) {
pp_diag(ppi, bmc, 1, "%s:%i: Error 1\n",
__func__, __LINE__);
return 0;
}
return 1;
if (ppi->role == PPSI_ROLE_SLAVE)
goto slave;
}
if (diff < 0) {
if (!pidcmp(pidtxb, pidrxb)) {
pp_diag(ppi, bmc, 1, "%s:%i: Error 1\n",
__func__, __LINE__);
return 0;
}
return -1;
}
/* stepsRemoved is equal, compare identities */
diff = pidcmp(pidtxa, pidtxb);
if (diff)
return diff;
/* sourcePortIdentity is equal, compare receive port identites, which
* is the last decision maker, which has to be different */
return pidcmp(pidrxa, pidrxb);
}
/*
* Data set comparison between two foreign masters. Return similar to
* memcmp(). However, lower values take precedence, so in A-B (like
* in comparisons, > 0 means B wins (and < 0 means A wins).
*/
int bmc_dataset_cmp(struct pp_instance *ppi,
struct pp_frgn_master *a,
struct pp_frgn_master *b)
{
struct MsgAnnounce *aa = &a->ann;
struct MsgAnnounce *ab = &b->ann;
/* dataset_cmp is called several times, so report only at level 2 */
pp_diag(ppi, bmc, 2, "%s\n", __func__);
if (!idcmp(&aa->grandmasterIdentity, &ab->grandmasterIdentity)) {
/* Check topology */
return bmc_topology_cmp(ppi, a, b);
} else {
/* Check grandmasters */
return bmc_gm_cmp(ppi, a, b);
}
}
/* State decision algorithm 9.3.3 Fig 26 */
static int bmc_state_decision(struct pp_instance *ppi)
{
int cmpres;
struct pp_frgn_master d0;
struct pp_globals *ppg = GLBS(ppi);
struct pp_instance *ppi_best;
struct pp_frgn_master *erbest = &ppi->frgn_master[ppi->frgn_rec_best];
struct pp_frgn_master *ebest;
/* bmc_state_decision is called several times, so report only at
* level 2 */
pp_diag(ppi, bmc, 2, "%s\n", __func__);
if (ppi->role == PPSI_ROLE_SLAVE) {
/* if on this conigured port is ebest it will be taken as
* parent */
ebest = erbest;
goto slave_s1;
}
if ((!ppi->frgn_rec_num) && (ppi->state == PPS_LISTENING))
return PPS_LISTENING;
/* copy local information to a foreign_master structure */
copy_d0(ppi, &myself);
copy_d0(ppi, &d0);
if (ppi->role == PPSI_ROLE_MASTER) {
/* if there is a better master show these values */
if (ppg->ebest_idx >= 0) {
/* don't update parent dataset */
goto master_m3;
} else {
/* provide our info */
goto master_m1;
}
}
/* dataset_cmp is "a - b" but lower values win */
cmpres = bmc_dataset_cmp(ppi, &myself, m);
if (ppi->role == PPSI_ROLE_MASTER)
goto master;
/* if there is a foreign master take it otherwise just go to master */
if (ppg->ebest_idx >= 0) {
ppi_best = INST(ppg, ppg->ebest_idx);
ebest = &ppi_best->frgn_master[ppi_best->frgn_rec_best];
pp_diag(ppi, bmc, 2, "Taking real Ebest at port %i foreign "
"master %i/%i\n", (ppg->ebest_idx+1),
ppi_best->frgn_rec_best, ppi_best->frgn_rec_num);
} else {
/* directly go to master state */
pp_diag(ppi, bmc, 2, "No real Ebest\n");
goto master_m1;
}
if (DSDEF(ppi)->clockQuality.clockClass < 128) {
/* dataset_cmp D0 with Erbest */
cmpres = bmc_dataset_cmp(ppi, &d0, erbest);
if (cmpres < 0)
goto master;
goto master_m1;
if (cmpres > 0)
goto passive;
goto passive_p1;
} else {
/* dataset_cmp D0 with Ebest */
cmpres = bmc_dataset_cmp(ppi, &d0, ebest);
if (cmpres < 0)
goto master_m2;
if (cmpres > 0) {
if (DSDEF(ppi)->numberPorts == 1)
goto slave_s1; /* directly skip to ordinary
* clock handling */
else
goto check_boundary_clk;
}
}
pp_diag(ppi, bmc, 1, "%s: error\n", __func__);
return PPS_FAULTY;
check_boundary_clk:
/* If this port is the Ebest */
if (ppi->port_idx == GLBS(ppi)->ebest_idx)
goto slave_s1;
/* bmc_gm_cmp Ebest with Erbest */
cmpres = bmc_gm_cmp(ppi, ebest, erbest);
if (cmpres < 0)
goto master;
if (cmpres > 0) {
if (DSDEF(ppi)->numberPorts == 1)
goto slave; /* directly skip to ordinary clock handling */
else
goto check_boundary_clk;
}
goto master_m3;
pp_diag(ppi, bmc, 1,"%s: error\n", __func__);
/* topology_cmp Ebest with Erbest */
cmpres = bmc_topology_cmp(ppi, ebest, erbest);
if (cmpres < 0)
goto passive_p2;
if (cmpres > 0)
goto master_m3;
pp_diag(ppi, bmc, 1, "%s: error\n", __func__);
/* MB: Is this the return code below correct? */
/* Anyway, it's a valid return code. */
return PPS_FAULTY;
check_boundary_clk:
if (ppi->port_idx == GLBS(ppi)->ebest_idx) /* This port is the Ebest */
goto slave;
/* If idcmp returns 0, it means that this port is not the best because
* Ebest is better by topology than Erbest */
if (!idcmp(&myself.ann.grandmasterIdentity,
&m->ann.grandmasterIdentity))
goto passive;
else
goto master;
passive:
p1(ppi, &m->hdr, &m->ann);
pp_diag(ppi, bmc, 1,"%s: passive\n", __func__);
passive_p1:
p1(ppi);
pp_diag(ppi, bmc, 1, "%s: passive p1\n", __func__);
return PPS_PASSIVE;
master:
//TODO: consider whether a smarter solution is needed for non-simple cases
if(cmpres < 0) { // it is M1 and M2, see IEEE1588-2008, page 87, in short switch is a GM
m1(ppi); //GM
ret = PPS_MASTER;
passive_p2:
p2(ppi);
pp_diag(ppi, bmc, 1, "%s: passive p2\n", __func__);
return PPS_PASSIVE;
master_m1:
m1(ppi);
pp_diag(ppi, bmc, 1, "%s: master m1\n", __func__);
if (ppi->state != PPS_MASTER) {
/* if not already in pre master state start qualification */
if (ppi->state != PPS_PRE_MASTER) {
/* 9.2.6.11 a) timeout 0 */
pp_timeout_clear(ppi, PP_TO_QUALIFICATION);
}
return PPS_PRE_MASTER;
} else
ret = PPS_PRE_MASTER;
pp_diag(ppi, bmc, 1,"%s: %smaster\n", __func__,
ret == PPS_PRE_MASTER ? "pre-" : "");
return ret;
slave:
s1(ppi, &m->hdr, &m->ann);
pp_diag(ppi, bmc, 1,"%s: slave\n", __func__);
return PPS_MASTER;
master_m2:
m2(ppi);
pp_diag(ppi, bmc, 1, "%s: master m2\n", __func__);
if (ppi->state != PPS_MASTER) {
/* if not already in pre master state start qualification */
if (ppi->state != PPS_PRE_MASTER) {
/* 9.2.6.11 a) timeout 0 */
pp_timeout_clear(ppi, PP_TO_QUALIFICATION);
}
return PPS_PRE_MASTER;
} else
return PPS_MASTER;
master_m3:
m3(ppi);
pp_diag(ppi, bmc, 1, "%s: master m3\n", __func__);
if (ppi->state != PPS_MASTER) {
/* if not already in pre master state start qualification */
if (ppi->state != PPS_PRE_MASTER) {
/* 9.2.6.11 b) timeout steps removed+1 */
pp_timeout_set(ppi, PP_TO_QUALIFICATION);
}
return PPS_PRE_MASTER;
} else
return PPS_MASTER;
slave_s1:
/* only update parent dataset if best master is on this port */
if (ppi->port_idx == GLBS(ppi)->ebest_idx)
s1(ppi, &ebest->hdr, &ebest->ann);
pp_diag(ppi, bmc, 1, "%s: slave s1\n", __func__);
return PPS_SLAVE;
}
static void bmc_age_frgn_master(struct pp_instance *ppi)
{
int i, j;
int qualified;
/* bmc_age_frgn_master is called several times, so report only at
* level 2 */
pp_diag(ppi, bmc, 2, "%s\n", __func__);
for (i = 0; i < ppi->frgn_rec_num; i++) {
/* get qualification */
qualified = 0;
for (j = 0; j < PP_FOREIGN_MASTER_TIME_WINDOW; j++)
qualified += ppi->frgn_master[i].ann_cnt[j];
/* shift qualification */
for (j = 1; j < PP_FOREIGN_MASTER_TIME_WINDOW; j++)
ppi->frgn_master[i].ann_cnt[
(PP_FOREIGN_MASTER_TIME_WINDOW - j)] =
ppi->frgn_master[i].ann_cnt[
(PP_FOREIGN_MASTER_TIME_WINDOW - j - 1)];
/* clear lowest */
ppi->frgn_master[i].ann_cnt[0] = 0;
/* remove aged out and shift foreign masters*/
if (qualified == 0) {
for (j = i; j < PP_NR_FOREIGN_RECORDS; j++) {
if (j < (ppi->frgn_rec_num-1)) {
/* overwrite and shift next foreign
* master in */
memcpy(&ppi->frgn_master[j],
&ppi->frgn_master[(j+1)],
sizeof(ppi->frgn_master[j]));
} else {
/* clear the last (and others) since
* shifted */
memset(&ppi->frgn_master[j], 0,
sizeof(ppi->frgn_master[j]));
}
}
pp_diag(ppi, bmc, 1, "Aged out foreign master %i/%i\n",
i, ppi->frgn_rec_num);
/* one less and restart at the shifted one */
ppi->frgn_rec_num--;
i--;
}
}
}
/* Check if any port is in initilaizing state */
static int bmc_any_port_initializing(struct pp_globals *ppg)
{
int i;
struct pp_instance *ppi;
/* bmc_any_port_initializing is called several times, so report only at
* level 2 */
pp_diag(INST(ppg, 0), bmc, 2, "%s\n", __func__);
for (i = 0; i < ppg->defaultDS->numberPorts; i++) {
ppi = INST(ppg, i);
if ((WR_DSPOR(ppi)->linkUP)
&& (ppi->state == PPS_INITIALIZING)) {
pp_diag(ppi, bmc, 2, "The first port in INITIALIZING "
"state is %i\n", i);
return 1;
}
}
return 0;
}
/* Find Erbest, 9.3.2.2 */
static void bmc_update_erbest(struct pp_globals *ppg)
{
int i, j, best;
struct pp_instance *ppi;
struct pp_frgn_master *frgn_master;
struct PortIdentity *frgn_master_pid;
/* bmc_update_erbest is called several times, so report only at
* level 2 */
pp_diag(INST(ppg, 0), bmc, 2, "%s\n", __func__);
for (i = 0; i < ppg->defaultDS->numberPorts; i++) {
ppi = INST(ppg, i);
frgn_master = ppi->frgn_master;
if (ppi->frgn_rec_num > 0) {
/* Only if port is not in the FAULTY or DISABLED
* state 9.2.6.8 */
if ((ppi->state != PPS_FAULTY)
&& (ppi->state != PPS_DISABLED)) {
for (j = 1, best = 0; j < ppi->frgn_rec_num;
j++)
if (bmc_dataset_cmp(ppi,
&frgn_master[j],
&frgn_master[best]
) < 0)
best = j;
pp_diag(ppi, bmc, 1, "Best foreign master is "
"at index %i/%i\n", best,
ppi->frgn_rec_num);
frgn_master_pid = &frgn_master[best].hdr.sourcePortIdentity;
pp_diag(ppi, bmc, 3, "SourePortId = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x.%04x\n",
frgn_master_pid->clockIdentity.id[0], frgn_master_pid->clockIdentity.id[1],
frgn_master_pid->clockIdentity.id[2], frgn_master_pid->clockIdentity.id[3],
frgn_master_pid->clockIdentity.id[4], frgn_master_pid->clockIdentity.id[5],
frgn_master_pid->clockIdentity.id[6], frgn_master_pid->clockIdentity.id[7],
frgn_master_pid->portNumber);
ppi->frgn_rec_best = best;
} else {
ppi->frgn_rec_num = 0;
ppi->frgn_rec_best = 0;
memset(&ppi->frgn_master, 0,
sizeof(ppi->frgn_master));
}
} else {
/* lets just set the first one */
ppi->frgn_rec_best = 0;
}
}
}
/* Find Ebest, 9.3.2.2 */
static void bmc_update_ebest(struct pp_globals *ppg)
{
int i, best;
struct pp_instance *ppi, *ppi_best;
struct PortIdentity *frgn_master_pid;
/* bmc_update_ebest is called several times, so report only at
* level 2 */
pp_diag(INST(ppg, 0), bmc, 2, "%s\n", __func__);
for (i = 1, best = 0; i < ppg->defaultDS->numberPorts; i++) {
ppi_best = INST(ppg, best);
ppi = INST(ppg, i);
if ((ppi->frgn_rec_num > 0) &&
(bmc_dataset_cmp(ppi,
&ppi_best->frgn_master[ppi_best->frgn_rec_best],
&ppi->frgn_master[ppi->frgn_rec_best])
< 0))
if ((ppi->frgn_rec_num > 0)
&& (bmc_dataset_cmp(ppi,
&ppi->frgn_master[ppi->frgn_rec_best],
&ppi_best->frgn_master[ppi_best->frgn_rec_best]
) < 0))
best = i;
}
/* check if best master is qualified */
ppi_best = INST(ppg, best);
if (ppi_best->frgn_rec_num == 0) {
pp_diag(ppi_best, bmc, 2, "No Ebest at port %i\n", (best+1));
best = -1;
} else {
pp_diag(ppi_best, bmc, 1, "Best foreign master is at port "
"%i\n", (best+1));
frgn_master_pid = &ppi_best->frgn_master[ppi_best->frgn_rec_best].hdr.sourcePortIdentity;
pp_diag(ppi_best, bmc, 3, "SourePortId = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x.%04x\n",
frgn_master_pid->clockIdentity.id[0], frgn_master_pid->clockIdentity.id[1],
frgn_master_pid->clockIdentity.id[2], frgn_master_pid->clockIdentity.id[3],
frgn_master_pid->clockIdentity.id[4], frgn_master_pid->clockIdentity.id[5],
frgn_master_pid->clockIdentity.id[6], frgn_master_pid->clockIdentity.id[7],
frgn_master_pid->portNumber);
}
if (ppg->ebest_idx != best) {
ppg->ebest_idx = best;
ppg->ebest_updated = 1;
......@@ -291,27 +671,32 @@ static void bmc_update_ebest(struct pp_globals *ppg)
int bmc(struct pp_instance *ppi)
{
struct pp_frgn_master *frgn_master = ppi->frgn_master;
int i, best;
if (!ppi->frgn_rec_num)
if (ppi->state == PPS_MASTER) {
m1(ppi);
return ppi->state;
}
struct pp_globals *ppg = GLBS(ppi);
/* bmc is called several times, so report only at level 2 */
pp_diag(ppi, bmc, 2, "%s\n", __func__);
/* Age table only based on timeouts*/
if (pp_timeout(ppi, PP_TO_BMC)) {
bmc_age_frgn_master(ppi);
/* restart timer, shall occur at
least once per annnounce interval 9.2.6.8
*/
pp_timeout_set(ppi, PP_TO_BMC);
}
/* Find Erbest, 9.3.2.3 */
for (i = 1, best = 0; i < ppi->frgn_rec_num; i++)
if (bmc_dataset_cmp(ppi, &frgn_master[i], &frgn_master[best])
< 0)
best = i;
pp_diag(ppi, bmc, 1,"Best foreign master is %i/%i\n", best,
ppi->frgn_rec_num);
if (ppi->frgn_rec_best != best) {
ppi->frgn_rec_best = best;
bmc_update_ebest(GLBS(ppi));
/* Only if port is not any port is in the INITIALIZING state 9.2.6.8 */
if (bmc_any_port_initializing(ppg)) {
pp_diag(ppi, bmc, 2, "A Port is in intializing\n");
return ppi->state;
}
return bmc_state_decision(ppi, &frgn_master[best]);
/* Calculate Erbest of all ports Figure 25 */
bmc_update_erbest(ppg);
/* Calulate Ebest Figure 25 */
bmc_update_ebest(ppg);
/* Make state decision*/
return bmc_state_decision(ppi);
}
......@@ -85,14 +85,13 @@ int st_com_execute_slave(struct pp_instance *ppi)
if (pp_timeout(ppi, PP_TO_ANN_RECEIPT)
|| pp_timeout(ppi, PP_TO_FAULT)) {
/*
/*
* Note: TO_FAULTY == SYNCHRONIZATION_FAULT
* should move us to UNCALIBRATED (not implemented)
*/
ppi->frgn_rec_num = 0;
if (DSDEF(ppi)->clockQuality.clockClass != PP_CLASS_SLAVE_ONLY
&& (ppi->role != PPSI_ROLE_SLAVE)) {
ppi->next_state = PPS_MASTER;
ppi->next_state = PPS_MASTER;
} else {
ppi->next_state = PPS_LISTENING;
pp_timeout_set(ppi, PP_TO_ANN_RECEIPT);
......
......@@ -111,14 +111,52 @@ int pp_lib_may_issue_request(struct pp_instance *ppi)
/* Called by this file, basically when an announce is got, all states */
static void __lib_add_foreign(struct pp_instance *ppi, unsigned char *buf)
{
int i;
int i, worst, sel;
struct pp_frgn_master frgn_master;
MsgHeader *hdr = &ppi->received_ptp_header;
/* if we are a configured master don't add*/
if (ppi->role == PPSI_ROLE_MASTER)
return;
/*
* header and announce field of each Foreign Master are
* useful to run Best Master Clock Algorithm
*/
msg_copy_header(&frgn_master.hdr, hdr);
msg_unpack_announce(buf, &frgn_master.ann);
/* Copy new foreign master data set from announce message */
memcpy(&frgn_master.port_id,
&hdr->sourcePortIdentity, sizeof(hdr->sourcePortIdentity));
/* Copy the source port identity */
memcpy(&frgn_master.source_id,
&DSPOR(ppi)->portIdentity, sizeof(DSPOR(ppi)->portIdentity));
/* Check if announce from a port from this clock 9.3.2.5 a) */
if (!memcmp(&hdr->sourcePortIdentity.clockIdentity,
&DSDEF(ppi)->clockIdentity,
sizeof(DSDEF(ppi)->clockIdentity)))
return;
/* Check if announce has steps removed larger than 255 9.3.2.5 d) */
if (frgn_master.ann.stepsRemoved >= 255)
return;
/* Check if foreign master is already known */
for (i = 0; i < ppi->frgn_rec_num; i++) {
if (!memcmp(&hdr->sourcePortIdentity,
&ppi->frgn_master[i].port_id,
sizeof(hdr->sourcePortIdentity))) {
pp_diag(ppi, bmc, 2, "Foreign Master %i updated\n", i);
/* update the number of announce received if correct
* sequence number 9.3.2.5 b) */
if (hdr->sequenceId
== (ppi->frgn_master[i].hdr.sequenceId + 1))
ppi->frgn_master[i].ann_cnt[0]++;
/* already in Foreign master data set, update info */
msg_copy_header(&ppi->frgn_master[i].hdr, hdr);
msg_unpack_announce(buf, &ppi->frgn_master[i].ann);
......@@ -126,32 +164,55 @@ static void __lib_add_foreign(struct pp_instance *ppi, unsigned char *buf)
}
}
/* set qualification timeouts as valid to compare against worst*/
for (i = 0; i < PP_FOREIGN_MASTER_TIME_WINDOW; i++)
frgn_master.ann_cnt[i] = 1;
/* New foreign master */
if (ppi->frgn_rec_num < PP_NR_FOREIGN_RECORDS)
if (ppi->frgn_rec_num < PP_NR_FOREIGN_RECORDS) {
/* there is space for a new one */
sel = ppi->frgn_rec_num;
ppi->frgn_rec_num++;
/* FIXME: replace the worst */
i = ppi->frgn_rec_num - 1;
} else {
/* find the worst to replace */
for (i = 1, worst = 0; i < ppi->frgn_rec_num; i++)
if (bmc_dataset_cmp(ppi, &ppi->frgn_master[i],
&ppi->frgn_master[worst]) > 0)
worst = i;
/* check if worst is better than the new one, and skip the new
* one if so */
if (bmc_dataset_cmp(ppi, &ppi->frgn_master[worst], &frgn_master)
< 0) {
pp_diag(ppi, bmc, 1, "%s:%i: New foreign "
"master worse than worst in the full "
"table, skipping\n",
__func__, __LINE__);
return;
}
/* Copy new foreign master data set from announce message */
memcpy(&ppi->frgn_master[i].port_id,
&hdr->sourcePortIdentity, sizeof(hdr->sourcePortIdentity));
sel = worst;
}
/*
* header and announce field of each Foreign Master are
* useful to run Best Master Clock Algorithm
*/
msg_copy_header(&ppi->frgn_master[i].hdr, hdr);
msg_unpack_announce(buf, &ppi->frgn_master[i].ann);
/* clear qualification timeouts */
for (i = 0; i < PP_FOREIGN_MASTER_TIME_WINDOW; i++)
frgn_master.ann_cnt[i] = 0;
/* This is the first one qualified 9.3.2.5 e)*/
frgn_master.ann_cnt[0] = 1;
/* Copy the temporary foreign master entry */
memcpy(&ppi->frgn_master[sel],
&frgn_master, sizeof(frgn_master));
pp_diag(ppi, bmc, 1, "New foreign Master %i added\n", i);
pp_diag(ppi, bmc, 1, "New foreign Master %i added\n", sel);
}
int pp_lib_handle_announce(struct pp_instance *ppi, unsigned char *buf, int len)
{
__lib_add_foreign(ppi, buf);
ppi->next_state = bmc(ppi); /* got a new announce: run bmc */
pp_timeout_set(ppi, PP_TO_ANN_RECEIPT);
if (pp_hooks.handle_announce)
......
......@@ -20,8 +20,6 @@ void pp_servo_init(struct pp_instance *ppi)
int d;
SRV(ppi)->mpd_fltr.s_exp = 0; /* clears meanPathDelay filter */
ppi->frgn_rec_num = 0; /* no known master */
DSPAR(ppi)->parentPortIdentity.portNumber = 0; /* invalid */
if (ppi->t_ops->init_servo) {
/* The system may pre-set us to keep current frequency */
......
......@@ -16,7 +16,7 @@ static void init_parent_ds(struct pp_instance *ppi)
/* 8.2.3.2 */
DSPAR(ppi)->parentPortIdentity.clockIdentity =
DSDEF(ppi)->clockIdentity;
/* FIXME: portNumber ? */
DSPAR(ppi)->parentPortIdentity.portNumber = 0;
/* 8.2.3.3 skipped (parentStats is not used) */
/* 8.2.3.4 */
DSPAR(ppi)->observedParentOffsetScaledLogVariance = 0xffff;
......
......@@ -47,6 +47,10 @@ int pp_master(struct pp_instance *ppi, uint8_t *pkt, int plen)
/* upgrade from pre-master to master */
if (pre && pp_timeout(ppi, PP_TO_QUALIFICATION)) {
ppi->next_state = PPS_MASTER;
/* start sending imediately and reenter */
pp_timeout_clear(ppi, PP_TO_SYNC_SEND);
pp_timeout_clear(ppi, PP_TO_ANN_SEND);
ppi->next_delay = 0;
return 0;
}
......@@ -108,9 +112,13 @@ out:
break;
}
/* we also use TO_QUALIFICATION, but avoid counting it here */
ppi->next_delay = pp_next_delay_3(ppi,
PP_TO_ANN_SEND, PP_TO_SYNC_SEND, PP_TO_REQUEST);
if (pre) {
ppi->next_delay = pp_next_delay_2(ppi,
PP_TO_QUALIFICATION, PP_TO_REQUEST);
} else {
ppi->next_delay = pp_next_delay_3(ppi,
PP_TO_ANN_SEND, PP_TO_SYNC_SEND, PP_TO_REQUEST);
}
return e;
}
......@@ -22,6 +22,7 @@ struct timeout_config {
static struct timeout_config to_configs[__PP_TO_ARRAY_SIZE] = {
[PP_TO_REQUEST] = {"REQUEST", RAND_0_200,},
[PP_TO_SYNC_SEND] = {"SYNC_SEND", RAND_70_130,},
[PP_TO_BMC] = {"BMC", RAND_NONE,},
[PP_TO_ANN_RECEIPT] = {"ANN_RECEIPT", RAND_NONE,},
[PP_TO_ANN_SEND] = {"ANN_SEND", RAND_70_130,},
[PP_TO_FAULT] = {"FAULT", RAND_NONE, 4000},
......@@ -40,6 +41,7 @@ void pp_timeout_init(struct pp_instance *ppi)
to_configs[PP_TO_FAULT].value =
1 << (port->logMinDelayReqInterval + 12); /* 0 -> 4096ms */
to_configs[PP_TO_SYNC_SEND].value = port->logSyncInterval;
to_configs[PP_TO_BMC].value = 1000 * (1 << port->logAnnounceInterval);
to_configs[PP_TO_ANN_RECEIPT].value = 1000 * (
port->announceReceiptTimeout << port->logAnnounceInterval);
to_configs[PP_TO_ANN_SEND].value = port->logAnnounceInterval;
......@@ -54,6 +56,10 @@ void __pp_timeout_set(struct pp_instance *ppi, int index, int millisec)
to_configs[index].name, millisec);
}
void pp_timeout_clear(struct pp_instance *ppi, int index)
{
__pp_timeout_set(ppi, index, 0);
}
void pp_timeout_set(struct pp_instance *ppi, int index)
{
......@@ -111,8 +117,11 @@ void pp_timeout_set(struct pp_instance *ppi, int index)
void pp_timeout_setall(struct pp_instance *ppi)
{
int i;
for (i = 0; i < __PP_TO_ARRAY_SIZE; i++)
pp_timeout_set(ppi, i);
for (i = 0; i < __PP_TO_ARRAY_SIZE; i++) {
/* keep BMC timeout */
if (i != PP_TO_BMC)
pp_timeout_set(ppi, i);
}
/* but announce_send must be send soon */
__pp_timeout_set(ppi, PP_TO_ANN_SEND, 20);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment