leap-a-day.c (9202B)
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 56time_t next_leap; 57int error_found; 58 59/* returns 1 if a <= b, 0 otherwise */ 60static 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 71struct 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 81char *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 */ 95int 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 */ 124void handler(int unused) 125{ 126 clear_time_state(); 127 exit(0); 128} 129 130void 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(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(ret)); 155 } 156} 157 158 159/* Test for known hrtimer failure */ 160void 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(target, now)) { 170 printf("ERROR: hrtimer early expiration failure observed.\n"); 171 error_found = 1; 172 } 173} 174 175int 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(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(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(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(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}