xref: /linux/tools/testing/selftests/timers/leap-a-day.c (revision 3b4128b9f374b4219eb716f4ad8a307bc7eb3d84)
1 /* Leap second stress test
2  *              by: John Stultz (john.stultz@linaro.org)
3  *              (C) Copyright IBM 2012
4  *              (C) Copyright 2013, 2015 Linaro Limited
5  *              Licensed under the GPLv2
6  *
7  *  This test signals the kernel to insert a leap second
8  *  every day at midnight GMT. This allows for stressing the
9  *  kernel's leap-second behavior, as well as how well applications
10  *  handle the leap-second discontinuity.
11  *
12  *  Usage: leap-a-day [-w] [-i <num>] [-t]
13  *
14  *  Options:
15  *	-w:	Only set the leap-second flag and wait for the leap second
16  *		each iteration, instead of advancing the time. By default the
17  *		date is set to 10 seconds before midnight GMT, which speeds up
18  *		the number of leapsecond transitions tested, but because it
19  *		calls settimeofday frequently, advancing the time by 24 hours
20  *		every ~16 seconds, it may cause application disruption.
21  *
22  *	-i:	Number of iterations to run (-1 = infinite, default: 10)
23  *
24  *	-t:	Print TAI time.
25  *
26  *  Other notes: Disabling NTP prior to running this is advised, as the two
27  *		 may conflict in their commands to the kernel.
28  *
29  *  To build:
30  *	$ gcc leap-a-day.c -o leap-a-day -lrt
31  *
32  *   This program is free software: you can redistribute it and/or modify
33  *   it under the terms of the GNU General Public License as published by
34  *   the Free Software Foundation, either version 2 of the License, or
35  *   (at your option) any later version.
36  *
37  *   This program is distributed in the hope that it will be useful,
38  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
39  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40  *   GNU General Public License for more details.
41  */
42 
43 
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <time.h>
48 #include <sys/time.h>
49 #include <sys/timex.h>
50 #include <sys/errno.h>
51 #include <string.h>
52 #include <signal.h>
53 #include <unistd.h>
54 #include "clock-helpers.h"
55 #include "kselftest.h"
56 
57 #define CLOCK_TAI 11
58 
59 time_t next_leap;
60 int error_found;
61 
62 /* returns 1 if a <= b, 0 otherwise */
63 static inline int in_order(struct timespec a, struct timespec b)
64 {
65 	if (a.tv_sec < b.tv_sec)
66 		return 1;
67 	if (a.tv_sec > b.tv_sec)
68 		return 0;
69 	if (a.tv_nsec > b.tv_nsec)
70 		return 0;
71 	return 1;
72 }
73 
74 struct timespec timespec_add(struct timespec ts, unsigned long long ns)
75 {
76 	ts.tv_nsec += ns;
77 	while (ts.tv_nsec >= NSEC_PER_SEC) {
78 		ts.tv_nsec -= NSEC_PER_SEC;
79 		ts.tv_sec++;
80 	}
81 	return ts;
82 }
83 
84 char *time_state_str(int state)
85 {
86 	switch (state) {
87 	case TIME_OK:	return "TIME_OK";
88 	case TIME_INS:	return "TIME_INS";
89 	case TIME_DEL:	return "TIME_DEL";
90 	case TIME_OOP:	return "TIME_OOP";
91 	case TIME_WAIT:	return "TIME_WAIT";
92 	case TIME_BAD:	return "TIME_BAD";
93 	}
94 	return "ERROR";
95 }
96 
97 /* clear NTP time_status & time_state */
98 int clear_time_state(void)
99 {
100 	struct timex tx;
101 	int ret;
102 
103 	/*
104 	 * We have to call adjtime twice here, as kernels
105 	 * prior to 6b1859dba01c7 (included in 3.5 and
106 	 * -stable), had an issue with the state machine
107 	 * and wouldn't clear the STA_INS/DEL flag directly.
108 	 */
109 	tx.modes = ADJ_STATUS;
110 	tx.status = STA_PLL;
111 	ret = adjtimex(&tx);
112 
113 	/* Clear maxerror, as it can cause UNSYNC to be set */
114 	tx.modes = ADJ_MAXERROR;
115 	tx.maxerror = 0;
116 	ret = adjtimex(&tx);
117 
118 	/* Clear the status */
119 	tx.modes = ADJ_STATUS;
120 	tx.status = 0;
121 	ret = adjtimex(&tx);
122 
123 	return ret;
124 }
125 
126 /* Make sure we cleanup on ctrl-c */
127 void handler(int unused)
128 {
129 	clear_time_state();
130 	exit(0);
131 }
132 
133 void sigalarm(int signo)
134 {
135 	struct timex tx;
136 	int ret;
137 
138 	tx.modes = 0;
139 	ret = adjtimex(&tx);
140 
141 	if (tx.time.tv_sec < next_leap) {
142 		printf("Error: Early timer expiration! (Should be %ld)\n", next_leap);
143 		error_found = 1;
144 		printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
145 					tx.time.tv_sec,
146 					tx.time.tv_usec,
147 					tx.tai,
148 					time_state_str(ret));
149 	}
150 	if (ret != TIME_WAIT) {
151 		printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n");
152 		error_found = 1;
153 		printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
154 					tx.time.tv_sec,
155 					tx.time.tv_usec,
156 					tx.tai,
157 					time_state_str(ret));
158 	}
159 }
160 
161 
162 /* Test for known hrtimer failure */
163 void test_hrtimer_failure(void)
164 {
165 	struct timespec now, target;
166 
167 	clock_gettime(CLOCK_REALTIME, &now);
168 	target = timespec_add(now, NSEC_PER_SEC/2);
169 	clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);
170 	clock_gettime(CLOCK_REALTIME, &now);
171 
172 	if (!in_order(target, now)) {
173 		printf("ERROR: hrtimer early expiration failure observed.\n");
174 		error_found = 1;
175 	}
176 }
177 
178 int main(int argc, char **argv)
179 {
180 	timer_t tm1;
181 	struct itimerspec its1;
182 	struct sigevent se;
183 	struct sigaction act;
184 	int signum = SIGRTMAX;
185 	int settime = 1;
186 	int tai_time = 0;
187 	int insert = 1;
188 	int iterations = 10;
189 	int opt;
190 
191 	/* Process arguments */
192 	while ((opt = getopt(argc, argv, "wti:")) != -1) {
193 		switch (opt) {
194 		case 'w':
195 			printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n");
196 			settime = 0;
197 			break;
198 		case 'i':
199 			iterations = atoi(optarg);
200 			break;
201 		case 't':
202 			tai_time = 1;
203 			break;
204 		default:
205 			printf("Usage: %s [-w] [-i <iterations>]\n", argv[0]);
206 			printf("	-w: Set flag and wait for leap second each iteration");
207 			printf("	    (default sets time to right before leapsecond)\n");
208 			printf("	-i: Number of iterations (-1 = infinite, default is 10)\n");
209 			printf("	-t: Print TAI time\n");
210 			exit(-1);
211 		}
212 	}
213 
214 	/* Make sure TAI support is present if -t was used */
215 	if (tai_time) {
216 		struct timespec ts;
217 
218 		if (clock_gettime(CLOCK_TAI, &ts)) {
219 			printf("System doesn't support CLOCK_TAI\n");
220 			ksft_exit_fail();
221 		}
222 	}
223 
224 	signal(SIGINT, handler);
225 	signal(SIGKILL, handler);
226 
227 	/* Set up timer signal handler: */
228 	sigfillset(&act.sa_mask);
229 	act.sa_flags = 0;
230 	act.sa_handler = sigalarm;
231 	sigaction(signum, &act, NULL);
232 
233 	if (iterations < 0)
234 		printf("This runs continuously. Press ctrl-c to stop\n");
235 	else
236 		printf("Running for %i iterations. Press ctrl-c to stop\n", iterations);
237 
238 	printf("\n");
239 	while (1) {
240 		int ret;
241 		struct timespec ts;
242 		struct timex tx;
243 		time_t now;
244 
245 		/* Get the current time */
246 		clock_gettime(CLOCK_REALTIME, &ts);
247 
248 		/* Calculate the next possible leap second 23:59:60 GMT */
249 		next_leap = ts.tv_sec;
250 		next_leap += 86400 - (next_leap % 86400);
251 
252 		if (settime) {
253 			struct timeval tv;
254 
255 			tv.tv_sec = next_leap - 10;
256 			tv.tv_usec = 0;
257 			settimeofday(&tv, NULL);
258 			printf("Setting time to %s", ctime(&tv.tv_sec));
259 		}
260 
261 		/* Reset NTP time state */
262 		clear_time_state();
263 
264 		/* Set the leap second insert flag */
265 		tx.modes = ADJ_STATUS;
266 		if (insert)
267 			tx.status = STA_INS;
268 		else
269 			tx.status = STA_DEL;
270 		ret = adjtimex(&tx);
271 		if (ret < 0) {
272 			printf("Error: Problem setting STA_INS/STA_DEL!: %s\n",
273 							time_state_str(ret));
274 			ksft_exit_fail();
275 		}
276 
277 		/* Validate STA_INS was set */
278 		tx.modes = 0;
279 		ret = adjtimex(&tx);
280 		if (tx.status != STA_INS && tx.status != STA_DEL) {
281 			printf("Error: STA_INS/STA_DEL not set!: %s\n",
282 							time_state_str(ret));
283 			ksft_exit_fail();
284 		}
285 
286 		if (tai_time) {
287 			printf("Using TAI time,"
288 				" no inconsistencies should be seen!\n");
289 		}
290 
291 		printf("Scheduling leap second for %s", ctime(&next_leap));
292 
293 		/* Set up timer */
294 		printf("Setting timer for %ld -  %s", next_leap, ctime(&next_leap));
295 		memset(&se, 0, sizeof(se));
296 		se.sigev_notify = SIGEV_SIGNAL;
297 		se.sigev_signo = signum;
298 		se.sigev_value.sival_int = 0;
299 		if (timer_create(CLOCK_REALTIME, &se, &tm1) == -1) {
300 			printf("Error: timer_create failed\n");
301 			ksft_exit_fail();
302 		}
303 		its1.it_value.tv_sec = next_leap;
304 		its1.it_value.tv_nsec = 0;
305 		its1.it_interval.tv_sec = 0;
306 		its1.it_interval.tv_nsec = 0;
307 		timer_settime(tm1, TIMER_ABSTIME, &its1, NULL);
308 
309 		/* Wake up 3 seconds before leap */
310 		ts.tv_sec = next_leap - 3;
311 		ts.tv_nsec = 0;
312 
313 
314 		while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL))
315 			printf("Something woke us up, returning to sleep\n");
316 
317 		/* Validate STA_INS is still set */
318 		tx.modes = 0;
319 		ret = adjtimex(&tx);
320 		if (tx.status != STA_INS && tx.status != STA_DEL) {
321 			printf("Something cleared STA_INS/STA_DEL, setting it again.\n");
322 			tx.modes = ADJ_STATUS;
323 			if (insert)
324 				tx.status = STA_INS;
325 			else
326 				tx.status = STA_DEL;
327 			ret = adjtimex(&tx);
328 		}
329 
330 		/* Check adjtimex output every half second */
331 		now = tx.time.tv_sec;
332 		while (now < next_leap + 2) {
333 			char buf[26];
334 			struct timespec tai;
335 			int ret;
336 
337 			tx.modes = 0;
338 			ret = adjtimex(&tx);
339 
340 			if (tai_time) {
341 				clock_gettime(CLOCK_TAI, &tai);
342 				printf("%ld sec, %9ld ns\t%s\n",
343 						tai.tv_sec,
344 						tai.tv_nsec,
345 						time_state_str(ret));
346 			} else {
347 				ctime_r(&tx.time.tv_sec, buf);
348 				buf[strlen(buf)-1] = 0; /*remove trailing\n */
349 
350 				printf("%s + %6ld us (%i)\t%s\n",
351 						buf,
352 						tx.time.tv_usec,
353 						tx.tai,
354 						time_state_str(ret));
355 			}
356 			now = tx.time.tv_sec;
357 			/* Sleep for another half second */
358 			ts.tv_sec = 0;
359 			ts.tv_nsec = NSEC_PER_SEC / 2;
360 			clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
361 		}
362 		/* Switch to using other mode */
363 		insert = !insert;
364 
365 		/* Note if kernel has known hrtimer failure */
366 		test_hrtimer_failure();
367 
368 		printf("Leap complete\n");
369 		if (error_found) {
370 			printf("Errors observed\n");
371 			clear_time_state();
372 			ksft_exit_fail();
373 		}
374 		printf("\n");
375 		if ((iterations != -1) && !(--iterations))
376 			break;
377 	}
378 
379 	clear_time_state();
380 	ksft_exit_pass();
381 }
382