162306a36Sopenharmony_ci/* Leap second stress test 262306a36Sopenharmony_ci * by: John Stultz (john.stultz@linaro.org) 362306a36Sopenharmony_ci * (C) Copyright IBM 2012 462306a36Sopenharmony_ci * (C) Copyright 2013, 2015 Linaro Limited 562306a36Sopenharmony_ci * Licensed under the GPLv2 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This test signals the kernel to insert a leap second 862306a36Sopenharmony_ci * every day at midnight GMT. This allows for stressing the 962306a36Sopenharmony_ci * kernel's leap-second behavior, as well as how well applications 1062306a36Sopenharmony_ci * handle the leap-second discontinuity. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Usage: leap-a-day [-s] [-i <num>] 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Options: 1562306a36Sopenharmony_ci * -s: Each iteration, set the date to 10 seconds before midnight GMT. 1662306a36Sopenharmony_ci * This speeds up the number of leapsecond transitions tested, 1762306a36Sopenharmony_ci * but because it calls settimeofday frequently, advancing the 1862306a36Sopenharmony_ci * time by 24 hours every ~16 seconds, it may cause application 1962306a36Sopenharmony_ci * disruption. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * -i: Number of iterations to run (default: infinite) 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Other notes: Disabling NTP prior to running this is advised, as the two 2462306a36Sopenharmony_ci * may conflict in their commands to the kernel. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * To build: 2762306a36Sopenharmony_ci * $ gcc leap-a-day.c -o leap-a-day -lrt 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * This program is free software: you can redistribute it and/or modify 3062306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 3162306a36Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 3262306a36Sopenharmony_ci * (at your option) any later version. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 3562306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 3662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 3762306a36Sopenharmony_ci * GNU General Public License for more details. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <stdio.h> 4362306a36Sopenharmony_ci#include <stdlib.h> 4462306a36Sopenharmony_ci#include <time.h> 4562306a36Sopenharmony_ci#include <sys/time.h> 4662306a36Sopenharmony_ci#include <sys/timex.h> 4762306a36Sopenharmony_ci#include <sys/errno.h> 4862306a36Sopenharmony_ci#include <string.h> 4962306a36Sopenharmony_ci#include <signal.h> 5062306a36Sopenharmony_ci#include <unistd.h> 5162306a36Sopenharmony_ci#include "../kselftest.h" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define NSEC_PER_SEC 1000000000ULL 5462306a36Sopenharmony_ci#define CLOCK_TAI 11 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_citime_t next_leap; 5762306a36Sopenharmony_ciint error_found; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* returns 1 if a <= b, 0 otherwise */ 6062306a36Sopenharmony_cistatic inline int in_order(struct timespec a, struct timespec b) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (a.tv_sec < b.tv_sec) 6362306a36Sopenharmony_ci return 1; 6462306a36Sopenharmony_ci if (a.tv_sec > b.tv_sec) 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci if (a.tv_nsec > b.tv_nsec) 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci return 1; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct timespec timespec_add(struct timespec ts, unsigned long long ns) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci ts.tv_nsec += ns; 7462306a36Sopenharmony_ci while (ts.tv_nsec >= NSEC_PER_SEC) { 7562306a36Sopenharmony_ci ts.tv_nsec -= NSEC_PER_SEC; 7662306a36Sopenharmony_ci ts.tv_sec++; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci return ts; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cichar *time_state_str(int state) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci switch (state) { 8462306a36Sopenharmony_ci case TIME_OK: return "TIME_OK"; 8562306a36Sopenharmony_ci case TIME_INS: return "TIME_INS"; 8662306a36Sopenharmony_ci case TIME_DEL: return "TIME_DEL"; 8762306a36Sopenharmony_ci case TIME_OOP: return "TIME_OOP"; 8862306a36Sopenharmony_ci case TIME_WAIT: return "TIME_WAIT"; 8962306a36Sopenharmony_ci case TIME_BAD: return "TIME_BAD"; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci return "ERROR"; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* clear NTP time_status & time_state */ 9562306a36Sopenharmony_ciint clear_time_state(void) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct timex tx; 9862306a36Sopenharmony_ci int ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * We have to call adjtime twice here, as kernels 10262306a36Sopenharmony_ci * prior to 6b1859dba01c7 (included in 3.5 and 10362306a36Sopenharmony_ci * -stable), had an issue with the state machine 10462306a36Sopenharmony_ci * and wouldn't clear the STA_INS/DEL flag directly. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci tx.modes = ADJ_STATUS; 10762306a36Sopenharmony_ci tx.status = STA_PLL; 10862306a36Sopenharmony_ci ret = adjtimex(&tx); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Clear maxerror, as it can cause UNSYNC to be set */ 11162306a36Sopenharmony_ci tx.modes = ADJ_MAXERROR; 11262306a36Sopenharmony_ci tx.maxerror = 0; 11362306a36Sopenharmony_ci ret = adjtimex(&tx); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Clear the status */ 11662306a36Sopenharmony_ci tx.modes = ADJ_STATUS; 11762306a36Sopenharmony_ci tx.status = 0; 11862306a36Sopenharmony_ci ret = adjtimex(&tx); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Make sure we cleanup on ctrl-c */ 12462306a36Sopenharmony_civoid handler(int unused) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci clear_time_state(); 12762306a36Sopenharmony_ci exit(0); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid sigalarm(int signo) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct timex tx; 13362306a36Sopenharmony_ci int ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci tx.modes = 0; 13662306a36Sopenharmony_ci ret = adjtimex(&tx); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (tx.time.tv_sec < next_leap) { 13962306a36Sopenharmony_ci printf("Error: Early timer expiration! (Should be %ld)\n", next_leap); 14062306a36Sopenharmony_ci error_found = 1; 14162306a36Sopenharmony_ci printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n", 14262306a36Sopenharmony_ci tx.time.tv_sec, 14362306a36Sopenharmony_ci tx.time.tv_usec, 14462306a36Sopenharmony_ci tx.tai, 14562306a36Sopenharmony_ci time_state_str(ret)); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci if (ret != TIME_WAIT) { 14862306a36Sopenharmony_ci printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n"); 14962306a36Sopenharmony_ci error_found = 1; 15062306a36Sopenharmony_ci printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n", 15162306a36Sopenharmony_ci tx.time.tv_sec, 15262306a36Sopenharmony_ci tx.time.tv_usec, 15362306a36Sopenharmony_ci tx.tai, 15462306a36Sopenharmony_ci time_state_str(ret)); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Test for known hrtimer failure */ 16062306a36Sopenharmony_civoid test_hrtimer_failure(void) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct timespec now, target; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci clock_gettime(CLOCK_REALTIME, &now); 16562306a36Sopenharmony_ci target = timespec_add(now, NSEC_PER_SEC/2); 16662306a36Sopenharmony_ci clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL); 16762306a36Sopenharmony_ci clock_gettime(CLOCK_REALTIME, &now); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!in_order(target, now)) { 17062306a36Sopenharmony_ci printf("ERROR: hrtimer early expiration failure observed.\n"); 17162306a36Sopenharmony_ci error_found = 1; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciint main(int argc, char **argv) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci timer_t tm1; 17862306a36Sopenharmony_ci struct itimerspec its1; 17962306a36Sopenharmony_ci struct sigevent se; 18062306a36Sopenharmony_ci struct sigaction act; 18162306a36Sopenharmony_ci int signum = SIGRTMAX; 18262306a36Sopenharmony_ci int settime = 1; 18362306a36Sopenharmony_ci int tai_time = 0; 18462306a36Sopenharmony_ci int insert = 1; 18562306a36Sopenharmony_ci int iterations = 10; 18662306a36Sopenharmony_ci int opt; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Process arguments */ 18962306a36Sopenharmony_ci while ((opt = getopt(argc, argv, "sti:")) != -1) { 19062306a36Sopenharmony_ci switch (opt) { 19162306a36Sopenharmony_ci case 'w': 19262306a36Sopenharmony_ci printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n"); 19362306a36Sopenharmony_ci settime = 0; 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci case 'i': 19662306a36Sopenharmony_ci iterations = atoi(optarg); 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci case 't': 19962306a36Sopenharmony_ci tai_time = 1; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci default: 20262306a36Sopenharmony_ci printf("Usage: %s [-w] [-i <iterations>]\n", argv[0]); 20362306a36Sopenharmony_ci printf(" -w: Set flag and wait for leap second each iteration"); 20462306a36Sopenharmony_ci printf(" (default sets time to right before leapsecond)\n"); 20562306a36Sopenharmony_ci printf(" -i: Number of iterations (-1 = infinite, default is 10)\n"); 20662306a36Sopenharmony_ci printf(" -t: Print TAI time\n"); 20762306a36Sopenharmony_ci exit(-1); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Make sure TAI support is present if -t was used */ 21262306a36Sopenharmony_ci if (tai_time) { 21362306a36Sopenharmony_ci struct timespec ts; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (clock_gettime(CLOCK_TAI, &ts)) { 21662306a36Sopenharmony_ci printf("System doesn't support CLOCK_TAI\n"); 21762306a36Sopenharmony_ci ksft_exit_fail(); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci signal(SIGINT, handler); 22262306a36Sopenharmony_ci signal(SIGKILL, handler); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Set up timer signal handler: */ 22562306a36Sopenharmony_ci sigfillset(&act.sa_mask); 22662306a36Sopenharmony_ci act.sa_flags = 0; 22762306a36Sopenharmony_ci act.sa_handler = sigalarm; 22862306a36Sopenharmony_ci sigaction(signum, &act, NULL); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (iterations < 0) 23162306a36Sopenharmony_ci printf("This runs continuously. Press ctrl-c to stop\n"); 23262306a36Sopenharmony_ci else 23362306a36Sopenharmony_ci printf("Running for %i iterations. Press ctrl-c to stop\n", iterations); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci printf("\n"); 23662306a36Sopenharmony_ci while (1) { 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci struct timespec ts; 23962306a36Sopenharmony_ci struct timex tx; 24062306a36Sopenharmony_ci time_t now; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Get the current time */ 24362306a36Sopenharmony_ci clock_gettime(CLOCK_REALTIME, &ts); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Calculate the next possible leap second 23:59:60 GMT */ 24662306a36Sopenharmony_ci next_leap = ts.tv_sec; 24762306a36Sopenharmony_ci next_leap += 86400 - (next_leap % 86400); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (settime) { 25062306a36Sopenharmony_ci struct timeval tv; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci tv.tv_sec = next_leap - 10; 25362306a36Sopenharmony_ci tv.tv_usec = 0; 25462306a36Sopenharmony_ci settimeofday(&tv, NULL); 25562306a36Sopenharmony_ci printf("Setting time to %s", ctime(&tv.tv_sec)); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Reset NTP time state */ 25962306a36Sopenharmony_ci clear_time_state(); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Set the leap second insert flag */ 26262306a36Sopenharmony_ci tx.modes = ADJ_STATUS; 26362306a36Sopenharmony_ci if (insert) 26462306a36Sopenharmony_ci tx.status = STA_INS; 26562306a36Sopenharmony_ci else 26662306a36Sopenharmony_ci tx.status = STA_DEL; 26762306a36Sopenharmony_ci ret = adjtimex(&tx); 26862306a36Sopenharmony_ci if (ret < 0) { 26962306a36Sopenharmony_ci printf("Error: Problem setting STA_INS/STA_DEL!: %s\n", 27062306a36Sopenharmony_ci time_state_str(ret)); 27162306a36Sopenharmony_ci return ksft_exit_fail(); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Validate STA_INS was set */ 27562306a36Sopenharmony_ci tx.modes = 0; 27662306a36Sopenharmony_ci ret = adjtimex(&tx); 27762306a36Sopenharmony_ci if (tx.status != STA_INS && tx.status != STA_DEL) { 27862306a36Sopenharmony_ci printf("Error: STA_INS/STA_DEL not set!: %s\n", 27962306a36Sopenharmony_ci time_state_str(ret)); 28062306a36Sopenharmony_ci return ksft_exit_fail(); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (tai_time) { 28462306a36Sopenharmony_ci printf("Using TAI time," 28562306a36Sopenharmony_ci " no inconsistencies should be seen!\n"); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci printf("Scheduling leap second for %s", ctime(&next_leap)); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Set up timer */ 29162306a36Sopenharmony_ci printf("Setting timer for %ld - %s", next_leap, ctime(&next_leap)); 29262306a36Sopenharmony_ci memset(&se, 0, sizeof(se)); 29362306a36Sopenharmony_ci se.sigev_notify = SIGEV_SIGNAL; 29462306a36Sopenharmony_ci se.sigev_signo = signum; 29562306a36Sopenharmony_ci se.sigev_value.sival_int = 0; 29662306a36Sopenharmony_ci if (timer_create(CLOCK_REALTIME, &se, &tm1) == -1) { 29762306a36Sopenharmony_ci printf("Error: timer_create failed\n"); 29862306a36Sopenharmony_ci return ksft_exit_fail(); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci its1.it_value.tv_sec = next_leap; 30162306a36Sopenharmony_ci its1.it_value.tv_nsec = 0; 30262306a36Sopenharmony_ci its1.it_interval.tv_sec = 0; 30362306a36Sopenharmony_ci its1.it_interval.tv_nsec = 0; 30462306a36Sopenharmony_ci timer_settime(tm1, TIMER_ABSTIME, &its1, NULL); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Wake up 3 seconds before leap */ 30762306a36Sopenharmony_ci ts.tv_sec = next_leap - 3; 30862306a36Sopenharmony_ci ts.tv_nsec = 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL)) 31262306a36Sopenharmony_ci printf("Something woke us up, returning to sleep\n"); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Validate STA_INS is still set */ 31562306a36Sopenharmony_ci tx.modes = 0; 31662306a36Sopenharmony_ci ret = adjtimex(&tx); 31762306a36Sopenharmony_ci if (tx.status != STA_INS && tx.status != STA_DEL) { 31862306a36Sopenharmony_ci printf("Something cleared STA_INS/STA_DEL, setting it again.\n"); 31962306a36Sopenharmony_ci tx.modes = ADJ_STATUS; 32062306a36Sopenharmony_ci if (insert) 32162306a36Sopenharmony_ci tx.status = STA_INS; 32262306a36Sopenharmony_ci else 32362306a36Sopenharmony_ci tx.status = STA_DEL; 32462306a36Sopenharmony_ci ret = adjtimex(&tx); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Check adjtimex output every half second */ 32862306a36Sopenharmony_ci now = tx.time.tv_sec; 32962306a36Sopenharmony_ci while (now < next_leap + 2) { 33062306a36Sopenharmony_ci char buf[26]; 33162306a36Sopenharmony_ci struct timespec tai; 33262306a36Sopenharmony_ci int ret; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci tx.modes = 0; 33562306a36Sopenharmony_ci ret = adjtimex(&tx); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (tai_time) { 33862306a36Sopenharmony_ci clock_gettime(CLOCK_TAI, &tai); 33962306a36Sopenharmony_ci printf("%ld sec, %9ld ns\t%s\n", 34062306a36Sopenharmony_ci tai.tv_sec, 34162306a36Sopenharmony_ci tai.tv_nsec, 34262306a36Sopenharmony_ci time_state_str(ret)); 34362306a36Sopenharmony_ci } else { 34462306a36Sopenharmony_ci ctime_r(&tx.time.tv_sec, buf); 34562306a36Sopenharmony_ci buf[strlen(buf)-1] = 0; /*remove trailing\n */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci printf("%s + %6ld us (%i)\t%s\n", 34862306a36Sopenharmony_ci buf, 34962306a36Sopenharmony_ci tx.time.tv_usec, 35062306a36Sopenharmony_ci tx.tai, 35162306a36Sopenharmony_ci time_state_str(ret)); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci now = tx.time.tv_sec; 35462306a36Sopenharmony_ci /* Sleep for another half second */ 35562306a36Sopenharmony_ci ts.tv_sec = 0; 35662306a36Sopenharmony_ci ts.tv_nsec = NSEC_PER_SEC / 2; 35762306a36Sopenharmony_ci clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci /* Switch to using other mode */ 36062306a36Sopenharmony_ci insert = !insert; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Note if kernel has known hrtimer failure */ 36362306a36Sopenharmony_ci test_hrtimer_failure(); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci printf("Leap complete\n"); 36662306a36Sopenharmony_ci if (error_found) { 36762306a36Sopenharmony_ci printf("Errors observed\n"); 36862306a36Sopenharmony_ci clear_time_state(); 36962306a36Sopenharmony_ci return ksft_exit_fail(); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci printf("\n"); 37262306a36Sopenharmony_ci if ((iterations != -1) && !(--iterations)) 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci clear_time_state(); 37762306a36Sopenharmony_ci return ksft_exit_pass(); 37862306a36Sopenharmony_ci} 379