Before setting the new frequency offset on a clock update, compare the current frequency returned by the kernel with the value saved from the previous update. Print a warning message if the difference is larger than 1 ppb, allowing for rounding errors in conversion to and from double. The kernel caches the value set by clock_adjtime() in shifted ppm, it doesn't request it from the driver, which can have a lower resulution.
This should detect misconfigurations where multiple processes are trying to control the clock (e.g. another ptp4l/phc2sys instance or an NTP client), even when they don't step the clock. Signed-off-by: Miroslav Lichvar <mlich...@redhat.com> --- clock.c | 3 +++ clockcheck.c | 10 ++++++++++ clockcheck.h | 8 ++++++++ phc2sys.c | 3 +++ ptp4l.8 | 6 ++++-- 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/clock.c b/clock.c index 46ac9c2..8177e77 100644 --- a/clock.c +++ b/clock.c @@ -1776,6 +1776,9 @@ static void clock_step_window(struct clock *c) static void clock_synchronize_locked(struct clock *c, double adj) { + if (c->sanity_check) { + clockcheck_freq(c->sanity_check, clockadj_get_freq(c->clkid)); + } clockadj_set_freq(c->clkid, -adj); if (c->clkid == CLOCK_REALTIME) { sysclk_set_sync(); diff --git a/clockcheck.c b/clockcheck.c index f0141be..b5a69cc 100644 --- a/clockcheck.c +++ b/clockcheck.c @@ -123,6 +123,16 @@ void clockcheck_set_freq(struct clockcheck *cc, int freq) cc->freq_known = 1; } +int clockcheck_freq(struct clockcheck *cc, int freq) +{ + /* Allow difference of 1 ppb due to conversion to/from double */ + if (cc->freq_known && abs(cc->current_freq - freq) > 1) { + pr_warning("clockcheck: clock frequency changed unexpectedly!"); + return 1; + } + return 0; +} + void clockcheck_step(struct clockcheck *cc, int64_t step) { if (cc->last_ts) diff --git a/clockcheck.h b/clockcheck.h index 1ff86eb..4b09b98 100644 --- a/clockcheck.h +++ b/clockcheck.h @@ -54,6 +54,14 @@ int clockcheck_sample(struct clockcheck *cc, uint64_t ts); */ void clockcheck_set_freq(struct clockcheck *cc, int freq); +/** + * Check whether the frequency correction did not change unexpectedly. + * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). + * @param freq Current reading of the frequency correction in ppb. + * @return Zero if the frequency did not change, non-zero otherwise. + */ +int clockcheck_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(). diff --git a/phc2sys.c b/phc2sys.c index ebc43e5..88ed00c 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -561,6 +561,9 @@ static void update_clock(struct phc2sys_private *priv, struct clock *clock, /* Fall through. */ case SERVO_LOCKED: case SERVO_LOCKED_STABLE: + if (clock->sanity_check) + clockcheck_freq(clock->sanity_check, + clockadj_get_freq(clock->clkid)); clockadj_set_freq(clock->clkid, -ppb); if (clock->clkid == CLOCK_REALTIME) sysclk_set_sync(); diff --git a/ptp4l.8 b/ptp4l.8 index 1268802..cd6299f 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -619,8 +619,10 @@ This option used to be called 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%). +will be printed and the servo will be reset. If the frequency correction set by +ptp4l changes unexpectedly between updates of the clock (e.g. due to another +process trying to control the clock), a warning message will be printed. When +set to 0, the sanity check is disabled. The default is 200000000 (20%). .TP .B initial_delay The initial path delay of the clock in nanoseconds used for synchronization of -- 2.37.3 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel