xref: /linux/tools/testing/selftests/timers/raw_skew.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
1 /* CLOCK_MONOTONIC vs CLOCK_MONOTONIC_RAW skew test
2  *		by: john stultz (johnstul@us.ibm.com)
3  *		    John Stultz <john.stultz@linaro.org>
4  *		(C) Copyright IBM 2012
5  *		(C) Copyright Linaro Limited 2015
6  *		Licensed under the GPLv2
7  *
8  *  To build:
9  *	$ gcc raw_skew.c -o raw_skew -lrt
10  *
11  *   This program is free software: you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation, either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  */
21 
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sys/time.h>
26 #include <sys/timex.h>
27 #include <time.h>
28 #include "../kselftest.h"
29 
30 #define CLOCK_MONOTONIC_RAW		4
31 #define NSEC_PER_SEC 1000000000LL
32 
33 #define shift_right(x, s) ({		\
34 	__typeof__(x) __x = (x);	\
35 	__typeof__(s) __s = (s);	\
36 	__x < 0 ? -(-__x >> __s) : __x >> __s; \
37 })
38 
39 long long llabs(long long val)
40 {
41 	if (val < 0)
42 		val = -val;
43 	return val;
44 }
45 
46 unsigned long long ts_to_nsec(struct timespec ts)
47 {
48 	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
49 }
50 
51 struct timespec nsec_to_ts(long long ns)
52 {
53 	struct timespec ts;
54 
55 	ts.tv_sec = ns/NSEC_PER_SEC;
56 	ts.tv_nsec = ns%NSEC_PER_SEC;
57 	return ts;
58 }
59 
60 long long diff_timespec(struct timespec start, struct timespec end)
61 {
62 	long long start_ns, end_ns;
63 
64 	start_ns = ts_to_nsec(start);
65 	end_ns = ts_to_nsec(end);
66 	return end_ns - start_ns;
67 }
68 
69 void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
70 {
71 	struct timespec start, mid, end;
72 	long long diff = 0, tmp;
73 	int i;
74 
75 	for (i = 0; i < 3; i++) {
76 		long long newdiff;
77 
78 		clock_gettime(CLOCK_MONOTONIC, &start);
79 		clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
80 		clock_gettime(CLOCK_MONOTONIC, &end);
81 
82 		newdiff = diff_timespec(start, end);
83 		if (diff == 0 || newdiff < diff) {
84 			diff = newdiff;
85 			*raw = mid;
86 			tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
87 			*mon = nsec_to_ts(tmp);
88 		}
89 	}
90 }
91 
92 int main(int argc, char **argv)
93 {
94 	struct timespec mon, raw, start, end;
95 	long long delta1, delta2, interval, eppm, ppm;
96 	struct timex tx1, tx2;
97 
98 	setbuf(stdout, NULL);
99 
100 	if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
101 		printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
102 		return -1;
103 	}
104 
105 	tx1.modes = 0;
106 	adjtimex(&tx1);
107 	get_monotonic_and_raw(&mon, &raw);
108 	start = mon;
109 	delta1 = diff_timespec(mon, raw);
110 
111 	if (tx1.offset)
112 		printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n");
113 
114 	printf("Estimating clock drift: ");
115 	fflush(stdout);
116 	sleep(120);
117 
118 	get_monotonic_and_raw(&mon, &raw);
119 	end = mon;
120 	tx2.modes = 0;
121 	adjtimex(&tx2);
122 	delta2 = diff_timespec(mon, raw);
123 
124 	interval = diff_timespec(start, end);
125 
126 	/* calculate measured ppm between MONOTONIC and MONOTONIC_RAW */
127 	eppm = ((delta2-delta1)*NSEC_PER_SEC)/interval;
128 	eppm = -eppm;
129 	printf("%lld.%i(est)", eppm/1000, abs((int)(eppm%1000)));
130 
131 	/* Avg the two actual freq samples adjtimex gave us */
132 	ppm = (long long)(tx1.freq + tx2.freq) * 1000 / 2;
133 	ppm = shift_right(ppm, 16);
134 	printf(" %lld.%i(act)", ppm/1000, abs((int)(ppm%1000)));
135 
136 	if (llabs(eppm - ppm) > 1000) {
137 		if (tx1.offset || tx2.offset ||
138 		    tx1.freq != tx2.freq || tx1.tick != tx2.tick) {
139 			printf("	[SKIP]\n");
140 			return ksft_exit_skip("The clock was adjusted externally. Shutdown NTPd or other time sync daemons\n");
141 		}
142 		printf("	[FAILED]\n");
143 		return ksft_exit_fail();
144 	}
145 	printf("	[OK]\n");
146 	return  ksft_exit_pass();
147 }
148