• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) Red Hat, Inc., 2012.
4  * Copyright (c) Linux Test Project, 2019
5  *
6  * Author:	Lingzhu Xiang <lxiang@redhat.com>
7  *
8  * Ported to new library:
9  * 07/2019	Christian Amann <camann@suse.com>
10  */
11 /*
12  * Regression test for hrtimer early expiration during and after leap seconds
13  *
14  * A bug in the hrtimer subsystem caused all TIMER_ABSTIME CLOCK_REALTIME
15  * timers to expire one second early during leap second.
16  * See http://lwn.net/Articles/504658/.
17  *
18  * This is a regression test for the bug.
19  *
20  * Note: running this test simultaneously with a timesync daemon
21  * (e.g. systemd-timesyncd) can cause this test to fail.
22  */
23 
24 #include <sys/types.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <time.h>
28 #include "tst_test.h"
29 #include "tst_safe_clocks.h"
30 #include "lapi/common_timers.h"
31 
32 #define SECONDS_BEFORE_LEAP 2
33 #define SECONDS_AFTER_LEAP 2
34 
35 static int errors;
36 
strtime(const struct timespec * now)37 static const char *strtime(const struct timespec *now)
38 {
39 	static char fmt[256], buf[256];
40 
41 	if (snprintf(fmt, sizeof(fmt), "%%T.%09ld", now->tv_nsec) < 0) {
42 		buf[0] = '\0';
43 		return buf;
44 	}
45 	if (!strftime(buf, sizeof(buf), fmt, localtime(&now->tv_sec))) {
46 		buf[0] = '\0';
47 		return buf;
48 	}
49 	return buf;
50 }
51 
in_order(struct timespec a,struct timespec b)52 static inline int in_order(struct timespec a, struct timespec b)
53 {
54 	if (a.tv_sec < b.tv_sec)
55 		return 1;
56 	if (a.tv_sec > b.tv_sec)
57 		return 0;
58 	if (a.tv_nsec > b.tv_nsec)
59 		return 0;
60 	return 1;
61 }
62 
adjtimex_status(struct timex * tx,int status)63 static void adjtimex_status(struct timex *tx, int status)
64 {
65 	const char *const msgs[6] = {
66 		"clock synchronized",
67 		"insert leap second",
68 		"delete leap second",
69 		"leap second in progress",
70 		"leap second has occurred",
71 		"clock not synchronized",
72 	};
73 
74 	int ret;
75 	struct timespec now;
76 
77 	tx->modes = ADJ_STATUS;
78 	tx->status = status;
79 	ret = adjtimex(tx);
80 	now.tv_sec = tx->time.tv_sec;
81 	now.tv_nsec = tx->time.tv_usec * 1000;
82 
83 	if ((tx->status & status) != status)
84 		tst_brk(TBROK, "adjtimex status %d not set", status);
85 	else if (ret < 0)
86 		tst_brk(TBROK | TERRNO, "adjtimex");
87 	else if (ret < 6)
88 		tst_res(TINFO, "%s adjtimex: %s", strtime(&now), msgs[ret]);
89 	else
90 		tst_res(TINFO, "%s adjtimex: clock state %d",
91 			 strtime(&now), ret);
92 }
93 
test_hrtimer_early_expiration(void)94 static void test_hrtimer_early_expiration(void)
95 {
96 	struct timespec now, target;
97 	int ret;
98 
99 	SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &now);
100 	tst_res(TINFO, "now is     %s", strtime(&now));
101 
102 	target = now;
103 	target.tv_sec++;
104 	tst_res(TINFO, "sleep until %s", strtime(&target));
105 	ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);
106 	if (ret < 0) {
107 		tst_res(TINFO | TERRNO, "clock_nanosleep");
108 		return;
109 	}
110 
111 	SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &now);
112 	tst_res(TINFO, "now is     %s", strtime(&now));
113 
114 	if (in_order(target, now)) {
115 		tst_res(TINFO, "hrtimer early expiration is not detected.");
116 	} else {
117 		tst_res(TFAIL, "hrtimer early expiration is detected.");
118 		errors++;
119 	}
120 }
121 
run_leapsec(void)122 static void run_leapsec(void)
123 {
124 	const struct timespec sleeptime = { 0, NSEC_PER_SEC / 2 };
125 	struct timespec now, leap, start;
126 	struct timex tx;
127 
128 	SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &now);
129 	start = now;
130 	tst_res(TINFO, "test start at %s", strtime(&now));
131 
132 	test_hrtimer_early_expiration();
133 
134 	/* calculate the next leap second */
135 	now.tv_sec += 86400 - now.tv_sec % 86400;
136 	now.tv_nsec = 0;
137 	leap = now;
138 	tst_res(TINFO, "scheduling leap second %s", strtime(&leap));
139 
140 	/* start before the leap second */
141 	now.tv_sec -= SECONDS_BEFORE_LEAP;
142 	SAFE_CLOCK_SETTIME(CLOCK_REALTIME, &now);
143 
144 	tst_res(TINFO, "setting time to        %s", strtime(&now));
145 
146 	/* reset NTP time state */
147 	adjtimex_status(&tx, STA_PLL);
148 	adjtimex_status(&tx, 0);
149 
150 	/* set the leap second insert flag */
151 	adjtimex_status(&tx, STA_INS);
152 
153 	/* reliably sleep until after the leap second */
154 	while (tx.time.tv_sec < leap.tv_sec + SECONDS_AFTER_LEAP) {
155 		adjtimex_status(&tx, tx.status);
156 		clock_nanosleep(CLOCK_MONOTONIC, 0, &sleeptime, NULL);
157 	}
158 
159 	test_hrtimer_early_expiration();
160 
161 	adjtimex_status(&tx, STA_PLL);
162 	adjtimex_status(&tx, 0);
163 
164 	/* recover from timer expiring state and restore time */
165 	SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &now);
166 	start.tv_sec += now.tv_sec - (leap.tv_sec - SECONDS_BEFORE_LEAP);
167 	start.tv_nsec += now.tv_nsec;
168 	start.tv_sec += start.tv_nsec / NSEC_PER_SEC;
169 	start.tv_nsec = start.tv_nsec % NSEC_PER_SEC;
170 	tst_res(TINFO, "restoring time to %s", strtime(&start));
171 	/* calls clock_was_set() in kernel to revert inconsistency */
172 	SAFE_CLOCK_SETTIME(CLOCK_REALTIME, &start);
173 
174 	test_hrtimer_early_expiration();
175 
176 	if (!errors)
177 		tst_res(TPASS, "No errors were reported during this test!");
178 	else
179 		tst_res(TFAIL, "Got %d errors during this test!", errors);
180 
181 }
182 
setup(void)183 static void setup(void)
184 {
185 	errors = 0;
186 }
187 
cleanup(void)188 static void cleanup(void)
189 {
190 	struct timespec now;
191 
192 	SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &now);
193 	/* Calls clock_was_set() in the kernel to revert inconsistencies.
194 	 * The only possible error EPERM doesn't matter here. */
195 	SAFE_CLOCK_SETTIME(CLOCK_REALTIME, &now);
196 }
197 
198 static struct tst_test test = {
199 	.test_all = run_leapsec,
200 	.setup = setup,
201 	.cleanup = cleanup,
202 	.needs_root = 1,
203 };
204