1/* 2 * Regression test for hrtimer early expiration during and after leap seconds 3 * 4 * A bug in the hrtimer subsystem caused all TIMER_ABSTIME CLOCK_REALTIME 5 * timers to expire one second early during leap second. 6 * See http://lwn.net/Articles/504658/. 7 * 8 * This is a regression test for the bug. 9 * 10 * Lingzhu Xiang <lxiang@redhat.com> Copyright (c) Red Hat, Inc., 2012. 11 * 12 * This program is free software; you can redistribute it and/or modify it 13 * under the terms of version 2 of the GNU General Public License as 14 * published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it would be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * You should have received a copy of the GNU General Public License along 21 * with this program; if not, write the Free Software Foundation, Inc., 22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 * 24 */ 25 26#include <sys/types.h> 27#include <sys/time.h> 28#include <sys/timex.h> 29#include <errno.h> 30#include <stdlib.h> 31#include <time.h> 32#include "test.h" 33#include "common_timers.h" 34 35#define SECONDS_BEFORE_LEAP 2 36#define SECONDS_AFTER_LEAP 2 37 38char *TCID = "leapsec_timer"; 39int TST_TOTAL = 1; 40 41static inline int in_order(struct timespec a, struct timespec b); 42static void adjtimex_status(struct timex *tx, int status); 43static const char *strtime(const struct timespec *now); 44static void test_hrtimer_early_expiration(void); 45static void run_leapsec(void); 46static void setup(void); 47static void cleanup(void); 48 49int main(int argc, char **argv) 50{ 51 int lc; 52 53 tst_parse_opts(argc, argv, NULL, NULL); 54 55 setup(); 56 57 for (lc = 0; TEST_LOOPING(lc); lc++) { 58 tst_count = 0; 59 run_leapsec(); 60 } 61 62 cleanup(); 63 tst_exit(); 64} 65 66static inline int in_order(struct timespec a, struct timespec b) 67{ 68 if (a.tv_sec < b.tv_sec) 69 return 1; 70 if (a.tv_sec > b.tv_sec) 71 return 0; 72 if (a.tv_nsec > b.tv_nsec) 73 return 0; 74 return 1; 75} 76 77static void adjtimex_status(struct timex *tx, int status) 78{ 79 const char *const msgs[6] = { 80 "clock synchronized", 81 "insert leap second", 82 "delete leap second", 83 "leap second in progress", 84 "leap second has occurred", 85 "clock not synchronized", 86 }; 87 int r; 88 struct timespec now; 89 90 tx->modes = ADJ_STATUS; 91 tx->status = status; 92 r = adjtimex(tx); 93 now.tv_sec = tx->time.tv_sec; 94 now.tv_nsec = tx->time.tv_usec * 1000; 95 96 if ((tx->status & status) != status) 97 tst_brkm(TBROK, cleanup, "adjtimex status %d not set", status); 98 else if (r < 0) 99 tst_brkm(TBROK | TERRNO, cleanup, "adjtimex"); 100 else if (r < 6) 101 tst_resm(TINFO, "%s adjtimex: %s", strtime(&now), msgs[r]); 102 else 103 tst_resm(TINFO, "%s adjtimex: clock state %d", 104 strtime(&now), r); 105} 106 107static const char *strtime(const struct timespec *now) 108{ 109 static char fmt[256], buf[256]; 110 111 if (snprintf(fmt, sizeof(fmt), "%%F %%T.%09ld %%z", now->tv_nsec) < 0) { 112 buf[0] = '\0'; 113 return buf; 114 } 115 if (!strftime(buf, sizeof(buf), fmt, localtime(&now->tv_sec))) { 116 buf[0] = '\0'; 117 return buf; 118 } 119 return buf; 120} 121 122static void test_hrtimer_early_expiration(void) 123{ 124 struct timespec now, target; 125 int r, fail; 126 127 clock_gettime(CLOCK_REALTIME, &now); 128 tst_resm(TINFO, "now is %s", strtime(&now)); 129 130 target = now; 131 target.tv_sec++; 132 tst_resm(TINFO, "sleep till %s", strtime(&target)); 133 r = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL); 134 if (r < 0) { 135 tst_resm(TINFO | TERRNO, "clock_nanosleep"); 136 return; 137 } 138 139 clock_gettime(CLOCK_REALTIME, &now); 140 tst_resm(TINFO, "now is %s", strtime(&now)); 141 142 fail = !in_order(target, now); 143 tst_resm(fail ? TFAIL : TINFO, "hrtimer early expiration is %s.", 144 fail ? "detected" : "not detected"); 145} 146 147static void run_leapsec(void) 148{ 149 const struct timespec sleeptime = { 0, NSEC_PER_SEC / 2 }; 150 struct timespec now, leap, start; 151 struct timex tx; 152 153 clock_gettime(CLOCK_REALTIME, &now); 154 start = now; 155 tst_resm(TINFO, "test start at %s", strtime(&now)); 156 157 test_hrtimer_early_expiration(); 158 159 /* calculate the next leap second */ 160 now.tv_sec += 86400 - now.tv_sec % 86400; 161 now.tv_nsec = 0; 162 leap = now; 163 tst_resm(TINFO, "scheduling leap second %s", strtime(&leap)); 164 165 /* start before the leap second */ 166 now.tv_sec -= SECONDS_BEFORE_LEAP; 167 if (clock_settime(CLOCK_REALTIME, &now) < 0) 168 tst_brkm(TBROK | TERRNO, cleanup, "clock_settime"); 169 tst_resm(TINFO, "setting time to %s", strtime(&now)); 170 171 /* reset NTP time state */ 172 adjtimex_status(&tx, STA_PLL); 173 adjtimex_status(&tx, 0); 174 175 /* set the leap second insert flag */ 176 adjtimex_status(&tx, STA_INS); 177 178 /* reliably sleep till after the leap second */ 179 while (tx.time.tv_sec < leap.tv_sec + SECONDS_AFTER_LEAP) { 180 adjtimex_status(&tx, tx.status); 181 clock_nanosleep(CLOCK_MONOTONIC, 0, &sleeptime, NULL); 182 } 183 184 test_hrtimer_early_expiration(); 185 186 adjtimex_status(&tx, STA_PLL); 187 adjtimex_status(&tx, 0); 188 189 /* recover from timer expiring state and restore time */ 190 clock_gettime(CLOCK_REALTIME, &now); 191 start.tv_sec += now.tv_sec - (leap.tv_sec - SECONDS_BEFORE_LEAP); 192 start.tv_nsec += now.tv_nsec; 193 start.tv_sec += start.tv_nsec / NSEC_PER_SEC; 194 start.tv_nsec = start.tv_nsec % NSEC_PER_SEC; 195 tst_resm(TINFO, "restoring time to %s", strtime(&start)); 196 /* calls clock_was_set() in kernel to revert inconsistency */ 197 if (clock_settime(CLOCK_REALTIME, &start) < 0) 198 tst_brkm(TBROK | TERRNO, cleanup, "clock_settime"); 199 200 test_hrtimer_early_expiration(); 201} 202 203static void setup(void) 204{ 205 tst_require_root(); 206 tst_sig(NOFORK, DEF_HANDLER, CLEANUP); 207 TEST_PAUSE; 208} 209 210static void cleanup(void) 211{ 212 struct timespec now; 213 clock_gettime(CLOCK_REALTIME, &now); 214 /* Calls clock_was_set() in kernel to revert inconsistency. 215 * The only possible EPERM doesn't matter here. */ 216 clock_settime(CLOCK_REALTIME, &now); 217} 218