Back to home page

OSCL-LXR

 
 

    


0001 /* Leap second stress test
0002  *              by: John Stultz (john.stultz@linaro.org)
0003  *              (C) Copyright IBM 2012
0004  *              (C) Copyright 2013, 2015 Linaro Limited
0005  *              Licensed under the GPLv2
0006  *
0007  *  This test signals the kernel to insert a leap second
0008  *  every day at midnight GMT. This allows for stressing the
0009  *  kernel's leap-second behavior, as well as how well applications
0010  *  handle the leap-second discontinuity.
0011  *
0012  *  Usage: leap-a-day [-s] [-i <num>]
0013  *
0014  *  Options:
0015  *  -s: Each iteration, set the date to 10 seconds before midnight GMT.
0016  *      This speeds up the number of leapsecond transitions tested,
0017  *      but because it calls settimeofday frequently, advancing the
0018  *      time by 24 hours every ~16 seconds, it may cause application
0019  *      disruption.
0020  *
0021  *  -i: Number of iterations to run (default: infinite)
0022  *
0023  *  Other notes: Disabling NTP prior to running this is advised, as the two
0024  *       may conflict in their commands to the kernel.
0025  *
0026  *  To build:
0027  *  $ gcc leap-a-day.c -o leap-a-day -lrt
0028  *
0029  *   This program is free software: you can redistribute it and/or modify
0030  *   it under the terms of the GNU General Public License as published by
0031  *   the Free Software Foundation, either version 2 of the License, or
0032  *   (at your option) any later version.
0033  *
0034  *   This program is distributed in the hope that it will be useful,
0035  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0036  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0037  *   GNU General Public License for more details.
0038  */
0039 
0040 
0041 
0042 #include <stdio.h>
0043 #include <stdlib.h>
0044 #include <time.h>
0045 #include <sys/time.h>
0046 #include <sys/timex.h>
0047 #include <sys/errno.h>
0048 #include <string.h>
0049 #include <signal.h>
0050 #include <unistd.h>
0051 #include "../kselftest.h"
0052 
0053 #define NSEC_PER_SEC 1000000000ULL
0054 #define CLOCK_TAI 11
0055 
0056 time_t next_leap;
0057 int error_found;
0058 
0059 /* returns 1 if a <= b, 0 otherwise */
0060 static inline int in_order(struct timespec a, struct timespec b)
0061 {
0062     if (a.tv_sec < b.tv_sec)
0063         return 1;
0064     if (a.tv_sec > b.tv_sec)
0065         return 0;
0066     if (a.tv_nsec > b.tv_nsec)
0067         return 0;
0068     return 1;
0069 }
0070 
0071 struct timespec timespec_add(struct timespec ts, unsigned long long ns)
0072 {
0073     ts.tv_nsec += ns;
0074     while (ts.tv_nsec >= NSEC_PER_SEC) {
0075         ts.tv_nsec -= NSEC_PER_SEC;
0076         ts.tv_sec++;
0077     }
0078     return ts;
0079 }
0080 
0081 char *time_state_str(int state)
0082 {
0083     switch (state) {
0084     case TIME_OK:   return "TIME_OK";
0085     case TIME_INS:  return "TIME_INS";
0086     case TIME_DEL:  return "TIME_DEL";
0087     case TIME_OOP:  return "TIME_OOP";
0088     case TIME_WAIT: return "TIME_WAIT";
0089     case TIME_BAD:  return "TIME_BAD";
0090     }
0091     return "ERROR";
0092 }
0093 
0094 /* clear NTP time_status & time_state */
0095 int clear_time_state(void)
0096 {
0097     struct timex tx;
0098     int ret;
0099 
0100     /*
0101      * We have to call adjtime twice here, as kernels
0102      * prior to 6b1859dba01c7 (included in 3.5 and
0103      * -stable), had an issue with the state machine
0104      * and wouldn't clear the STA_INS/DEL flag directly.
0105      */
0106     tx.modes = ADJ_STATUS;
0107     tx.status = STA_PLL;
0108     ret = adjtimex(&tx);
0109 
0110     /* Clear maxerror, as it can cause UNSYNC to be set */
0111     tx.modes = ADJ_MAXERROR;
0112     tx.maxerror = 0;
0113     ret = adjtimex(&tx);
0114 
0115     /* Clear the status */
0116     tx.modes = ADJ_STATUS;
0117     tx.status = 0;
0118     ret = adjtimex(&tx);
0119 
0120     return ret;
0121 }
0122 
0123 /* Make sure we cleanup on ctrl-c */
0124 void handler(int unused)
0125 {
0126     clear_time_state();
0127     exit(0);
0128 }
0129 
0130 void sigalarm(int signo)
0131 {
0132     struct timex tx;
0133     int ret;
0134 
0135     tx.modes = 0;
0136     ret = adjtimex(&tx);
0137 
0138     if (tx.time.tv_sec < next_leap) {
0139         printf("Error: Early timer expiration! (Should be %ld)\n", next_leap);
0140         error_found = 1;
0141         printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
0142                     tx.time.tv_sec,
0143                     tx.time.tv_usec,
0144                     tx.tai,
0145                     time_state_str(ret));
0146     }
0147     if (ret != TIME_WAIT) {
0148         printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n");
0149         error_found = 1;
0150         printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
0151                     tx.time.tv_sec,
0152                     tx.time.tv_usec,
0153                     tx.tai,
0154                     time_state_str(ret));
0155     }
0156 }
0157 
0158 
0159 /* Test for known hrtimer failure */
0160 void test_hrtimer_failure(void)
0161 {
0162     struct timespec now, target;
0163 
0164     clock_gettime(CLOCK_REALTIME, &now);
0165     target = timespec_add(now, NSEC_PER_SEC/2);
0166     clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);
0167     clock_gettime(CLOCK_REALTIME, &now);
0168 
0169     if (!in_order(target, now)) {
0170         printf("ERROR: hrtimer early expiration failure observed.\n");
0171         error_found = 1;
0172     }
0173 }
0174 
0175 int main(int argc, char **argv)
0176 {
0177     timer_t tm1;
0178     struct itimerspec its1;
0179     struct sigevent se;
0180     struct sigaction act;
0181     int signum = SIGRTMAX;
0182     int settime = 1;
0183     int tai_time = 0;
0184     int insert = 1;
0185     int iterations = 10;
0186     int opt;
0187 
0188     /* Process arguments */
0189     while ((opt = getopt(argc, argv, "sti:")) != -1) {
0190         switch (opt) {
0191         case 'w':
0192             printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n");
0193             settime = 0;
0194             break;
0195         case 'i':
0196             iterations = atoi(optarg);
0197             break;
0198         case 't':
0199             tai_time = 1;
0200             break;
0201         default:
0202             printf("Usage: %s [-w] [-i <iterations>]\n", argv[0]);
0203             printf("    -w: Set flag and wait for leap second each iteration");
0204             printf("        (default sets time to right before leapsecond)\n");
0205             printf("    -i: Number of iterations (-1 = infinite, default is 10)\n");
0206             printf("    -t: Print TAI time\n");
0207             exit(-1);
0208         }
0209     }
0210 
0211     /* Make sure TAI support is present if -t was used */
0212     if (tai_time) {
0213         struct timespec ts;
0214 
0215         if (clock_gettime(CLOCK_TAI, &ts)) {
0216             printf("System doesn't support CLOCK_TAI\n");
0217             ksft_exit_fail();
0218         }
0219     }
0220 
0221     signal(SIGINT, handler);
0222     signal(SIGKILL, handler);
0223 
0224     /* Set up timer signal handler: */
0225     sigfillset(&act.sa_mask);
0226     act.sa_flags = 0;
0227     act.sa_handler = sigalarm;
0228     sigaction(signum, &act, NULL);
0229 
0230     if (iterations < 0)
0231         printf("This runs continuously. Press ctrl-c to stop\n");
0232     else
0233         printf("Running for %i iterations. Press ctrl-c to stop\n", iterations);
0234 
0235     printf("\n");
0236     while (1) {
0237         int ret;
0238         struct timespec ts;
0239         struct timex tx;
0240         time_t now;
0241 
0242         /* Get the current time */
0243         clock_gettime(CLOCK_REALTIME, &ts);
0244 
0245         /* Calculate the next possible leap second 23:59:60 GMT */
0246         next_leap = ts.tv_sec;
0247         next_leap += 86400 - (next_leap % 86400);
0248 
0249         if (settime) {
0250             struct timeval tv;
0251 
0252             tv.tv_sec = next_leap - 10;
0253             tv.tv_usec = 0;
0254             settimeofday(&tv, NULL);
0255             printf("Setting time to %s", ctime(&tv.tv_sec));
0256         }
0257 
0258         /* Reset NTP time state */
0259         clear_time_state();
0260 
0261         /* Set the leap second insert flag */
0262         tx.modes = ADJ_STATUS;
0263         if (insert)
0264             tx.status = STA_INS;
0265         else
0266             tx.status = STA_DEL;
0267         ret = adjtimex(&tx);
0268         if (ret < 0) {
0269             printf("Error: Problem setting STA_INS/STA_DEL!: %s\n",
0270                             time_state_str(ret));
0271             return ksft_exit_fail();
0272         }
0273 
0274         /* Validate STA_INS was set */
0275         tx.modes = 0;
0276         ret = adjtimex(&tx);
0277         if (tx.status != STA_INS && tx.status != STA_DEL) {
0278             printf("Error: STA_INS/STA_DEL not set!: %s\n",
0279                             time_state_str(ret));
0280             return ksft_exit_fail();
0281         }
0282 
0283         if (tai_time) {
0284             printf("Using TAI time,"
0285                 " no inconsistencies should be seen!\n");
0286         }
0287 
0288         printf("Scheduling leap second for %s", ctime(&next_leap));
0289 
0290         /* Set up timer */
0291         printf("Setting timer for %ld -  %s", next_leap, ctime(&next_leap));
0292         memset(&se, 0, sizeof(se));
0293         se.sigev_notify = SIGEV_SIGNAL;
0294         se.sigev_signo = signum;
0295         se.sigev_value.sival_int = 0;
0296         if (timer_create(CLOCK_REALTIME, &se, &tm1) == -1) {
0297             printf("Error: timer_create failed\n");
0298             return ksft_exit_fail();
0299         }
0300         its1.it_value.tv_sec = next_leap;
0301         its1.it_value.tv_nsec = 0;
0302         its1.it_interval.tv_sec = 0;
0303         its1.it_interval.tv_nsec = 0;
0304         timer_settime(tm1, TIMER_ABSTIME, &its1, NULL);
0305 
0306         /* Wake up 3 seconds before leap */
0307         ts.tv_sec = next_leap - 3;
0308         ts.tv_nsec = 0;
0309 
0310 
0311         while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL))
0312             printf("Something woke us up, returning to sleep\n");
0313 
0314         /* Validate STA_INS is still set */
0315         tx.modes = 0;
0316         ret = adjtimex(&tx);
0317         if (tx.status != STA_INS && tx.status != STA_DEL) {
0318             printf("Something cleared STA_INS/STA_DEL, setting it again.\n");
0319             tx.modes = ADJ_STATUS;
0320             if (insert)
0321                 tx.status = STA_INS;
0322             else
0323                 tx.status = STA_DEL;
0324             ret = adjtimex(&tx);
0325         }
0326 
0327         /* Check adjtimex output every half second */
0328         now = tx.time.tv_sec;
0329         while (now < next_leap + 2) {
0330             char buf[26];
0331             struct timespec tai;
0332             int ret;
0333 
0334             tx.modes = 0;
0335             ret = adjtimex(&tx);
0336 
0337             if (tai_time) {
0338                 clock_gettime(CLOCK_TAI, &tai);
0339                 printf("%ld sec, %9ld ns\t%s\n",
0340                         tai.tv_sec,
0341                         tai.tv_nsec,
0342                         time_state_str(ret));
0343             } else {
0344                 ctime_r(&tx.time.tv_sec, buf);
0345                 buf[strlen(buf)-1] = 0; /*remove trailing\n */
0346 
0347                 printf("%s + %6ld us (%i)\t%s\n",
0348                         buf,
0349                         tx.time.tv_usec,
0350                         tx.tai,
0351                         time_state_str(ret));
0352             }
0353             now = tx.time.tv_sec;
0354             /* Sleep for another half second */
0355             ts.tv_sec = 0;
0356             ts.tv_nsec = NSEC_PER_SEC / 2;
0357             clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
0358         }
0359         /* Switch to using other mode */
0360         insert = !insert;
0361 
0362         /* Note if kernel has known hrtimer failure */
0363         test_hrtimer_failure();
0364 
0365         printf("Leap complete\n");
0366         if (error_found) {
0367             printf("Errors observed\n");
0368             clear_time_state();
0369             return ksft_exit_fail();
0370         }
0371         printf("\n");
0372         if ((iterations != -1) && !(--iterations))
0373             break;
0374     }
0375 
0376     clear_time_state();
0377     return ksft_exit_pass();
0378 }