Blame clockcheck.c

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
}