Blame clockcheck.c

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