|
Packit |
9c3e7e |
/**
|
|
Packit |
9c3e7e |
* @file clockcheck.c
|
|
Packit |
9c3e7e |
* @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@redhat.com>
|
|
Packit |
9c3e7e |
*
|
|
Packit |
9c3e7e |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
9c3e7e |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
9c3e7e |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
9c3e7e |
* (at your option) any later version.
|
|
Packit |
9c3e7e |
*
|
|
Packit |
9c3e7e |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
9c3e7e |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
9c3e7e |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
9c3e7e |
* GNU General Public License for more details.
|
|
Packit |
9c3e7e |
*
|
|
Packit |
9c3e7e |
* You should have received a copy of the GNU General Public License along
|
|
Packit |
9c3e7e |
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
9c3e7e |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
9c3e7e |
*/
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
#include <stdlib.h>
|
|
Packit |
9c3e7e |
#include <time.h>
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
#include "clockcheck.h"
|
|
Packit |
9c3e7e |
#include "print.h"
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
#define CHECK_MIN_INTERVAL 100000000
|
|
Packit |
9c3e7e |
#define CHECK_MAX_FREQ 900000000
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
struct clockcheck {
|
|
Packit |
9c3e7e |
/* Sanity frequency limit */
|
|
Packit |
9c3e7e |
int freq_limit;
|
|
Packit |
9c3e7e |
/* Frequency was set at least once */
|
|
Packit |
9c3e7e |
int freq_known;
|
|
Packit |
9c3e7e |
/* Current frequency */
|
|
Packit |
9c3e7e |
int current_freq;
|
|
Packit |
9c3e7e |
/* Maximum and minimum frequency since last update */
|
|
Packit |
9c3e7e |
int max_freq;
|
|
Packit |
9c3e7e |
int min_freq;
|
|
Packit |
9c3e7e |
uint64_t last_ts;
|
|
Packit |
9c3e7e |
uint64_t last_mono_ts;
|
|
Packit |
9c3e7e |
};
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
struct clockcheck *clockcheck_create(int freq_limit)
|
|
Packit |
9c3e7e |
{
|
|
Packit |
9c3e7e |
struct clockcheck *cc;
|
|
Packit |
9c3e7e |
cc = calloc(1, sizeof(*cc));
|
|
Packit |
9c3e7e |
if (!cc)
|
|
Packit |
9c3e7e |
return NULL;
|
|
Packit |
9c3e7e |
cc->freq_limit = freq_limit;
|
|
Packit |
9c3e7e |
cc->max_freq = -CHECK_MAX_FREQ;
|
|
Packit |
9c3e7e |
cc->min_freq = CHECK_MAX_FREQ;
|
|
Packit |
9c3e7e |
return cc;
|
|
Packit |
9c3e7e |
}
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
int clockcheck_sample(struct clockcheck *cc, uint64_t ts)
|
|
Packit |
9c3e7e |
{
|
|
Packit |
9c3e7e |
uint64_t mono_ts;
|
|
Packit |
9c3e7e |
int64_t interval, mono_interval;
|
|
Packit |
9c3e7e |
double max_foffset, min_foffset;
|
|
Packit |
9c3e7e |
struct timespec now;
|
|
Packit |
9c3e7e |
int ret = 0;
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
/* Check the sanity of the synchronized clock by comparing its
|
|
Packit |
9c3e7e |
uncorrected frequency with the system monotonic clock. If
|
|
Packit |
9c3e7e |
the synchronized clock is the system clock, the measured
|
|
Packit |
9c3e7e |
frequency offset will be the current frequency correction of
|
|
Packit |
9c3e7e |
the system clock. */
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
if (!cc->freq_known)
|
|
Packit |
9c3e7e |
return ret;
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
interval = (int64_t)ts - cc->last_ts;
|
|
Packit |
9c3e7e |
if (interval >= 0 && interval < CHECK_MIN_INTERVAL)
|
|
Packit |
9c3e7e |
return ret;
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
clock_gettime(CLOCK_MONOTONIC, &now;;
|
|
Packit |
9c3e7e |
mono_ts = now.tv_sec * 1000000000LL + now.tv_nsec;
|
|
Packit |
9c3e7e |
mono_interval = (int64_t)mono_ts - cc->last_mono_ts;
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
if (mono_interval < CHECK_MIN_INTERVAL)
|
|
Packit |
9c3e7e |
return ret;
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
if (cc->last_ts && cc->max_freq <= CHECK_MAX_FREQ) {
|
|
Packit |
9c3e7e |
max_foffset = 1e9 * (interval /
|
|
Packit |
9c3e7e |
(1.0 + cc->min_freq / 1e9) /
|
|
Packit |
9c3e7e |
mono_interval - 1.0);
|
|
Packit |
9c3e7e |
min_foffset = 1e9 * (interval /
|
|
Packit |
9c3e7e |
(1.0 + cc->max_freq / 1e9) /
|
|
Packit |
9c3e7e |
mono_interval - 1.0);
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
if (min_foffset > cc->freq_limit) {
|
|
Packit |
9c3e7e |
pr_warning("clockcheck: clock jumped forward or"
|
|
Packit |
9c3e7e |
" running faster than expected!");
|
|
Packit |
9c3e7e |
ret = 1;
|
|
Packit |
9c3e7e |
} else if (max_foffset < -cc->freq_limit) {
|
|
Packit |
9c3e7e |
pr_warning("clockcheck: clock jumped backward or"
|
|
Packit |
9c3e7e |
" running slower than expected!");
|
|
Packit |
9c3e7e |
ret = 1;
|
|
Packit |
9c3e7e |
}
|
|
Packit |
9c3e7e |
}
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
cc->last_mono_ts = mono_ts;
|
|
Packit |
9c3e7e |
cc->last_ts = ts;
|
|
Packit |
9c3e7e |
cc->max_freq = cc->min_freq = cc->current_freq;
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
return ret;
|
|
Packit |
9c3e7e |
}
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
void clockcheck_set_freq(struct clockcheck *cc, int freq)
|
|
Packit |
9c3e7e |
{
|
|
Packit |
9c3e7e |
if (cc->max_freq < freq)
|
|
Packit |
9c3e7e |
cc->max_freq = freq;
|
|
Packit |
9c3e7e |
if (cc->min_freq > freq)
|
|
Packit |
9c3e7e |
cc->min_freq = freq;
|
|
Packit |
9c3e7e |
cc->current_freq = freq;
|
|
Packit |
9c3e7e |
cc->freq_known = 1;
|
|
Packit |
9c3e7e |
}
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
void clockcheck_step(struct clockcheck *cc, int64_t step)
|
|
Packit |
9c3e7e |
{
|
|
Packit |
9c3e7e |
if (cc->last_ts)
|
|
Packit |
9c3e7e |
cc->last_ts += step;
|
|
Packit |
9c3e7e |
}
|
|
Packit |
9c3e7e |
|
|
Packit |
9c3e7e |
void clockcheck_destroy(struct clockcheck *cc)
|
|
Packit |
9c3e7e |
{
|
|
Packit |
9c3e7e |
free(cc);
|
|
Packit |
9c3e7e |
}
|