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>
51 #include "../kselftest.h"
53 #define NSEC_PER_SEC 1000000000ULL
59 /* returns 1 if a <= b, 0 otherwise */
60 static inline int in_order(struct timespec a
, struct timespec b
)
62 if (a
.tv_sec
< b
.tv_sec
)
64 if (a
.tv_sec
> b
.tv_sec
)
66 if (a
.tv_nsec
> b
.tv_nsec
)
71 struct timespec
timespec_add(struct timespec ts
, unsigned long long ns
)
74 while (ts
.tv_nsec
>= NSEC_PER_SEC
) {
75 ts
.tv_nsec
-= NSEC_PER_SEC
;
81 char *time_state_str(int 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";
94 /* clear NTP time_status & time_state */
95 int clear_time_state(void)
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.
106 tx
.modes
= ADJ_STATUS
;
110 /* Clear maxerror, as it can cause UNSYNC to be set */
111 tx
.modes
= ADJ_MAXERROR
;
115 /* Clear the status */
116 tx
.modes
= ADJ_STATUS
;
123 /* Make sure we cleanup on ctrl-c */
124 void handler(int unused
)
130 void sigalarm(int signo
)
138 if (tx
.time
.tv_sec
< next_leap
) {
139 printf("Error: Early timer expiration! (Should be %ld)\n", next_leap
);
141 printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
145 time_state_str(ret
));
147 if (ret
!= TIME_WAIT
) {
148 printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n");
150 printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
154 time_state_str(ret
));
159 /* Test for known hrtimer failure */
160 void test_hrtimer_failure(void)
162 struct timespec now
, target
;
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
);
169 if (!in_order(target
, now
)) {
170 printf("ERROR: hrtimer early expiration failure observed.\n");
175 int main(int argc
, char **argv
)
178 struct itimerspec its1
;
180 struct sigaction act
;
181 int signum
= SIGRTMAX
;
188 /* Process arguments */
189 while ((opt
= getopt(argc
, argv
, "sti:")) != -1) {
192 printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n");
196 iterations
= atoi(optarg
);
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");
211 /* Make sure TAI support is present if -t was used */
215 if (clock_gettime(CLOCK_TAI
, &ts
)) {
216 printf("System doesn't support CLOCK_TAI\n");
221 signal(SIGINT
, handler
);
222 signal(SIGKILL
, handler
);
224 /* Set up timer signal handler: */
225 sigfillset(&act
.sa_mask
);
227 act
.sa_handler
= sigalarm
;
228 sigaction(signum
, &act
, NULL
);
231 printf("This runs continuously. Press ctrl-c to stop\n");
233 printf("Running for %i iterations. Press ctrl-c to stop\n", iterations
);
242 /* Get the current time */
243 clock_gettime(CLOCK_REALTIME
, &ts
);
245 /* Calculate the next possible leap second 23:59:60 GMT */
246 next_leap
= ts
.tv_sec
;
247 next_leap
+= 86400 - (next_leap
% 86400);
252 tv
.tv_sec
= next_leap
- 10;
254 settimeofday(&tv
, NULL
);
255 printf("Setting time to %s", ctime(&tv
.tv_sec
));
258 /* Reset NTP time state */
261 /* Set the leap second insert flag */
262 tx
.modes
= ADJ_STATUS
;
269 printf("Error: Problem setting STA_INS/STA_DEL!: %s\n",
270 time_state_str(ret
));
271 return ksft_exit_fail();
274 /* Validate STA_INS was set */
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(ret
));
280 return ksft_exit_fail();
284 printf("Using TAI time,"
285 " no inconsistencies should be seen!\n");
288 printf("Scheduling leap second for %s", ctime(&next_leap
));
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();
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
);
306 /* Wake up 3 seconds before leap */
307 ts
.tv_sec
= next_leap
- 3;
311 while (clock_nanosleep(CLOCK_REALTIME
, TIMER_ABSTIME
, &ts
, NULL
))
312 printf("Something woke us up, returning to sleep\n");
314 /* Validate STA_INS is still set */
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
;
327 /* Check adjtimex output every half second */
328 now
= tx
.time
.tv_sec
;
329 while (now
< next_leap
+ 2) {
338 clock_gettime(CLOCK_TAI
, &tai
);
339 printf("%ld sec, %9ld ns\t%s\n",
342 time_state_str(ret
));
344 ctime_r(&tx
.time
.tv_sec
, buf
);
345 buf
[strlen(buf
)-1] = 0; /*remove trailing\n */
347 printf("%s + %6ld us (%i)\t%s\n",
351 time_state_str(ret
));
353 now
= tx
.time
.tv_sec
;
354 /* Sleep for another half second */
356 ts
.tv_nsec
= NSEC_PER_SEC
/ 2;
357 clock_nanosleep(CLOCK_MONOTONIC
, 0, &ts
, NULL
);
359 /* Switch to using other mode */
362 /* Note if kernel has known hrtimer failure */
363 test_hrtimer_failure();
365 printf("Leap complete\n");
367 printf("Errors observed\n");
369 return ksft_exit_fail();
372 if ((iterations
!= -1) && !(--iterations
))
377 return ksft_exit_pass();