Discussion:
[Linuxptp-devel] [PATCHv2 RFC 0/2] Add clock sanity check.
Miroslav Lichvar
2013-10-22 16:57:13 UTC
Permalink
Changes in v2:
- the clock sanity check moved to its own module
- the check is called in ptp4l from port when the time stamp is made,
in phc2sys when the clock is updated
- the code makes the check only if the local interval is longer that
the minimum to avoid frequent reading of CLOCK_MONOTONIC
- because the clock's frequency now changes during the check interval,
the code makes the check with both minimum and maximum frequency
from the interval
- added option to set the limit in phc2sys

Check the sanity of the synchronized clock by comparing its uncorrected
frequency with the system monotonic clock. When the measured frequency
offset is larger than the value of the sanity_freq_limit option (20% by
default), a warning message will be printed and the servo will be reset.
Setting the option to zero disables the check.

This is useful to detect when the clock is broken or adjusted by another
program.

Miroslav Lichvar (2):
Add reset function to servo.
Add clock sanity check.

clock.c | 25 +++++++++++
clock.h | 7 ++++
clockcheck.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
clockcheck.h | 64 ++++++++++++++++++++++++++++
config.c | 6 +++
config.h | 2 +
default.cfg | 1 +
ds.h | 1 +
gPTP.cfg | 1 +
makefile | 12 +++---
phc2sys.8 | 9 ++++
phc2sys.c | 24 ++++++++++-
pi.c | 8 ++++
port.c | 3 ++
ptp4l.8 | 7 ++++
ptp4l.c | 1 +
servo.c | 5 +++
servo.h | 6 +++
servo_private.h | 2 +
19 files changed, 305 insertions(+), 7 deletions(-)
create mode 100644 clockcheck.c
create mode 100644 clockcheck.h
--
1.8.3.1
Miroslav Lichvar
2013-10-22 16:57:14 UTC
Permalink
This will be used with clock sanity checking.

Signed-off-by: Miroslav Lichvar <***@redhat.com>
---
pi.c | 8 ++++++++
servo.c | 5 +++++
servo.h | 6 ++++++
servo_private.h | 2 ++
4 files changed, 21 insertions(+)

diff --git a/pi.c b/pi.c
index 3c8a635..bd78e40 100644
--- a/pi.c
+++ b/pi.c
@@ -171,6 +171,13 @@ static void pi_sync_interval(struct servo *servo, double interval)
interval, s->kp, s->ki);
}

+static void pi_reset(struct servo *servo)
+{
+ struct pi_servo *s = container_of(servo, struct pi_servo, servo);
+
+ s->count = 0;
+}
+
struct servo *pi_servo_create(int fadj, int max_ppb, int sw_ts)
{
struct pi_servo *s;
@@ -182,6 +189,7 @@ struct servo *pi_servo_create(int fadj, int max_ppb, int sw_ts)
s->servo.destroy = pi_destroy;
s->servo.sample = pi_sample;
s->servo.sync_interval = pi_sync_interval;
+ s->servo.reset = pi_reset;
s->drift = fadj;
s->maxppb = max_ppb;
s->first_update = 1;
diff --git a/servo.c b/servo.c
index 45c5832..5af020a 100644
--- a/servo.c
+++ b/servo.c
@@ -46,3 +46,8 @@ void servo_sync_interval(struct servo *servo, double interval)
{
servo->sync_interval(servo, interval);
}
+
+void servo_reset(struct servo *servo)
+{
+ servo->reset(servo);
+}
diff --git a/servo.h b/servo.h
index 052740e..7a5d0d3 100644
--- a/servo.h
+++ b/servo.h
@@ -93,4 +93,10 @@ double servo_sample(struct servo *servo,
*/
void servo_sync_interval(struct servo *servo, double interval);

+/**
+ * Reset a clock servo.
+ * @param servo Pointer to a servo obtained via @ref servo_create().
+ */
+void servo_reset(struct servo *servo);
+
#endif
diff --git a/servo_private.h b/servo_private.h
index 98c7db2..82e2bf5 100644
--- a/servo_private.h
+++ b/servo_private.h
@@ -30,6 +30,8 @@ struct servo {
enum servo_state *state);

void (*sync_interval)(struct servo *servo, double interval);
+
+ void (*reset)(struct servo *servo);
};

#endif
--
1.8.3.1
Miroslav Lichvar
2013-10-22 16:57:15 UTC
Permalink
Check the sanity of the synchronized clock by comparing its uncorrected
frequency with the system monotonic clock. When the measured frequency
offset is larger than the value of the sanity_freq_limit option (20% by
default), a warning message will be printed and the servo will be reset.
Setting the option to zero disables the check.

This is useful to detect when the clock is broken or adjusted by another
program.

Signed-off-by: Miroslav Lichvar <***@redhat.com>
---
clock.c | 25 ++++++++++++
clock.h | 7 ++++
clockcheck.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
clockcheck.h | 64 ++++++++++++++++++++++++++++++
config.c | 6 +++
config.h | 2 +
default.cfg | 1 +
ds.h | 1 +
gPTP.cfg | 1 +
makefile | 12 +++---
phc2sys.8 | 9 +++++
phc2sys.c | 24 ++++++++++-
port.c | 3 ++
ptp4l.8 | 7 ++++
ptp4l.c | 1 +
15 files changed, 284 insertions(+), 7 deletions(-)
create mode 100644 clockcheck.c
create mode 100644 clockcheck.h

diff --git a/clock.c b/clock.c
index 819421e..16de7b7 100644
--- a/clock.c
+++ b/clock.c
@@ -25,6 +25,7 @@
#include "bmc.h"
#include "clock.h"
#include "clockadj.h"
+#include "clockcheck.h"
#include "foreign.h"
#include "mave.h"
#include "missing.h"
@@ -96,6 +97,7 @@ struct clock {
struct clock_description desc;
struct clock_stats stats;
int stats_interval;
+ struct clockcheck *sanity_check;
};

struct clock the_clock;
@@ -643,6 +645,13 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
pr_err("failed to create stats");
return NULL;
}
+ if (dds->sanity_freq_limit) {
+ c->sanity_check = clockcheck_create(dds->sanity_freq_limit);
+ if (!c->sanity_check) {
+ pr_err("Failed to create clock sanity check");
+ return NULL;
+ }
+ }

c->dds = dds->dds;

@@ -1084,11 +1093,18 @@ enum servo_state clock_synchronize(struct clock *c,
clockadj_step(c->clkid, -tmv_to_nanoseconds(c->master_offset));
c->t1 = tmv_zero();
c->t2 = tmv_zero();
+ if (c->sanity_check) {
+ clockcheck_set_freq(c->sanity_check, -adj);
+ clockcheck_step(c->sanity_check,
+ -tmv_to_nanoseconds(c->master_offset));
+ }
break;
case SERVO_LOCKED:
clockadj_set_freq(c->clkid, -adj);
if (c->clkid == CLOCK_REALTIME)
sysclk_set_sync();
+ if (c->sanity_check)
+ clockcheck_set_freq(c->sanity_check, -adj);
break;
}
return state;
@@ -1204,3 +1220,12 @@ int clock_num_ports(struct clock *c)
{
return c->nports;
}
+
+void clock_check_ts(struct clock *c, struct timespec ts)
+{
+ if (c->sanity_check &&
+ clockcheck_sample(c->sanity_check,
+ ts.tv_sec * NS_PER_SEC + ts.tv_nsec)) {
+ servo_reset(c->servo);
+ }
+}
diff --git a/clock.h b/clock.h
index 309f731..bc66dce 100644
--- a/clock.h
+++ b/clock.h
@@ -238,4 +238,11 @@ struct clock_description *clock_description(struct clock *c);
*/
int clock_num_ports(struct clock *c);

+/**
+ * Perform a sanity check on a time stamp made by a clock.
+ * @param c The clock instance.
+ * @param ts The time stamp.
+ */
+void clock_check_ts(struct clock *c, struct timespec ts);
+
#endif
diff --git a/clockcheck.c b/clockcheck.c
new file mode 100644
index 0000000..d48a578
--- /dev/null
+++ b/clockcheck.c
@@ -0,0 +1,128 @@
+/**
+ * @file clockcheck.c
+ * @note Copyright (C) 2013 Miroslav Lichvar <***@redhat.com>
+ *
+ * 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 <stdlib.h>
+#include <time.h>
+
+#include "clockcheck.h"
+#include "print.h"
+
+#define CHECK_MIN_INTERVAL 100000000
+#define CHECK_MAX_FREQ 900000000
+
+struct clockcheck {
+ /* Sanity frequency limit */
+ int freq_limit;
+ /* Frequency was set at least once */
+ int freq_known;
+ /* Current frequency */
+ int current_freq;
+ /* Maximum and minimum frequency since last update */
+ int max_freq;
+ int min_freq;
+ uint64_t last_ts;
+ uint64_t last_mono_ts;
+};
+
+struct clockcheck *clockcheck_create(int freq_limit)
+{
+ struct clockcheck *cc;
+ cc = calloc(1, sizeof(*cc));
+ if (!cc)
+ return NULL;
+ cc->freq_limit = freq_limit;
+ cc->max_freq = -CHECK_MAX_FREQ;
+ cc->min_freq = CHECK_MAX_FREQ;
+ return cc;
+}
+
+int clockcheck_sample(struct clockcheck *cc, uint64_t ts)
+{
+ uint64_t mono_ts;
+ int64_t interval, mono_interval;
+ double max_foffset, min_foffset;
+ struct timespec now;
+ int ret = 0;
+
+ /* Check the sanity of the synchronized clock by comparing its
+ uncorrected frequency with the system monotonic clock. If
+ the synchronized clock is the system clock, the measured
+ frequency offset will be the current frequency correction of
+ the system clock. */
+
+ if (!cc->freq_known)
+ return ret;
+
+ interval = (int64_t)ts - cc->last_ts;
+ if (interval >= 0 && interval < CHECK_MIN_INTERVAL)
+ return ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ mono_ts = now.tv_sec * 1000000000LL + now.tv_nsec;
+ mono_interval = (int64_t)mono_ts - cc->last_mono_ts;
+
+ if (mono_interval < CHECK_MIN_INTERVAL)
+ return ret;
+
+ if (cc->last_ts && cc->max_freq <= CHECK_MAX_FREQ) {
+ max_foffset = 1e9 * (interval /
+ (1.0 + cc->min_freq / 1e9) /
+ mono_interval - 1.0);
+ min_foffset = 1e9 * (interval /
+ (1.0 + cc->max_freq / 1e9) /
+ mono_interval - 1.0);
+
+ if (min_foffset > cc->freq_limit) {
+ pr_warning("clockcheck: clock jumped forward or"
+ " running faster than expected!");
+ ret = 1;
+ } else if (max_foffset < -cc->freq_limit) {
+ pr_warning("clockcheck: clock jumped backward or"
+ " running slower than expected!");
+ ret = 1;
+ }
+ }
+
+ cc->last_mono_ts = mono_ts;
+ cc->last_ts = ts;
+ cc->max_freq = cc->min_freq = cc->current_freq;
+
+ return ret;
+}
+
+void clockcheck_set_freq(struct clockcheck *cc, int freq)
+{
+ if (cc->max_freq < freq)
+ cc->max_freq = freq;
+ if (cc->min_freq > freq)
+ cc->min_freq = freq;
+ cc->current_freq = freq;
+ cc->freq_known = 1;
+}
+
+void clockcheck_step(struct clockcheck *cc, int64_t step)
+{
+ if (cc->last_ts)
+ cc->last_ts += step;
+}
+
+void clockcheck_destroy(struct clockcheck *cc)
+{
+ free(cc);
+}
diff --git a/clockcheck.h b/clockcheck.h
new file mode 100644
index 0000000..78aca48
--- /dev/null
+++ b/clockcheck.h
@@ -0,0 +1,64 @@
+/**
+ * @file clockcheck.h
+ * @brief Implements clock sanity checking.
+ * @note Copyright (C) 2013 Miroslav Lichvar <***@redhat.com>
+ *
+ * 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_CLOCKCHECK_H
+#define HAVE_CLOCKCHECK_H
+
+#include <stdint.h>
+
+/** Opaque type */
+struct clockcheck;
+
+/**
+ * Create a new instance of a clock sanity check.
+ * @param freq_limit The maximum allowed frequency offset between uncorrected
+ * clock and the system monotonic clock in ppb.
+ * @return A pointer to a new clock check on success, NULL otherwise.
+ */
+struct clockcheck *clockcheck_create(int freq_limit);
+
+/**
+ * Perform the sanity check on a time stamp.
+ * @param cc Pointer to a clock check obtained via @ref clockcheck_create().
+ * @param ts Time stamp made by the clock in nanoseconds.
+ * @return Zero if ts passed the check, non-zero otherwise.
+ */
+int clockcheck_sample(struct clockcheck *cc, uint64_t ts);
+
+/**
+ * Inform clock check about changes in current frequency of the clock.
+ * @param cc Pointer to a clock check obtained via @ref clockcheck_create().
+ * @param freq Frequency correction applied to the clock in ppb.
+ */
+void clockcheck_set_freq(struct clockcheck *cc, int freq);
+
+/**
+ * Inform clock check that the clock was stepped.
+ * @param cc Pointer to a clock check obtained via @ref clockcheck_create().
+ * @param step Step correction applied to the clock in nanoseconds.
+ */
+void clockcheck_step(struct clockcheck *cc, int64_t step);
+
+/**
+ * Destroy a clock check.
+ * @param cc Pointer to a clock check obtained via @ref clockcheck_create().
+ */
+void clockcheck_destroy(struct clockcheck *cc);
+
+#endif
diff --git a/config.c b/config.c
index 8cae57f..34af544 100644
--- a/config.c
+++ b/config.c
@@ -373,6 +373,12 @@ static enum parser_result parse_global_setting(const char *option,
return r;
*cfg->pi_max_frequency = val;

+ } else if (!strcmp(option, "sanity_freq_limit")) {
+ r = get_ranged_int(value, &val, 0, INT_MAX);
+ if (r != PARSED_OK)
+ return r;
+ cfg->dds.sanity_freq_limit = val;
+
} else if (!strcmp(option, "ptp_dst_mac")) {
if (MAC_LEN != sscanf(value, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]))
diff --git a/config.h b/config.h
index 94704a4..9e0d6e6 100644
--- a/config.h
+++ b/config.h
@@ -86,6 +86,8 @@ struct config {
double *pi_f_offset_const;
int *pi_max_frequency;

+ int *sanity_freq_limit;
+
unsigned char *ptp_dst_mac;
unsigned char *p2p_dst_mac;
unsigned char *udp6_scope;
diff --git a/default.cfg b/default.cfg
index 72665a6..7ff66fb 100644
--- a/default.cfg
+++ b/default.cfg
@@ -52,6 +52,7 @@ pi_offset_const 0.0
pi_f_offset_const 0.0000001
pi_max_frequency 900000000
clock_servo pi
+sanity_freq_limit 200000000
#
# Transport options
#
diff --git a/ds.h b/ds.h
index 55ad043..e8e94ad 100644
--- a/ds.h
+++ b/ds.h
@@ -54,6 +54,7 @@ struct default_ds {
int freq_est_interval; /*log seconds*/
int stats_interval; /*log seconds*/
int kernel_leap;
+ int sanity_freq_limit;
int time_source;
struct clock_description clock_desc;
};
diff --git a/gPTP.cfg b/gPTP.cfg
index 30719b6..a3ddd9d 100644
--- a/gPTP.cfg
+++ b/gPTP.cfg
@@ -51,6 +51,7 @@ pi_offset_const 0.0
pi_f_offset_const 0.0000001
pi_max_frequency 900000000
clock_servo pi
+sanity_freq_limit 200000000
#
# Transport options
#
diff --git a/makefile b/makefile
index d79a1df..56cbfc4 100644
--- a/makefile
+++ b/makefile
@@ -32,9 +32,9 @@ VER = -DVER=$(version)
CFLAGS = -Wall $(VER) $(INC) $(DEBUG) $(FEAT_CFLAGS) $(EXTRA_CFLAGS)
LDLIBS = -lm -lrt $(EXTRA_LDFLAGS)
PRG = ptp4l pmc phc2sys hwstamp_ctl
-OBJ = bmc.o clock.o clockadj.o config.o fault.o fsm.o ptp4l.o mave.o \
- msg.o phc.o pi.o port.o print.o raw.o servo.o sk.o stats.o tlv.o tmtab.o \
- transport.o udp.o udp6.o uds.o util.o version.o
+OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \
+ fsm.o ptp4l.o mave.o msg.o phc.o pi.o port.o print.o raw.o servo.o \
+ sk.o stats.o tlv.o tmtab.o transport.o udp.o udp6.o uds.o util.o version.o

OBJECTS = $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o
SRC = $(OBJECTS:.o=.c)
@@ -55,9 +55,9 @@ ptp4l: $(OBJ)
pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
udp6.o uds.o util.o version.o

-phc2sys: clockadj.o msg.o phc.o phc2sys.o pi.o pmc_common.o print.o servo.o \
- raw.o sk.o stats.o sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.o \
- version.o
+phc2sys: clockadj.o clockcheck.o msg.o phc.o phc2sys.o pi.o pmc_common.o \
+ print.o raw.o servo.o sk.o stats.o sysoff.o tlv.o transport.o udp.o udp6.o \
+ uds.o util.o version.o

hwstamp_ctl: hwstamp_ctl.o version.o

diff --git a/phc2sys.8 b/phc2sys.8
index d7a4569..5fcd6f0 100644
--- a/phc2sys.8
+++ b/phc2sys.8
@@ -27,6 +27,8 @@ phc2sys \- synchronize two clocks
] [
.BI \-N " clock-readings"
] [
+.BI \-L " freq-limit"
+] [
.BI \-u " summary-updates"
] [
.BI \-n " domain-number"
@@ -118,6 +120,13 @@ Specify the offset between the slave and master times in seconds. See
.B TIME SCALE USAGE
below.
.TP
+.BI \-L " freq-limit"
+The maximum allowed frequency offset between uncorrected clock and the system
+monotonic clock in parts per billion (ppb). This is used as a sanity check of
+the synchronized clock. When a larger offset is measured, a warning message
+will be printed and the servo will be reset. When set to 0, the sanity check is
+disabled. The default is 200000000 (20%).
+.TP
.BI \-u " summary-updates"
Specify the number of clock updates included in summary statistics. The
statistics include offset root mean square (RMS), maximum absolute offset,
diff --git a/phc2sys.c b/phc2sys.c
index 2012fbd..2b8af49 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -36,6 +36,7 @@
#include <linux/ptp_clock.h>

#include "clockadj.h"
+#include "clockcheck.h"
#include "ds.h"
#include "fsm.h"
#include "missing.h"
@@ -143,6 +144,7 @@ struct clock {
int pmc_ds_idx;
int pmc_ds_requested;
uint64_t pmc_last_update;
+ struct clockcheck *sanity_check;
};

static void update_clock_stats(struct clock *clock,
@@ -193,6 +195,9 @@ static void update_clock(struct clock *clock,
offset += clock->sync_offset * NS_PER_SEC *
clock->sync_offset_direction;

+ if (clock->sanity_check && clockcheck_sample(clock->sanity_check, ts))
+ servo_reset(clock->servo);
+
ppb = servo_sample(clock->servo, offset, ts, &state);
clock->servo_state = state;

@@ -201,11 +206,15 @@ static void update_clock(struct clock *clock,
break;
case SERVO_JUMP:
clockadj_step(clock->clkid, -offset);
+ if (clock->sanity_check)
+ clockcheck_step(clock->sanity_check, -offset);
/* Fall through. */
case SERVO_LOCKED:
clockadj_set_freq(clock->clkid, -ppb);
if (clock->clkid == CLOCK_REALTIME)
sysclk_set_sync();
+ if (clock->sanity_check)
+ clockcheck_set_freq(clock->sanity_check, -ppb);
break;
}

@@ -559,6 +568,7 @@ static void usage(char *progname)
" -R [rate] slave clock update rate in HZ (1.0)\n"
" -N [num] number of master clock readings per update (5)\n"
" -O [offset] slave-master time offset (0)\n"
+ " -L [limit] sanity frequency limit in ppb (200000000)\n"
" -u [num] number of clock updates in summary stats (0)\n"
" -w wait for ptp4l\n"
" -n [num] domain number (0)\n"
@@ -579,6 +589,7 @@ int main(int argc, char *argv[])
int c, domain_number = 0, phc_readings = 5, pps_fd = -1;
int max_ppb, r, wait_sync = 0, forced_sync_offset = 0;
int print_level = LOG_INFO, use_syslog = 1, verbose = 0;
+ int sanity_freq_limit = 200000000;
double ppb, phc_interval = 1.0, phc_rate;
struct timespec phc_interval_tp;
struct clock dst_clock = {
@@ -594,7 +605,7 @@ int main(int argc, char *argv[])
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
while (EOF != (c = getopt(argc, argv,
- "c:d:s:P:I:S:F:R:N:O:i:u:wn:xl:mqvh"))) {
+ "c:d:s:P:I:S:F:R:N:O:L:i:u:wn:xl:mqvh"))) {
switch (c) {
case 'c':
dst_clock.clkid = clock_open(optarg);
@@ -649,6 +660,10 @@ int main(int argc, char *argv[])
dst_clock.sync_offset_direction = -1;
forced_sync_offset = 1;
break;
+ case 'L':
+ if (get_arg_val_i(c, optarg, &sanity_freq_limit, 0, INT_MAX))
+ return -1;
+ break;
case 'u':
if (get_arg_val_ui(c, optarg, &dst_clock.stats_max_count,
0, UINT_MAX))
@@ -721,6 +736,13 @@ int main(int argc, char *argv[])
return -1;
}
}
+ if (sanity_freq_limit) {
+ dst_clock.sanity_check = clockcheck_create(sanity_freq_limit);
+ if (!dst_clock.sanity_check) {
+ fprintf(stderr, "failed to create clock check");
+ return -1;
+ }
+ }

print_set_progname(progname);
print_set_verbose(verbose);
diff --git a/port.c b/port.c
index d98bb80..bd10d0a 100644
--- a/port.c
+++ b/port.c
@@ -2082,6 +2082,9 @@ enum fsm_event port_event(struct port *p, int fd_index)
msg_put(msg);
return EV_NONE;
}
+ if (msg->hwts.ts.tv_sec && msg->hwts.ts.tv_nsec) {
+ clock_check_ts(p->clock, msg->hwts.ts);
+ }
if (port_ignore(p, msg)) {
msg_put(msg);
return EV_NONE;
diff --git a/ptp4l.8 b/ptp4l.8
index 493626d..a008325 100644
--- a/ptp4l.8
+++ b/ptp4l.8
@@ -340,6 +340,13 @@ The maximum allowed frequency adjustment of the clock in parts per billion
set to 0, the hardware limit will be used.
The default is 900000000 (90%).
.TP
+.B sanity_freq_limit
+The maximum allowed frequency offset between uncorrected clock and the system
+monotonic clock in parts per billion (ppb). This is used as a sanity check of
+the synchronized clock. When a larger offset is measured, a warning message
+will be printed and the servo will be reset. When set to 0, the sanity check is
+disabled. The default is 200000000 (20%).
+.TP
.B ptp_dst_mac
The MAC address where should be PTP messages sent.
Relevant only with L2 transport. The default is 01:1B:19:00:00:00.
diff --git a/ptp4l.c b/ptp4l.c
index b0d1c9c..f1e98aa 100644
--- a/ptp4l.c
+++ b/ptp4l.c
@@ -54,6 +54,7 @@ static struct config cfg_settings = {
.freq_est_interval = 1,
.stats_interval = 0,
.kernel_leap = 1,
+ .sanity_freq_limit = 200000000,
.time_source = INTERNAL_OSCILLATOR,
.clock_desc = {
.productDescription = {
--
1.8.3.1
Richard Cochran
2013-10-27 12:48:24 UTC
Permalink
Post by Miroslav Lichvar
This is useful to detect when the clock is broken or adjusted by another
program.
Add reset function to servo.
Add clock sanity check.
Series applied (rebased onto current master).

Thanks,
Richard

Loading...