1 | /* Leap second stress test |
2 | * by: John Stultz (john.stultz@linaro.org) |
3 | * (C) Copyright IBM 2012 |
4 | * (C) Copyright 2013, 2015 Linaro Limited |
5 | * Licensed under the GPLv2 |
6 | * |
7 | * This test signals the kernel to insert a leap second |
8 | * every day at midnight GMT. This allows for stressing the |
9 | * kernel's leap-second behavior, as well as how well applications |
10 | * handle the leap-second discontinuity. |
11 | * |
12 | * Usage: leap-a-day [-s] [-i <num>] |
13 | * |
14 | * Options: |
15 | * -s: Each iteration, set the date to 10 seconds before midnight GMT. |
16 | * This speeds up the number of leapsecond transitions tested, |
17 | * but because it calls settimeofday frequently, advancing the |
18 | * time by 24 hours every ~16 seconds, it may cause application |
19 | * disruption. |
20 | * |
21 | * -i: Number of iterations to run (default: infinite) |
22 | * |
23 | * Other notes: Disabling NTP prior to running this is advised, as the two |
24 | * may conflict in their commands to the kernel. |
25 | * |
26 | * To build: |
27 | * $ gcc leap-a-day.c -o leap-a-day -lrt |
28 | * |
29 | * This program is free software: you can redistribute it and/or modify |
30 | * it under the terms of the GNU General Public License as published by |
31 | * the Free Software Foundation, either version 2 of the License, or |
32 | * (at your option) any later version. |
33 | * |
34 | * This program is distributed in the hope that it will be useful, |
35 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
36 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
37 | * GNU General Public License for more details. |
38 | */ |
39 | |
40 | |
41 | |
42 | #include <stdio.h> |
43 | #include <stdlib.h> |
44 | #include <time.h> |
45 | #include <sys/time.h> |
46 | #include <sys/timex.h> |
47 | #include <sys/errno.h> |
48 | #include <string.h> |
49 | #include <signal.h> |
50 | #include <unistd.h> |
51 | #include "../kselftest.h" |
52 | |
53 | #define NSEC_PER_SEC 1000000000ULL |
54 | #define CLOCK_TAI 11 |
55 | |
56 | time_t next_leap; |
57 | int error_found; |
58 | |
59 | /* returns 1 if a <= b, 0 otherwise */ |
60 | static inline int in_order(struct timespec a, struct timespec b) |
61 | { |
62 | if (a.tv_sec < b.tv_sec) |
63 | return 1; |
64 | if (a.tv_sec > b.tv_sec) |
65 | return 0; |
66 | if (a.tv_nsec > b.tv_nsec) |
67 | return 0; |
68 | return 1; |
69 | } |
70 | |
71 | struct timespec timespec_add(struct timespec ts, unsigned long long ns) |
72 | { |
73 | ts.tv_nsec += ns; |
74 | while (ts.tv_nsec >= NSEC_PER_SEC) { |
75 | ts.tv_nsec -= NSEC_PER_SEC; |
76 | ts.tv_sec++; |
77 | } |
78 | return ts; |
79 | } |
80 | |
81 | char *time_state_str(int state) |
82 | { |
83 | switch (state) { |
84 | case TIME_OK: return "TIME_OK" ; |
85 | case TIME_INS: return "TIME_INS" ; |
86 | case TIME_DEL: return "TIME_DEL" ; |
87 | case TIME_OOP: return "TIME_OOP" ; |
88 | case TIME_WAIT: return "TIME_WAIT" ; |
89 | case TIME_BAD: return "TIME_BAD" ; |
90 | } |
91 | return "ERROR" ; |
92 | } |
93 | |
94 | /* clear NTP time_status & time_state */ |
95 | int clear_time_state(void) |
96 | { |
97 | struct timex tx; |
98 | int ret; |
99 | |
100 | /* |
101 | * We have to call adjtime twice here, as kernels |
102 | * prior to 6b1859dba01c7 (included in 3.5 and |
103 | * -stable), had an issue with the state machine |
104 | * and wouldn't clear the STA_INS/DEL flag directly. |
105 | */ |
106 | tx.modes = ADJ_STATUS; |
107 | tx.status = STA_PLL; |
108 | ret = adjtimex(&tx); |
109 | |
110 | /* Clear maxerror, as it can cause UNSYNC to be set */ |
111 | tx.modes = ADJ_MAXERROR; |
112 | tx.maxerror = 0; |
113 | ret = adjtimex(&tx); |
114 | |
115 | /* Clear the status */ |
116 | tx.modes = ADJ_STATUS; |
117 | tx.status = 0; |
118 | ret = adjtimex(&tx); |
119 | |
120 | return ret; |
121 | } |
122 | |
123 | /* Make sure we cleanup on ctrl-c */ |
124 | void handler(int unused) |
125 | { |
126 | clear_time_state(); |
127 | exit(0); |
128 | } |
129 | |
130 | void sigalarm(int signo) |
131 | { |
132 | struct timex tx; |
133 | int ret; |
134 | |
135 | tx.modes = 0; |
136 | ret = adjtimex(&tx); |
137 | |
138 | if (tx.time.tv_sec < next_leap) { |
139 | printf("Error: Early timer expiration! (Should be %ld)\n" , next_leap); |
140 | error_found = 1; |
141 | printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n" , |
142 | tx.time.tv_sec, |
143 | tx.time.tv_usec, |
144 | tx.tai, |
145 | time_state_str(state: ret)); |
146 | } |
147 | if (ret != TIME_WAIT) { |
148 | printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n" ); |
149 | error_found = 1; |
150 | printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n" , |
151 | tx.time.tv_sec, |
152 | tx.time.tv_usec, |
153 | tx.tai, |
154 | time_state_str(state: ret)); |
155 | } |
156 | } |
157 | |
158 | |
159 | /* Test for known hrtimer failure */ |
160 | void test_hrtimer_failure(void) |
161 | { |
162 | struct timespec now, target; |
163 | |
164 | clock_gettime(CLOCK_REALTIME, &now); |
165 | target = timespec_add(now, NSEC_PER_SEC/2); |
166 | clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL); |
167 | clock_gettime(CLOCK_REALTIME, &now); |
168 | |
169 | if (!in_order(a: target, b: now)) { |
170 | printf("ERROR: hrtimer early expiration failure observed.\n" ); |
171 | error_found = 1; |
172 | } |
173 | } |
174 | |
175 | int main(int argc, char **argv) |
176 | { |
177 | timer_t tm1; |
178 | struct itimerspec its1; |
179 | struct sigevent se; |
180 | struct sigaction act; |
181 | int signum = SIGRTMAX; |
182 | int settime = 1; |
183 | int tai_time = 0; |
184 | int insert = 1; |
185 | int iterations = 10; |
186 | int opt; |
187 | |
188 | /* Process arguments */ |
189 | while ((opt = getopt(argc, argv, "sti:" )) != -1) { |
190 | switch (opt) { |
191 | case 'w': |
192 | printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n" ); |
193 | settime = 0; |
194 | break; |
195 | case 'i': |
196 | iterations = atoi(optarg); |
197 | break; |
198 | case 't': |
199 | tai_time = 1; |
200 | break; |
201 | default: |
202 | printf("Usage: %s [-w] [-i <iterations>]\n" , argv[0]); |
203 | printf(" -w: Set flag and wait for leap second each iteration" ); |
204 | printf(" (default sets time to right before leapsecond)\n" ); |
205 | printf(" -i: Number of iterations (-1 = infinite, default is 10)\n" ); |
206 | printf(" -t: Print TAI time\n" ); |
207 | exit(-1); |
208 | } |
209 | } |
210 | |
211 | /* Make sure TAI support is present if -t was used */ |
212 | if (tai_time) { |
213 | struct timespec ts; |
214 | |
215 | if (clock_gettime(CLOCK_TAI, &ts)) { |
216 | printf("System doesn't support CLOCK_TAI\n" ); |
217 | ksft_exit_fail(); |
218 | } |
219 | } |
220 | |
221 | signal(SIGINT, handler); |
222 | signal(SIGKILL, handler); |
223 | |
224 | /* Set up timer signal handler: */ |
225 | sigfillset(&act.sa_mask); |
226 | act.sa_flags = 0; |
227 | act.sa_handler = sigalarm; |
228 | sigaction(signum, &act, NULL); |
229 | |
230 | if (iterations < 0) |
231 | printf("This runs continuously. Press ctrl-c to stop\n" ); |
232 | else |
233 | printf("Running for %i iterations. Press ctrl-c to stop\n" , iterations); |
234 | |
235 | printf("\n" ); |
236 | while (1) { |
237 | int ret; |
238 | struct timespec ts; |
239 | struct timex tx; |
240 | time_t now; |
241 | |
242 | /* Get the current time */ |
243 | clock_gettime(CLOCK_REALTIME, &ts); |
244 | |
245 | /* Calculate the next possible leap second 23:59:60 GMT */ |
246 | next_leap = ts.tv_sec; |
247 | next_leap += 86400 - (next_leap % 86400); |
248 | |
249 | if (settime) { |
250 | struct timeval tv; |
251 | |
252 | tv.tv_sec = next_leap - 10; |
253 | tv.tv_usec = 0; |
254 | settimeofday(&tv, NULL); |
255 | printf("Setting time to %s" , ctime(&tv.tv_sec)); |
256 | } |
257 | |
258 | /* Reset NTP time state */ |
259 | clear_time_state(); |
260 | |
261 | /* Set the leap second insert flag */ |
262 | tx.modes = ADJ_STATUS; |
263 | if (insert) |
264 | tx.status = STA_INS; |
265 | else |
266 | tx.status = STA_DEL; |
267 | ret = adjtimex(&tx); |
268 | if (ret < 0) { |
269 | printf("Error: Problem setting STA_INS/STA_DEL!: %s\n" , |
270 | time_state_str(state: ret)); |
271 | return ksft_exit_fail(); |
272 | } |
273 | |
274 | /* Validate STA_INS was set */ |
275 | tx.modes = 0; |
276 | ret = adjtimex(&tx); |
277 | if (tx.status != STA_INS && tx.status != STA_DEL) { |
278 | printf("Error: STA_INS/STA_DEL not set!: %s\n" , |
279 | time_state_str(state: ret)); |
280 | return ksft_exit_fail(); |
281 | } |
282 | |
283 | if (tai_time) { |
284 | printf("Using TAI time," |
285 | " no inconsistencies should be seen!\n" ); |
286 | } |
287 | |
288 | printf("Scheduling leap second for %s" , ctime(&next_leap)); |
289 | |
290 | /* Set up timer */ |
291 | printf("Setting timer for %ld - %s" , next_leap, ctime(&next_leap)); |
292 | memset(&se, 0, sizeof(se)); |
293 | se.sigev_notify = SIGEV_SIGNAL; |
294 | se.sigev_signo = signum; |
295 | se.sigev_value.sival_int = 0; |
296 | if (timer_create(CLOCK_REALTIME, &se, &tm1) == -1) { |
297 | printf("Error: timer_create failed\n" ); |
298 | return ksft_exit_fail(); |
299 | } |
300 | its1.it_value.tv_sec = next_leap; |
301 | its1.it_value.tv_nsec = 0; |
302 | its1.it_interval.tv_sec = 0; |
303 | its1.it_interval.tv_nsec = 0; |
304 | timer_settime(tm1, TIMER_ABSTIME, &its1, NULL); |
305 | |
306 | /* Wake up 3 seconds before leap */ |
307 | ts.tv_sec = next_leap - 3; |
308 | ts.tv_nsec = 0; |
309 | |
310 | |
311 | while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL)) |
312 | printf("Something woke us up, returning to sleep\n" ); |
313 | |
314 | /* Validate STA_INS is still set */ |
315 | tx.modes = 0; |
316 | ret = adjtimex(&tx); |
317 | if (tx.status != STA_INS && tx.status != STA_DEL) { |
318 | printf("Something cleared STA_INS/STA_DEL, setting it again.\n" ); |
319 | tx.modes = ADJ_STATUS; |
320 | if (insert) |
321 | tx.status = STA_INS; |
322 | else |
323 | tx.status = STA_DEL; |
324 | ret = adjtimex(&tx); |
325 | } |
326 | |
327 | /* Check adjtimex output every half second */ |
328 | now = tx.time.tv_sec; |
329 | while (now < next_leap + 2) { |
330 | char buf[26]; |
331 | struct timespec tai; |
332 | int ret; |
333 | |
334 | tx.modes = 0; |
335 | ret = adjtimex(&tx); |
336 | |
337 | if (tai_time) { |
338 | clock_gettime(CLOCK_TAI, &tai); |
339 | printf("%ld sec, %9ld ns\t%s\n" , |
340 | tai.tv_sec, |
341 | tai.tv_nsec, |
342 | time_state_str(state: ret)); |
343 | } else { |
344 | ctime_r(&tx.time.tv_sec, buf); |
345 | buf[strlen(buf)-1] = 0; /*remove trailing\n */ |
346 | |
347 | printf("%s + %6ld us (%i)\t%s\n" , |
348 | buf, |
349 | tx.time.tv_usec, |
350 | tx.tai, |
351 | time_state_str(state: ret)); |
352 | } |
353 | now = tx.time.tv_sec; |
354 | /* Sleep for another half second */ |
355 | ts.tv_sec = 0; |
356 | ts.tv_nsec = NSEC_PER_SEC / 2; |
357 | clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); |
358 | } |
359 | /* Switch to using other mode */ |
360 | insert = !insert; |
361 | |
362 | /* Note if kernel has known hrtimer failure */ |
363 | test_hrtimer_failure(); |
364 | |
365 | printf("Leap complete\n" ); |
366 | if (error_found) { |
367 | printf("Errors observed\n" ); |
368 | clear_time_state(); |
369 | return ksft_exit_fail(); |
370 | } |
371 | printf("\n" ); |
372 | if ((iterations != -1) && !(--iterations)) |
373 | break; |
374 | } |
375 | |
376 | clear_time_state(); |
377 | return ksft_exit_pass(); |
378 | } |
379 | |