| /** |
| * @file clockcheck.c |
| * @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@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 1000000000 |
| #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; |
| clockcheck_reset(cc); |
| return cc; |
| } |
| |
| void clockcheck_reset(struct clockcheck *cc) |
| { |
| cc->freq_known = 0; |
| cc->max_freq = -CHECK_MAX_FREQ; |
| cc->min_freq = CHECK_MAX_FREQ; |
| cc->last_ts = 0; |
| } |
| |
| 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; |
| } |
| |
| 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) |
| cc->last_ts += step; |
| } |
| |
| void clockcheck_destroy(struct clockcheck *cc) |
| { |
| free(cc); |
| } |