1 | /* adjtimex() tick adjustment test |
2 | * by: John Stultz <john.stultz@linaro.org> |
3 | * (C) Copyright Linaro Limited 2015 |
4 | * Licensed under the GPLv2 |
5 | * |
6 | * To build: |
7 | * $ gcc adjtick.c -o adjtick -lrt |
8 | * |
9 | * This program is free software: you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation, either version 2 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | */ |
19 | #include <stdio.h> |
20 | #include <unistd.h> |
21 | #include <stdlib.h> |
22 | #include <sys/time.h> |
23 | #include <sys/timex.h> |
24 | #include <time.h> |
25 | |
26 | #include "../kselftest.h" |
27 | |
28 | #define CLOCK_MONOTONIC_RAW 4 |
29 | |
30 | #define NSEC_PER_SEC 1000000000LL |
31 | #define USEC_PER_SEC 1000000 |
32 | |
33 | #define MILLION 1000000 |
34 | |
35 | long systick; |
36 | |
37 | long long llabs(long long val) |
38 | { |
39 | if (val < 0) |
40 | val = -val; |
41 | return val; |
42 | } |
43 | |
44 | unsigned long long ts_to_nsec(struct timespec ts) |
45 | { |
46 | return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; |
47 | } |
48 | |
49 | struct timespec nsec_to_ts(long long ns) |
50 | { |
51 | struct timespec ts; |
52 | |
53 | ts.tv_sec = ns/NSEC_PER_SEC; |
54 | ts.tv_nsec = ns%NSEC_PER_SEC; |
55 | |
56 | return ts; |
57 | } |
58 | |
59 | long long diff_timespec(struct timespec start, struct timespec end) |
60 | { |
61 | long long start_ns, end_ns; |
62 | |
63 | start_ns = ts_to_nsec(ts: start); |
64 | end_ns = ts_to_nsec(ts: end); |
65 | |
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 | clock_gettime(CLOCK_MONOTONIC, mon); |
76 | clock_gettime(CLOCK_MONOTONIC_RAW, raw); |
77 | |
78 | /* Try to get a more tightly bound pairing */ |
79 | for (i = 0; i < 3; i++) { |
80 | long long newdiff; |
81 | |
82 | clock_gettime(CLOCK_MONOTONIC, &start); |
83 | clock_gettime(CLOCK_MONOTONIC_RAW, &mid); |
84 | clock_gettime(CLOCK_MONOTONIC, &end); |
85 | |
86 | newdiff = diff_timespec(start: start, end: end); |
87 | if (diff == 0 || newdiff < diff) { |
88 | diff = newdiff; |
89 | *raw = mid; |
90 | tmp = (ts_to_nsec(ts: start) + ts_to_nsec(ts: end))/2; |
91 | *mon = nsec_to_ts(tmp); |
92 | } |
93 | } |
94 | } |
95 | |
96 | long long get_ppm_drift(void) |
97 | { |
98 | struct timespec mon_start, raw_start, mon_end, raw_end; |
99 | long long delta1, delta2, eppm; |
100 | |
101 | get_monotonic_and_raw(mon: &mon_start, raw: &raw_start); |
102 | |
103 | sleep(15); |
104 | |
105 | get_monotonic_and_raw(mon: &mon_end, raw: &raw_end); |
106 | |
107 | delta1 = diff_timespec(start: mon_start, end: mon_end); |
108 | delta2 = diff_timespec(start: raw_start, end: raw_end); |
109 | |
110 | eppm = (delta1*MILLION)/delta2 - MILLION; |
111 | |
112 | return eppm; |
113 | } |
114 | |
115 | int check_tick_adj(long tickval) |
116 | { |
117 | long long eppm, ppm; |
118 | struct timex tx1; |
119 | |
120 | tx1.modes = ADJ_TICK; |
121 | tx1.modes |= ADJ_OFFSET; |
122 | tx1.modes |= ADJ_FREQUENCY; |
123 | tx1.modes |= ADJ_STATUS; |
124 | |
125 | tx1.status = STA_PLL; |
126 | tx1.offset = 0; |
127 | tx1.freq = 0; |
128 | tx1.tick = tickval; |
129 | |
130 | adjtimex(&tx1); |
131 | |
132 | sleep(1); |
133 | |
134 | ppm = ((long long)tickval * MILLION)/systick - MILLION; |
135 | printf("Estimating tick (act: %ld usec, %lld ppm): " , tickval, ppm); |
136 | |
137 | eppm = get_ppm_drift(); |
138 | printf("%lld usec, %lld ppm" , systick + (systick * eppm / MILLION), eppm); |
139 | fflush(stdout); |
140 | |
141 | tx1.modes = 0; |
142 | adjtimex(&tx1); |
143 | |
144 | if (tx1.offset || tx1.freq || tx1.tick != tickval) { |
145 | printf(" [ERROR]\n" ); |
146 | printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n" ); |
147 | return -1; |
148 | } |
149 | |
150 | /* |
151 | * Here we use 100ppm difference as an error bound. |
152 | * We likely should see better, but some coarse clocksources |
153 | * cannot match the HZ tick size accurately, so we have a |
154 | * internal correction factor that doesn't scale exactly |
155 | * with the adjustment, resulting in > 10ppm error during |
156 | * a 10% adjustment. 100ppm also gives us more breathing |
157 | * room for interruptions during the measurement. |
158 | */ |
159 | if (llabs(val: eppm - ppm) > 100) { |
160 | printf(" [FAILED]\n" ); |
161 | return -1; |
162 | } |
163 | printf(" [OK]\n" ); |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | int main(int argc, char **argv) |
169 | { |
170 | struct timespec raw; |
171 | long tick, max, interval, err; |
172 | struct timex tx1; |
173 | |
174 | err = 0; |
175 | setbuf(stdout, NULL); |
176 | |
177 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) { |
178 | printf("ERR: NO CLOCK_MONOTONIC_RAW\n" ); |
179 | return -1; |
180 | } |
181 | |
182 | printf("Each iteration takes about 15 seconds\n" ); |
183 | |
184 | systick = sysconf(_SC_CLK_TCK); |
185 | systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK); |
186 | max = systick/10; /* +/- 10% */ |
187 | interval = max/4; /* in 4 steps each side */ |
188 | |
189 | for (tick = (systick - max); tick < (systick + max); tick += interval) { |
190 | if (check_tick_adj(tickval: tick)) { |
191 | err = 1; |
192 | break; |
193 | } |
194 | } |
195 | |
196 | /* Reset things to zero */ |
197 | tx1.modes = ADJ_TICK; |
198 | tx1.modes |= ADJ_OFFSET; |
199 | tx1.modes |= ADJ_FREQUENCY; |
200 | |
201 | tx1.offset = 0; |
202 | tx1.freq = 0; |
203 | tx1.tick = systick; |
204 | |
205 | adjtimex(&tx1); |
206 | |
207 | if (err) |
208 | return ksft_exit_fail(); |
209 | |
210 | return ksft_exit_pass(); |
211 | } |
212 | |