162306a36Sopenharmony_ci/* CLOCK_MONOTONIC vs CLOCK_MONOTONIC_RAW skew test
262306a36Sopenharmony_ci *		by: john stultz (johnstul@us.ibm.com)
362306a36Sopenharmony_ci *		    John Stultz <john.stultz@linaro.org>
462306a36Sopenharmony_ci *		(C) Copyright IBM 2012
562306a36Sopenharmony_ci *		(C) Copyright Linaro Limited 2015
662306a36Sopenharmony_ci *		Licensed under the GPLv2
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  To build:
962306a36Sopenharmony_ci *	$ gcc raw_skew.c -o raw_skew -lrt
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *   This program is free software: you can redistribute it and/or modify
1262306a36Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
1362306a36Sopenharmony_ci *   the Free Software Foundation, either version 2 of the License, or
1462306a36Sopenharmony_ci *   (at your option) any later version.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
1762306a36Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1862306a36Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1962306a36Sopenharmony_ci *   GNU General Public License for more details.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <stdio.h>
2362306a36Sopenharmony_ci#include <unistd.h>
2462306a36Sopenharmony_ci#include <stdlib.h>
2562306a36Sopenharmony_ci#include <sys/time.h>
2662306a36Sopenharmony_ci#include <sys/timex.h>
2762306a36Sopenharmony_ci#include <time.h>
2862306a36Sopenharmony_ci#include "../kselftest.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define CLOCK_MONOTONIC_RAW		4
3162306a36Sopenharmony_ci#define NSEC_PER_SEC 1000000000LL
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define shift_right(x, s) ({		\
3462306a36Sopenharmony_ci	__typeof__(x) __x = (x);	\
3562306a36Sopenharmony_ci	__typeof__(s) __s = (s);	\
3662306a36Sopenharmony_ci	__x < 0 ? -(-__x >> __s) : __x >> __s; \
3762306a36Sopenharmony_ci})
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cilong long llabs(long long val)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	if (val < 0)
4262306a36Sopenharmony_ci		val = -val;
4362306a36Sopenharmony_ci	return val;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciunsigned long long ts_to_nsec(struct timespec ts)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct timespec nsec_to_ts(long long ns)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct timespec ts;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	ts.tv_sec = ns/NSEC_PER_SEC;
5662306a36Sopenharmony_ci	ts.tv_nsec = ns%NSEC_PER_SEC;
5762306a36Sopenharmony_ci	return ts;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cilong long diff_timespec(struct timespec start, struct timespec end)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	long long start_ns, end_ns;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	start_ns = ts_to_nsec(start);
6562306a36Sopenharmony_ci	end_ns = ts_to_nsec(end);
6662306a36Sopenharmony_ci	return end_ns - start_ns;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct timespec start, mid, end;
7262306a36Sopenharmony_ci	long long diff = 0, tmp;
7362306a36Sopenharmony_ci	int i;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
7662306a36Sopenharmony_ci		long long newdiff;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &start);
7962306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
8062306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &end);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		newdiff = diff_timespec(start, end);
8362306a36Sopenharmony_ci		if (diff == 0 || newdiff < diff) {
8462306a36Sopenharmony_ci			diff = newdiff;
8562306a36Sopenharmony_ci			*raw = mid;
8662306a36Sopenharmony_ci			tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
8762306a36Sopenharmony_ci			*mon = nsec_to_ts(tmp);
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciint main(int argc, char **argv)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct timespec mon, raw, start, end;
9562306a36Sopenharmony_ci	long long delta1, delta2, interval, eppm, ppm;
9662306a36Sopenharmony_ci	struct timex tx1, tx2;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	setbuf(stdout, NULL);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
10162306a36Sopenharmony_ci		printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
10262306a36Sopenharmony_ci		return -1;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	tx1.modes = 0;
10662306a36Sopenharmony_ci	adjtimex(&tx1);
10762306a36Sopenharmony_ci	get_monotonic_and_raw(&mon, &raw);
10862306a36Sopenharmony_ci	start = mon;
10962306a36Sopenharmony_ci	delta1 = diff_timespec(mon, raw);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (tx1.offset)
11262306a36Sopenharmony_ci		printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n");
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	printf("Estimating clock drift: ");
11562306a36Sopenharmony_ci	fflush(stdout);
11662306a36Sopenharmony_ci	sleep(120);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	get_monotonic_and_raw(&mon, &raw);
11962306a36Sopenharmony_ci	end = mon;
12062306a36Sopenharmony_ci	tx2.modes = 0;
12162306a36Sopenharmony_ci	adjtimex(&tx2);
12262306a36Sopenharmony_ci	delta2 = diff_timespec(mon, raw);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	interval = diff_timespec(start, end);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* calculate measured ppm between MONOTONIC and MONOTONIC_RAW */
12762306a36Sopenharmony_ci	eppm = ((delta2-delta1)*NSEC_PER_SEC)/interval;
12862306a36Sopenharmony_ci	eppm = -eppm;
12962306a36Sopenharmony_ci	printf("%lld.%i(est)", eppm/1000, abs((int)(eppm%1000)));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Avg the two actual freq samples adjtimex gave us */
13262306a36Sopenharmony_ci	ppm = (long long)(tx1.freq + tx2.freq) * 1000 / 2;
13362306a36Sopenharmony_ci	ppm = shift_right(ppm, 16);
13462306a36Sopenharmony_ci	printf(" %lld.%i(act)", ppm/1000, abs((int)(ppm%1000)));
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (llabs(eppm - ppm) > 1000) {
13762306a36Sopenharmony_ci		if (tx1.offset || tx2.offset ||
13862306a36Sopenharmony_ci		    tx1.freq != tx2.freq || tx1.tick != tx2.tick) {
13962306a36Sopenharmony_ci			printf("	[SKIP]\n");
14062306a36Sopenharmony_ci			return ksft_exit_skip("The clock was adjusted externally. Shutdown NTPd or other time sync daemons\n");
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci		printf("	[FAILED]\n");
14362306a36Sopenharmony_ci		return ksft_exit_fail();
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	printf("	[OK]\n");
14662306a36Sopenharmony_ci	return  ksft_exit_pass();
14762306a36Sopenharmony_ci}
148