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 stessing 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 #ifdef KTEST 52 #include "../kselftest.h" 53 #else 54 static inline int ksft_exit_pass(void) 55 { 56 exit(0); 57 } 58 static inline int ksft_exit_fail(void) 59 { 60 exit(1); 61 } 62 #endif 63 64 #define NSEC_PER_SEC 1000000000ULL 65 #define CLOCK_TAI 11 66 67 time_t next_leap; 68 int error_found; 69 70 /* returns 1 if a <= b, 0 otherwise */ 71 static inline int in_order(struct timespec a, struct timespec b) 72 { 73 if (a.tv_sec < b.tv_sec) 74 return 1; 75 if (a.tv_sec > b.tv_sec) 76 return 0; 77 if (a.tv_nsec > b.tv_nsec) 78 return 0; 79 return 1; 80 } 81 82 struct timespec timespec_add(struct timespec ts, unsigned long long ns) 83 { 84 ts.tv_nsec += ns; 85 while (ts.tv_nsec >= NSEC_PER_SEC) { 86 ts.tv_nsec -= NSEC_PER_SEC; 87 ts.tv_sec++; 88 } 89 return ts; 90 } 91 92 char *time_state_str(int state) 93 { 94 switch (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"; 101 } 102 return "ERROR"; 103 } 104 105 /* clear NTP time_status & time_state */ 106 int clear_time_state(void) 107 { 108 struct timex tx; 109 int ret; 110 111 /* 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. 116 */ 117 tx.modes = ADJ_STATUS; 118 tx.status = STA_PLL; 119 ret = adjtimex(&tx); 120 121 /* Clear maxerror, as it can cause UNSYNC to be set */ 122 tx.modes = ADJ_MAXERROR; 123 tx.maxerror = 0; 124 ret = adjtimex(&tx); 125 126 /* Clear the status */ 127 tx.modes = ADJ_STATUS; 128 tx.status = 0; 129 ret = adjtimex(&tx); 130 131 return ret; 132 } 133 134 /* Make sure we cleanup on ctrl-c */ 135 void handler(int unused) 136 { 137 clear_time_state(); 138 exit(0); 139 } 140 141 void sigalarm(int signo) 142 { 143 struct timex tx; 144 int ret; 145 146 tx.modes = 0; 147 ret = adjtimex(&tx); 148 149 if (tx.time.tv_sec < next_leap) { 150 printf("Error: Early timer expiration! (Should be %ld)\n", next_leap); 151 error_found = 1; 152 printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n", 153 tx.time.tv_sec, 154 tx.time.tv_usec, 155 tx.tai, 156 time_state_str(ret)); 157 } 158 if (ret != TIME_WAIT) { 159 printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n"); 160 error_found = 1; 161 printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n", 162 tx.time.tv_sec, 163 tx.time.tv_usec, 164 tx.tai, 165 time_state_str(ret)); 166 } 167 } 168 169 170 /* Test for known hrtimer failure */ 171 void test_hrtimer_failure(void) 172 { 173 struct timespec now, target; 174 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); 179 180 if (!in_order(target, now)) { 181 printf("ERROR: hrtimer early expiration failure observed.\n"); 182 error_found = 1; 183 } 184 } 185 186 int main(int argc, char **argv) 187 { 188 timer_t tm1; 189 struct itimerspec its1; 190 struct sigevent se; 191 struct sigaction act; 192 int signum = SIGRTMAX; 193 int settime = 0; 194 int tai_time = 0; 195 int insert = 1; 196 int iterations = -1; 197 int opt; 198 199 /* Process arguments */ 200 while ((opt = getopt(argc, argv, "sti:")) != -1) { 201 switch (opt) { 202 case 's': 203 printf("Setting time to speed up testing\n"); 204 settime = 1; 205 break; 206 case 'i': 207 iterations = atoi(optarg); 208 break; 209 case 't': 210 tai_time = 1; 211 break; 212 default: 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"); 217 exit(-1); 218 } 219 } 220 221 /* Make sure TAI support is present if -t was used */ 222 if (tai_time) { 223 struct timespec ts; 224 225 if (clock_gettime(CLOCK_TAI, &ts)) { 226 printf("System doesn't support CLOCK_TAI\n"); 227 ksft_exit_fail(); 228 } 229 } 230 231 signal(SIGINT, handler); 232 signal(SIGKILL, handler); 233 234 /* Set up timer signal handler: */ 235 sigfillset(&act.sa_mask); 236 act.sa_flags = 0; 237 act.sa_handler = sigalarm; 238 sigaction(signum, &act, NULL); 239 240 if (iterations < 0) 241 printf("This runs continuously. Press ctrl-c to stop\n"); 242 else 243 printf("Running for %i iterations. Press ctrl-c to stop\n", iterations); 244 245 printf("\n"); 246 while (1) { 247 int ret; 248 struct timespec ts; 249 struct timex tx; 250 time_t now; 251 252 /* Get the current time */ 253 clock_gettime(CLOCK_REALTIME, &ts); 254 255 /* Calculate the next possible leap second 23:59:60 GMT */ 256 next_leap = ts.tv_sec; 257 next_leap += 86400 - (next_leap % 86400); 258 259 if (settime) { 260 struct timeval tv; 261 262 tv.tv_sec = next_leap - 10; 263 tv.tv_usec = 0; 264 settimeofday(&tv, NULL); 265 printf("Setting time to %s", ctime(&tv.tv_sec)); 266 } 267 268 /* Reset NTP time state */ 269 clear_time_state(); 270 271 /* Set the leap second insert flag */ 272 tx.modes = ADJ_STATUS; 273 if (insert) 274 tx.status = STA_INS; 275 else 276 tx.status = STA_DEL; 277 ret = adjtimex(&tx); 278 if (ret < 0) { 279 printf("Error: Problem setting STA_INS/STA_DEL!: %s\n", 280 time_state_str(ret)); 281 return ksft_exit_fail(); 282 } 283 284 /* Validate STA_INS was set */ 285 tx.modes = 0; 286 ret = adjtimex(&tx); 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(); 291 } 292 293 if (tai_time) { 294 printf("Using TAI time," 295 " no inconsistencies should be seen!\n"); 296 } 297 298 printf("Scheduling leap second for %s", ctime(&next_leap)); 299 300 /* Set up timer */ 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(); 309 } 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); 315 316 /* Wake up 3 seconds before leap */ 317 ts.tv_sec = next_leap - 3; 318 ts.tv_nsec = 0; 319 320 321 while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL)) 322 printf("Something woke us up, returning to sleep\n"); 323 324 /* Validate STA_INS is still set */ 325 tx.modes = 0; 326 ret = adjtimex(&tx); 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; 330 if (insert) 331 tx.status = STA_INS; 332 else 333 tx.status = STA_DEL; 334 ret = adjtimex(&tx); 335 } 336 337 /* Check adjtimex output every half second */ 338 now = tx.time.tv_sec; 339 while (now < next_leap + 2) { 340 char buf[26]; 341 struct timespec tai; 342 int ret; 343 344 tx.modes = 0; 345 ret = adjtimex(&tx); 346 347 if (tai_time) { 348 clock_gettime(CLOCK_TAI, &tai); 349 printf("%ld sec, %9ld ns\t%s\n", 350 tai.tv_sec, 351 tai.tv_nsec, 352 time_state_str(ret)); 353 } else { 354 ctime_r(&tx.time.tv_sec, buf); 355 buf[strlen(buf)-1] = 0; /*remove trailing\n */ 356 357 printf("%s + %6ld us (%i)\t%s\n", 358 buf, 359 tx.time.tv_usec, 360 tx.tai, 361 time_state_str(ret)); 362 } 363 now = tx.time.tv_sec; 364 /* Sleep for another half second */ 365 ts.tv_sec = 0; 366 ts.tv_nsec = NSEC_PER_SEC / 2; 367 clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); 368 } 369 /* Switch to using other mode */ 370 insert = !insert; 371 372 /* Note if kernel has known hrtimer failure */ 373 test_hrtimer_failure(); 374 375 printf("Leap complete\n"); 376 if (error_found) { 377 printf("Errors observed\n"); 378 clear_time_state(); 379 return ksft_exit_fail(); 380 } 381 printf("\n"); 382 if ((iterations != -1) && !(--iterations)) 383 break; 384 } 385 386 clear_time_state(); 387 return ksft_exit_pass(); 388 } 389