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