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
7 * This test signals the kernel to insert a leap second
8 * every day at midnight GMT. This allows for stessing the
9 * kernel's leap-second behavior, as well as how well applications
10 * handle the leap-second discontinuity.
12 * Usage: leap-a-day [-s] [-i <num>]
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
21 * -i: Number of iterations to run (default: infinite)
23 * Other notes: Disabling NTP prior to running this is advised, as the two
24 * may conflict in their commands to the kernel.
27 * $ gcc leap-a-day.c -o leap-a-day -lrt
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.
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.
46 #include <sys/timex.h>
47 #include <sys/errno.h>
52 #include "../kselftest.h"
54 static inline int ksft_exit_pass(void)
58 static inline int ksft_exit_fail(void)
64 #define NSEC_PER_SEC 1000000000ULL
70 /* returns 1 if a <= b, 0 otherwise */
71 static inline int in_order(struct timespec a
, struct timespec b
)
73 if (a
.tv_sec
< b
.tv_sec
)
75 if (a
.tv_sec
> b
.tv_sec
)
77 if (a
.tv_nsec
> b
.tv_nsec
)
82 struct timespec
timespec_add(struct timespec ts
, unsigned long long ns
)
85 while (ts
.tv_nsec
>= NSEC_PER_SEC
) {
86 ts
.tv_nsec
-= NSEC_PER_SEC
;
92 char *time_state_str(int state
)
95 case TIME_OK
: return "TIME_OK";
96 case TIME_INS
: return "TIME_INS";
97 case TIME_DEL
: return "TIME_DEL";
98 case TIME_OOP
: return "TIME_OOP";
99 case TIME_WAIT
: return "TIME_WAIT";
100 case TIME_BAD
: return "TIME_BAD";
105 /* clear NTP time_status & time_state */
106 int clear_time_state(void)
112 * We have to call adjtime twice here, as kernels
113 * prior to 6b1859dba01c7 (included in 3.5 and
114 * -stable), had an issue with the state machine
115 * and wouldn't clear the STA_INS/DEL flag directly.
117 tx
.modes
= ADJ_STATUS
;
121 /* Clear maxerror, as it can cause UNSYNC to be set */
122 tx
.modes
= ADJ_MAXERROR
;
126 /* Clear the status */
127 tx
.modes
= ADJ_STATUS
;
134 /* Make sure we cleanup on ctrl-c */
135 void handler(int unused
)
141 void sigalarm(int signo
)
149 if (tx
.time
.tv_sec
< next_leap
) {
150 printf("Error: Early timer expiration! (Should be %ld)\n", next_leap
);
152 printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
156 time_state_str(ret
));
158 if (ret
!= TIME_WAIT
) {
159 printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n");
161 printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
165 time_state_str(ret
));
170 /* Test for known hrtimer failure */
171 void test_hrtimer_failure(void)
173 struct timespec now
, target
;
175 clock_gettime(CLOCK_REALTIME
, &now
);
176 target
= timespec_add(now
, NSEC_PER_SEC
/2);
177 clock_nanosleep(CLOCK_REALTIME
, TIMER_ABSTIME
, &target
, NULL
);
178 clock_gettime(CLOCK_REALTIME
, &now
);
180 if (!in_order(target
, now
)) {
181 printf("ERROR: hrtimer early expiration failure observed.\n");
186 int main(int argc
, char **argv
)
189 struct itimerspec its1
;
191 struct sigaction act
;
192 int signum
= SIGRTMAX
;
199 /* Process arguments */
200 while ((opt
= getopt(argc
, argv
, "sti:")) != -1) {
203 printf("Setting time to speed up testing\n");
207 iterations
= atoi(optarg
);
213 printf("Usage: %s [-s] [-i <iterations>]\n", argv
[0]);
214 printf(" -s: Set time to right before leap second each iteration\n");
215 printf(" -i: Number of iterations\n");
216 printf(" -t: Print TAI time\n");
221 /* Make sure TAI support is present if -t was used */
225 if (clock_gettime(CLOCK_TAI
, &ts
)) {
226 printf("System doesn't support CLOCK_TAI\n");
231 signal(SIGINT
, handler
);
232 signal(SIGKILL
, handler
);
234 /* Set up timer signal handler: */
235 sigfillset(&act
.sa_mask
);
237 act
.sa_handler
= sigalarm
;
238 sigaction(signum
, &act
, NULL
);
241 printf("This runs continuously. Press ctrl-c to stop\n");
243 printf("Running for %i iterations. Press ctrl-c to stop\n", iterations
);
252 /* Get the current time */
253 clock_gettime(CLOCK_REALTIME
, &ts
);
255 /* Calculate the next possible leap second 23:59:60 GMT */
256 next_leap
= ts
.tv_sec
;
257 next_leap
+= 86400 - (next_leap
% 86400);
262 tv
.tv_sec
= next_leap
- 10;
264 settimeofday(&tv
, NULL
);
265 printf("Setting time to %s", ctime(&tv
.tv_sec
));
268 /* Reset NTP time state */
271 /* Set the leap second insert flag */
272 tx
.modes
= ADJ_STATUS
;
279 printf("Error: Problem setting STA_INS/STA_DEL!: %s\n",
280 time_state_str(ret
));
281 return ksft_exit_fail();
284 /* Validate STA_INS was set */
287 if (tx
.status
!= STA_INS
&& tx
.status
!= STA_DEL
) {
288 printf("Error: STA_INS/STA_DEL not set!: %s\n",
289 time_state_str(ret
));
290 return ksft_exit_fail();
294 printf("Using TAI time,"
295 " no inconsistencies should be seen!\n");
298 printf("Scheduling leap second for %s", ctime(&next_leap
));
301 printf("Setting timer for %ld - %s", next_leap
, ctime(&next_leap
));
302 memset(&se
, 0, sizeof(se
));
303 se
.sigev_notify
= SIGEV_SIGNAL
;
304 se
.sigev_signo
= signum
;
305 se
.sigev_value
.sival_int
= 0;
306 if (timer_create(CLOCK_REALTIME
, &se
, &tm1
) == -1) {
307 printf("Error: timer_create failed\n");
308 return ksft_exit_fail();
310 its1
.it_value
.tv_sec
= next_leap
;
311 its1
.it_value
.tv_nsec
= 0;
312 its1
.it_interval
.tv_sec
= 0;
313 its1
.it_interval
.tv_nsec
= 0;
314 timer_settime(tm1
, TIMER_ABSTIME
, &its1
, NULL
);
316 /* Wake up 3 seconds before leap */
317 ts
.tv_sec
= next_leap
- 3;
321 while (clock_nanosleep(CLOCK_REALTIME
, TIMER_ABSTIME
, &ts
, NULL
))
322 printf("Something woke us up, returning to sleep\n");
324 /* Validate STA_INS is still set */
327 if (tx
.status
!= STA_INS
&& tx
.status
!= STA_DEL
) {
328 printf("Something cleared STA_INS/STA_DEL, setting it again.\n");
329 tx
.modes
= ADJ_STATUS
;
337 /* Check adjtimex output every half second */
338 now
= tx
.time
.tv_sec
;
339 while (now
< next_leap
+ 2) {
348 clock_gettime(CLOCK_TAI
, &tai
);
349 printf("%ld sec, %9ld ns\t%s\n",
352 time_state_str(ret
));
354 ctime_r(&tx
.time
.tv_sec
, buf
);
355 buf
[strlen(buf
)-1] = 0; /*remove trailing\n */
357 printf("%s + %6ld us (%i)\t%s\n",
361 time_state_str(ret
));
363 now
= tx
.time
.tv_sec
;
364 /* Sleep for another half second */
366 ts
.tv_nsec
= NSEC_PER_SEC
/ 2;
367 clock_nanosleep(CLOCK_MONOTONIC
, 0, &ts
, NULL
);
369 /* Switch to using other mode */
372 /* Note if kernel has known hrtimer failure */
373 test_hrtimer_failure();
375 printf("Leap complete\n");
377 printf("Errors observed\n");
379 return ksft_exit_fail();
382 if ((iterations
!= -1) && !(--iterations
))
387 return ksft_exit_pass();