xref: /linux/tools/testing/selftests/timers/adjtick.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
16035519fSJohn Stultz /* adjtimex() tick adjustment test
26035519fSJohn Stultz  *		by:   John Stultz <john.stultz@linaro.org>
36035519fSJohn Stultz  *		(C) Copyright Linaro Limited 2015
46035519fSJohn Stultz  *		Licensed under the GPLv2
56035519fSJohn Stultz  *
66035519fSJohn Stultz  *  To build:
76035519fSJohn Stultz  *	$ gcc adjtick.c -o adjtick -lrt
86035519fSJohn Stultz  *
96035519fSJohn Stultz  *   This program is free software: you can redistribute it and/or modify
106035519fSJohn Stultz  *   it under the terms of the GNU General Public License as published by
116035519fSJohn Stultz  *   the Free Software Foundation, either version 2 of the License, or
126035519fSJohn Stultz  *   (at your option) any later version.
136035519fSJohn Stultz  *
146035519fSJohn Stultz  *   This program is distributed in the hope that it will be useful,
156035519fSJohn Stultz  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
166035519fSJohn Stultz  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
176035519fSJohn Stultz  *   GNU General Public License for more details.
186035519fSJohn Stultz  */
196035519fSJohn Stultz #include <stdio.h>
206035519fSJohn Stultz #include <unistd.h>
216035519fSJohn Stultz #include <stdlib.h>
226035519fSJohn Stultz #include <sys/time.h>
236035519fSJohn Stultz #include <sys/timex.h>
246035519fSJohn Stultz #include <time.h>
256035519fSJohn Stultz 
266035519fSJohn Stultz #include "../kselftest.h"
276035519fSJohn Stultz 
286035519fSJohn Stultz #define CLOCK_MONOTONIC_RAW	4
296035519fSJohn Stultz 
306035519fSJohn Stultz #define NSEC_PER_SEC		1000000000LL
316035519fSJohn Stultz #define USEC_PER_SEC		1000000
326035519fSJohn Stultz 
336035519fSJohn Stultz #define MILLION			1000000
346035519fSJohn Stultz 
356035519fSJohn Stultz long systick;
366035519fSJohn Stultz 
llabs(long long val)376035519fSJohn Stultz long long llabs(long long val)
386035519fSJohn Stultz {
396035519fSJohn Stultz 	if (val < 0)
406035519fSJohn Stultz 		val = -val;
416035519fSJohn Stultz 	return val;
426035519fSJohn Stultz }
436035519fSJohn Stultz 
ts_to_nsec(struct timespec ts)446035519fSJohn Stultz unsigned long long ts_to_nsec(struct timespec ts)
456035519fSJohn Stultz {
466035519fSJohn Stultz 	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
476035519fSJohn Stultz }
486035519fSJohn Stultz 
nsec_to_ts(long long ns)496035519fSJohn Stultz struct timespec nsec_to_ts(long long ns)
506035519fSJohn Stultz {
516035519fSJohn Stultz 	struct timespec ts;
526035519fSJohn Stultz 
536035519fSJohn Stultz 	ts.tv_sec = ns/NSEC_PER_SEC;
546035519fSJohn Stultz 	ts.tv_nsec = ns%NSEC_PER_SEC;
556035519fSJohn Stultz 
566035519fSJohn Stultz 	return ts;
576035519fSJohn Stultz }
586035519fSJohn Stultz 
diff_timespec(struct timespec start,struct timespec end)596035519fSJohn Stultz long long diff_timespec(struct timespec start, struct timespec end)
606035519fSJohn Stultz {
616035519fSJohn Stultz 	long long start_ns, end_ns;
626035519fSJohn Stultz 
636035519fSJohn Stultz 	start_ns = ts_to_nsec(start);
646035519fSJohn Stultz 	end_ns = ts_to_nsec(end);
656035519fSJohn Stultz 
666035519fSJohn Stultz 	return end_ns - start_ns;
676035519fSJohn Stultz }
686035519fSJohn Stultz 
get_monotonic_and_raw(struct timespec * mon,struct timespec * raw)696035519fSJohn Stultz void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
706035519fSJohn Stultz {
716035519fSJohn Stultz 	struct timespec start, mid, end;
726035519fSJohn Stultz 	long long diff = 0, tmp;
736035519fSJohn Stultz 	int i;
746035519fSJohn Stultz 
756035519fSJohn Stultz 	clock_gettime(CLOCK_MONOTONIC, mon);
766035519fSJohn Stultz 	clock_gettime(CLOCK_MONOTONIC_RAW, raw);
776035519fSJohn Stultz 
786035519fSJohn Stultz 	/* Try to get a more tightly bound pairing */
796035519fSJohn Stultz 	for (i = 0; i < 3; i++) {
806035519fSJohn Stultz 		long long newdiff;
816035519fSJohn Stultz 
826035519fSJohn Stultz 		clock_gettime(CLOCK_MONOTONIC, &start);
836035519fSJohn Stultz 		clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
846035519fSJohn Stultz 		clock_gettime(CLOCK_MONOTONIC, &end);
856035519fSJohn Stultz 
866035519fSJohn Stultz 		newdiff = diff_timespec(start, end);
876035519fSJohn Stultz 		if (diff == 0 || newdiff < diff) {
886035519fSJohn Stultz 			diff = newdiff;
896035519fSJohn Stultz 			*raw = mid;
906035519fSJohn Stultz 			tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
916035519fSJohn Stultz 			*mon = nsec_to_ts(tmp);
926035519fSJohn Stultz 		}
936035519fSJohn Stultz 	}
946035519fSJohn Stultz }
956035519fSJohn Stultz 
get_ppm_drift(void)966035519fSJohn Stultz long long get_ppm_drift(void)
976035519fSJohn Stultz {
986035519fSJohn Stultz 	struct timespec mon_start, raw_start, mon_end, raw_end;
996035519fSJohn Stultz 	long long delta1, delta2, eppm;
1006035519fSJohn Stultz 
1016035519fSJohn Stultz 	get_monotonic_and_raw(&mon_start, &raw_start);
1026035519fSJohn Stultz 
1036035519fSJohn Stultz 	sleep(15);
1046035519fSJohn Stultz 
1056035519fSJohn Stultz 	get_monotonic_and_raw(&mon_end, &raw_end);
1066035519fSJohn Stultz 
1076035519fSJohn Stultz 	delta1 = diff_timespec(mon_start, mon_end);
1086035519fSJohn Stultz 	delta2 = diff_timespec(raw_start, raw_end);
1096035519fSJohn Stultz 
1106035519fSJohn Stultz 	eppm = (delta1*MILLION)/delta2 - MILLION;
1116035519fSJohn Stultz 
1126035519fSJohn Stultz 	return eppm;
1136035519fSJohn Stultz }
1146035519fSJohn Stultz 
check_tick_adj(long tickval)1156035519fSJohn Stultz int check_tick_adj(long tickval)
1166035519fSJohn Stultz {
1176035519fSJohn Stultz 	long long eppm, ppm;
1186035519fSJohn Stultz 	struct timex tx1;
1196035519fSJohn Stultz 
1206035519fSJohn Stultz 	tx1.modes	 = ADJ_TICK;
1216035519fSJohn Stultz 	tx1.modes	|= ADJ_OFFSET;
1226035519fSJohn Stultz 	tx1.modes	|= ADJ_FREQUENCY;
1236035519fSJohn Stultz 	tx1.modes	|= ADJ_STATUS;
1246035519fSJohn Stultz 
1256035519fSJohn Stultz 	tx1.status	= STA_PLL;
1266035519fSJohn Stultz 	tx1.offset	= 0;
1276035519fSJohn Stultz 	tx1.freq	= 0;
1286035519fSJohn Stultz 	tx1.tick	= tickval;
1296035519fSJohn Stultz 
1306035519fSJohn Stultz 	adjtimex(&tx1);
1316035519fSJohn Stultz 
1326035519fSJohn Stultz 	sleep(1);
1336035519fSJohn Stultz 
1346035519fSJohn Stultz 	ppm = ((long long)tickval * MILLION)/systick - MILLION;
1356035519fSJohn Stultz 	printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
1366035519fSJohn Stultz 
1376035519fSJohn Stultz 	eppm = get_ppm_drift();
1386035519fSJohn Stultz 	printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
139fe483192SKees Cook 	fflush(stdout);
1406035519fSJohn Stultz 
1416035519fSJohn Stultz 	tx1.modes = 0;
1426035519fSJohn Stultz 	adjtimex(&tx1);
1436035519fSJohn Stultz 
1446035519fSJohn Stultz 	if (tx1.offset || tx1.freq || tx1.tick != tickval) {
1456035519fSJohn Stultz 		printf("	[ERROR]\n");
1466035519fSJohn Stultz 		printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
1476035519fSJohn Stultz 		return -1;
1486035519fSJohn Stultz 	}
1496035519fSJohn Stultz 
1506035519fSJohn Stultz 	/*
1516035519fSJohn Stultz 	 * Here we use 100ppm difference as an error bound.
1526035519fSJohn Stultz 	 * We likely should see better, but some coarse clocksources
1536035519fSJohn Stultz 	 * cannot match the HZ tick size accurately, so we have a
1546035519fSJohn Stultz 	 * internal correction factor that doesn't scale exactly
1556035519fSJohn Stultz 	 * with the adjustment, resulting in > 10ppm error during
1566035519fSJohn Stultz 	 * a 10% adjustment. 100ppm also gives us more breathing
1576035519fSJohn Stultz 	 * room for interruptions during the measurement.
1586035519fSJohn Stultz 	 */
1596035519fSJohn Stultz 	if (llabs(eppm - ppm) > 100) {
1606035519fSJohn Stultz 		printf("	[FAILED]\n");
1616035519fSJohn Stultz 		return -1;
1626035519fSJohn Stultz 	}
1636035519fSJohn Stultz 	printf("	[OK]\n");
1646035519fSJohn Stultz 
1656035519fSJohn Stultz 	return  0;
1666035519fSJohn Stultz }
1676035519fSJohn Stultz 
main(int argc,char ** argv)168a8d74fe7SWolfram Sang int main(int argc, char **argv)
1696035519fSJohn Stultz {
1706035519fSJohn Stultz 	struct timespec raw;
1716035519fSJohn Stultz 	long tick, max, interval, err;
1726035519fSJohn Stultz 	struct timex tx1;
1736035519fSJohn Stultz 
1746035519fSJohn Stultz 	err = 0;
1756035519fSJohn Stultz 	setbuf(stdout, NULL);
1766035519fSJohn Stultz 
1776035519fSJohn Stultz 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
1786035519fSJohn Stultz 		printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
1796035519fSJohn Stultz 		return -1;
1806035519fSJohn Stultz 	}
1816035519fSJohn Stultz 
1826035519fSJohn Stultz 	printf("Each iteration takes about 15 seconds\n");
1836035519fSJohn Stultz 
1846035519fSJohn Stultz 	systick = sysconf(_SC_CLK_TCK);
1856035519fSJohn Stultz 	systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
1866035519fSJohn Stultz 	max = systick/10; /* +/- 10% */
1876035519fSJohn Stultz 	interval = max/4; /* in 4 steps each side */
1886035519fSJohn Stultz 
1896035519fSJohn Stultz 	for (tick = (systick - max); tick < (systick + max); tick += interval) {
1906035519fSJohn Stultz 		if (check_tick_adj(tick)) {
1916035519fSJohn Stultz 			err = 1;
1926035519fSJohn Stultz 			break;
1936035519fSJohn Stultz 		}
1946035519fSJohn Stultz 	}
1956035519fSJohn Stultz 
1966035519fSJohn Stultz 	/* Reset things to zero */
1976035519fSJohn Stultz 	tx1.modes	 = ADJ_TICK;
1986035519fSJohn Stultz 	tx1.modes	|= ADJ_OFFSET;
1996035519fSJohn Stultz 	tx1.modes	|= ADJ_FREQUENCY;
2006035519fSJohn Stultz 
2016035519fSJohn Stultz 	tx1.offset	 = 0;
2026035519fSJohn Stultz 	tx1.freq	 = 0;
2036035519fSJohn Stultz 	tx1.tick	 = systick;
2046035519fSJohn Stultz 
2056035519fSJohn Stultz 	adjtimex(&tx1);
2066035519fSJohn Stultz 
2076035519fSJohn Stultz 	if (err)
208*bc7e5d23SNathan Chancellor 		ksft_exit_fail();
2096035519fSJohn Stultz 
210*bc7e5d23SNathan Chancellor 	ksft_exit_pass();
2116035519fSJohn Stultz }
212