blob: be9675ded5f420373645188f02b2e323cfd636da [file] [log] [blame]
Lingzhu Xiang03c739d2012-07-08 08:09:53 +08001/*
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
Wanlong Gaofed96412012-10-24 10:10:29 +080021 * with this program; if not, write the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Lingzhu Xiang03c739d2012-07-08 08:09:53 +080023 *
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"
Lingzhu Xiang03c739d2012-07-08 08:09:53 +080033#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{
Cyril Hrubis0b9589f2014-05-27 17:40:33 +020051 const char *msg;
Lingzhu Xiang03c739d2012-07-08 08:09:53 +080052 int lc;
53
54 msg = parse_opts(argc, argv, NULL, NULL);
55 if (msg != NULL)
56 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
57
58 setup();
59
60 for (lc = 0; TEST_LOOPING(lc); lc++) {
Caspar Zhangd59a6592013-03-07 14:59:12 +080061 tst_count = 0;
Lingzhu Xiang03c739d2012-07-08 08:09:53 +080062 run_leapsec();
63 }
64
65 cleanup();
66 tst_exit();
67}
68
69static inline int in_order(struct timespec a, struct timespec b)
70{
71 if (a.tv_sec < b.tv_sec)
72 return 1;
73 if (a.tv_sec > b.tv_sec)
74 return 0;
75 if (a.tv_nsec > b.tv_nsec)
76 return 0;
77 return 1;
78}
79
80static void adjtimex_status(struct timex *tx, int status)
81{
Wanlong Gao354ebb42012-12-07 10:10:04 +080082 const char *const msgs[6] = {
Lingzhu Xiang03c739d2012-07-08 08:09:53 +080083 "clock synchronized",
84 "insert leap second",
85 "delete leap second",
86 "leap second in progress",
87 "leap second has occurred",
88 "clock not synchronized",
89 };
90 int r;
91 struct timespec now;
92
93 tx->modes = ADJ_STATUS;
94 tx->status = status;
95 r = adjtimex(tx);
96 now.tv_sec = tx->time.tv_sec;
97 now.tv_nsec = tx->time.tv_usec * 1000;
98
99 if ((tx->status & status) != status)
100 tst_brkm(TBROK, cleanup, "adjtimex status %d not set", status);
101 else if (r < 0)
102 tst_brkm(TBROK | TERRNO, cleanup, "adjtimex");
103 else if (r < 6)
104 tst_resm(TINFO, "%s adjtimex: %s", strtime(&now), msgs[r]);
105 else
106 tst_resm(TINFO, "%s adjtimex: clock state %d",
Wanlong Gao354ebb42012-12-07 10:10:04 +0800107 strtime(&now), r);
Lingzhu Xiang03c739d2012-07-08 08:09:53 +0800108}
109
110static const char *strtime(const struct timespec *now)
111{
112 static char fmt[256], buf[256];
113
114 if (snprintf(fmt, sizeof(fmt), "%%F %%T.%09ld %%z", now->tv_nsec) < 0) {
115 buf[0] = '\0';
116 return buf;
117 }
118 if (!strftime(buf, sizeof(buf), fmt, localtime(&now->tv_sec))) {
119 buf[0] = '\0';
120 return buf;
121 }
122 return buf;
123}
124
125static void test_hrtimer_early_expiration(void)
126{
127 struct timespec now, target;
128 int r, fail;
129
130 clock_gettime(CLOCK_REALTIME, &now);
131 tst_resm(TINFO, "now is %s", strtime(&now));
132
133 target = now;
134 target.tv_sec++;
135 tst_resm(TINFO, "sleep till %s", strtime(&target));
136 r = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);
137 if (r < 0) {
138 tst_resm(TINFO | TERRNO, "clock_nanosleep");
139 return;
140 }
141
142 clock_gettime(CLOCK_REALTIME, &now);
143 tst_resm(TINFO, "now is %s", strtime(&now));
144
145 fail = !in_order(target, now);
146 tst_resm(fail ? TFAIL : TINFO, "hrtimer early expiration is %s.",
Wanlong Gao354ebb42012-12-07 10:10:04 +0800147 fail ? "detected" : "not detected");
Lingzhu Xiang03c739d2012-07-08 08:09:53 +0800148}
149
150static void run_leapsec(void)
151{
Wanlong Gao354ebb42012-12-07 10:10:04 +0800152 const struct timespec sleeptime = { 0, NSEC_PER_SEC / 2 };
Lingzhu Xiang03c739d2012-07-08 08:09:53 +0800153 struct timespec now, leap, start;
154 struct timex tx;
155
156 clock_gettime(CLOCK_REALTIME, &now);
157 start = now;
158 tst_resm(TINFO, "test start at %s", strtime(&now));
159
160 test_hrtimer_early_expiration();
161
162 /* calculate the next leap second */
163 now.tv_sec += 86400 - now.tv_sec % 86400;
164 now.tv_nsec = 0;
165 leap = now;
166 tst_resm(TINFO, "scheduling leap second %s", strtime(&leap));
167
168 /* start before the leap second */
169 now.tv_sec -= SECONDS_BEFORE_LEAP;
170 if (clock_settime(CLOCK_REALTIME, &now) < 0)
171 tst_brkm(TBROK | TERRNO, cleanup, "clock_settime");
172 tst_resm(TINFO, "setting time to %s", strtime(&now));
173
174 /* reset NTP time state */
175 adjtimex_status(&tx, STA_PLL);
176 adjtimex_status(&tx, 0);
177
178 /* set the leap second insert flag */
179 adjtimex_status(&tx, STA_INS);
180
181 /* reliably sleep till after the leap second */
182 while (tx.time.tv_sec < leap.tv_sec + SECONDS_AFTER_LEAP) {
183 adjtimex_status(&tx, tx.status);
184 clock_nanosleep(CLOCK_MONOTONIC, 0, &sleeptime, NULL);
185 }
186
187 test_hrtimer_early_expiration();
188
189 adjtimex_status(&tx, STA_PLL);
190 adjtimex_status(&tx, 0);
191
192 /* recover from timer expiring state and restore time */
193 clock_gettime(CLOCK_REALTIME, &now);
194 start.tv_sec += now.tv_sec - (leap.tv_sec - SECONDS_BEFORE_LEAP);
195 start.tv_nsec += now.tv_nsec;
196 start.tv_sec += start.tv_nsec / NSEC_PER_SEC;
197 start.tv_nsec = start.tv_nsec % NSEC_PER_SEC;
198 tst_resm(TINFO, "restoring time to %s", strtime(&start));
199 /* calls clock_was_set() in kernel to revert inconsistency */
200 if (clock_settime(CLOCK_REALTIME, &start) < 0)
201 tst_brkm(TBROK | TERRNO, cleanup, "clock_settime");
202
203 test_hrtimer_early_expiration();
204}
205
206static void setup(void)
207{
208 tst_require_root(NULL);
209 tst_sig(NOFORK, DEF_HANDLER, CLEANUP);
210 TEST_PAUSE;
211}
212
213static void cleanup(void)
214{
215 struct timespec now;
216 clock_gettime(CLOCK_REALTIME, &now);
217 /* Calls clock_was_set() in kernel to revert inconsistency.
218 * The only possible EPERM doesn't matter here. */
219 clock_settime(CLOCK_REALTIME, &now);
Lingzhu Xiang03c739d2012-07-08 08:09:53 +0800220}