From c9a15e609479cc8bb9bca84b12d349210768e803 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 4 Nov 2011 19:20:23 +0100 Subject: [PATCH] Add the best master clock algorithm. This commit also introduces clock and port objects, but only with the minimal interface needed by the BMC. Signed-off-by: Richard Cochran --- bmc.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ bmc.h | 45 +++++++++++++++++ clock.h | 57 +++++++++++++++++++++ makefile | 2 +- port.h | 41 +++++++++++++++ 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 bmc.c create mode 100644 bmc.h create mode 100644 clock.h create mode 100644 port.h diff --git a/bmc.c b/bmc.c new file mode 100644 index 0000000..25fcfb6 --- /dev/null +++ b/bmc.c @@ -0,0 +1,148 @@ +/** + * @file bmc.c + * @note Copyright (C) 2011 Richard Cochran + * + * 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-1301 USA. + */ +#include + +#include "bmc.h" +#include "ds.h" + +#define A_BETTER 1 +#define B_BETTER -1 + +static int dscmp2(struct dataset *a, struct dataset *b) +{ + int diff; + unsigned int A = a->stepsRemoved, B = b->stepsRemoved; + + if (A + 1 < B) + return A_BETTER; + if (B + 1 < A) + return B_BETTER; + /* + * We ignore the "error-1" conditions mentioned in the + * standard, since there is nothing we can do about it anyway. + */ + if (A < B) + return A_BETTER; + if (A > B) + return B_BETTER; + + diff = memcmp(&a->sender, &b->sender, sizeof(a->sender)); + if (diff < 0) + return A_BETTER; + if (diff > 0) + return B_BETTER; + + if (a->receiver.portNumber < b->receiver.portNumber) + return A_BETTER; + if (a->receiver.portNumber > b->receiver.portNumber) + return B_BETTER; + /* + * If we got this far, it means "error-2" has occured. + */ + return 0; +} + +int dscmp(struct dataset *a, struct dataset *b) +{ + int diff; + + if (a == b) + return 0; + if (a && !b) + return A_BETTER; + if (b && !a) + return B_BETTER; + + diff = memcmp(&a->identity, &b->identity, sizeof(a->identity)); + + if (!diff) + return dscmp2(a, b); + + if (a->priority1 < b->priority1) + return A_BETTER; + if (a->priority1 > b->priority1) + return B_BETTER; + + if (a->quality.clockClass < b->quality.clockClass) + return A_BETTER; + if (a->quality.clockClass > b->quality.clockClass) + return B_BETTER; + + if (a->quality.clockAccuracy < b->quality.clockAccuracy) + return A_BETTER; + if (a->quality.clockAccuracy > b->quality.clockAccuracy) + return B_BETTER; + + if (a->quality.offsetScaledLogVariance < + b->quality.offsetScaledLogVariance) + return A_BETTER; + if (a->quality.offsetScaledLogVariance > + b->quality.offsetScaledLogVariance) + return B_BETTER; + + if (a->priority2 < b->priority2) + return A_BETTER; + if (a->priority2 > b->priority2) + return B_BETTER; + + return diff < 0 ? A_BETTER : B_BETTER; +} + +enum port_state bmc_state_decision(struct clock *c, struct port *r) +{ + struct dataset *clock_ds, *clock_best, *port_best; + enum port_state ps; + + clock_ds = clock_default_ds(c); + clock_best = clock_best_foreign(c); + port_best = port_best_foreign(r); + ps = port_state(r); + + if (!port_best) { + if (PS_LISTENING == ps) + return ps; + else + return PS_FAULTY; + } + + if (!clock_best) + return PS_FAULTY; + + if (clock_class(c) <= 127) { + if (dscmp(clock_ds, port_best) > 0) { + return PS_GRAND_MASTER; /*M1*/ + } else { + return PS_PASSIVE; /*P1*/ + } + } + + if (dscmp(clock_ds, clock_best) > 0) { + return PS_GRAND_MASTER; /*M2*/ + } + + if (clock_best_port(c) == r) { + return PS_SLAVE; /*S1*/ + } + + if (dscmp(clock_best, port_best) > 0) { + return PS_PASSIVE; /*P2*/ + } else { + return PS_MASTER; /*M3*/ + } +} diff --git a/bmc.h b/bmc.h new file mode 100644 index 0000000..81b3ee6 --- /dev/null +++ b/bmc.h @@ -0,0 +1,45 @@ +/** + * @file bmc.h + * @brief Best master clock algorithm + * @note Copyright (C) 2011 Richard Cochran + * + * 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-1301 USA. + */ +#ifndef HAVE_BMC_H +#define HAVE_BMC_H + +#include "clock.h" +#include "port.h" +#include "fsm.h" + +/** + * BMC state decision algorithm. + * @param c The local clock. + * @param r The port in question. + * @return A @ref port_state value as the recommended state. + */ +enum port_state bmc_state_decision(struct clock *c, struct port *r); + +/** + * Compare two data sets. + * @param a A dataset to compare. + * @param b A dataset to compare. + * @return An integer less than, equal to, or greater than zero + * if the dataset @a a is found, respectively, to be + * less than, to match, or be greater than @a b. + */ +int dscmp(struct dataset *a, struct dataset *b); + +#endif diff --git a/clock.h b/clock.h new file mode 100644 index 0000000..660abc1 --- /dev/null +++ b/clock.h @@ -0,0 +1,57 @@ +/** + * @file clock.h + * @note Copyright (C) 2011 Richard Cochran + * + * 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-1301 USA. + */ +#ifndef HAVE_CLOCK_H +#define HAVE_CLOCK_H + +#include "ds.h" + +/** Opaque type. */ +struct clock; + +/** + * Obtains a reference to the best foreign master of a clock. + * @param c The clock instance. + * @return A pointer to the data set of the foreign master, + * or NULL if none has been yet discovered. + */ +struct dataset *clock_best_foreign(struct clock *c); + +/** + * Obtains a reference to the port with the best foreign master. + * @param c The clock instance. + * @return A pointer to the port with the best foreign master, + * or NULL if none has been yet discovered. + */ +struct port *clock_best_port(struct clock *c); + +/** + * Obtain the clockClass attribute from a clock. + * @param c The clock instance. + * @return The value of the clock's class. + */ +UInteger8 clock_class(struct clock *c); + +/** + * Obtains a clock's default data set. + * @param c The clock instance. + * @return A pointer to the data set of the clock. + */ +struct dataset *clock_default_ds(struct clock *c); + +#endif diff --git a/makefile b/makefile index 0d5d9ce..a3cccb0 100644 --- a/makefile +++ b/makefile @@ -24,7 +24,7 @@ CFLAGS = -Wall $(INC) $(DEBUG) LDFLAGS = LDLIBS = -lm -lrt PRG = linuxptp -OBJ = fsm.o phc.o print.o +OBJ = bmc.o fsm.o phc.o print.o SRC = $(OBJ:.o=.c) DEPEND = $(OBJ:.o=.d) diff --git a/port.h b/port.h new file mode 100644 index 0000000..5a542cf --- /dev/null +++ b/port.h @@ -0,0 +1,41 @@ +/** + * @file port.h + * @note Copyright (C) 2011 Richard Cochran + * + * 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-1301 USA. + */ +#ifndef HAVE_PORT_H +#define HAVE_PORT_H + +/** Opaque type. */ +struct port; + +/** + * Returns the dataset from a port's best foreign clock record, if any + * has yet been discovered. + * + * @param port A port instance. + * @return A pointer to a dataset, or NULL. + */ +struct dataset *port_best_foreign(struct port *port); + +/** + * Returns a port's current state. + * @param port A port instance. + * @return One of the @ref port_state values. + */ +enum port_state port_state(struct port *port); + +#endif