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