xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/clock_gettime/clock_gettime04.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2020 Linaro Limited. All rights reserved.
4  * Author: Viresh Kumar<[email protected]>
5  */
6 
7 /*\
8  * [Description]
9  *
10  * Check time difference between successive readings and report a bug if
11  * difference found to be over 5 ms.
12  *
13  * This test reports a s390x BUG which has been fixed in:
14  *
15  *    commit 5b43bd184530af6b868d8273b0a743a138d37ee8
16  *    Author: Heiko Carstens <[email protected]>
17  *    Date:   Wed Mar 24 20:23:55 2021 +0100
18  *
19  *    s390/vdso: fix initializing and updating of vdso_data
20  */
21 
22 #include "config.h"
23 #include "parse_vdso.h"
24 #include "time64_variants.h"
25 #include "tst_timer.h"
26 #include "tst_safe_clocks.h"
27 
28 clockid_t clks[] = {
29 	CLOCK_REALTIME,
30 	CLOCK_REALTIME_COARSE,
31 	CLOCK_MONOTONIC,
32 	CLOCK_MONOTONIC_COARSE,
33 	CLOCK_MONOTONIC_RAW,
34 	CLOCK_BOOTTIME,
35 };
36 
37 static gettime_t ptr_vdso_gettime, ptr_vdso_gettime64;
38 static long long delta, precise_delta, coarse_delta;
39 
do_vdso_gettime(gettime_t vdso,clockid_t clk_id,void * ts)40 static inline int do_vdso_gettime(gettime_t vdso, clockid_t clk_id, void *ts)
41 {
42 	if (!vdso) {
43 		errno = ENOSYS;
44 		return -1;
45 	}
46 
47 	return vdso(clk_id, ts);
48 }
49 
vdso_gettime(clockid_t clk_id,void * ts)50 static inline int vdso_gettime(clockid_t clk_id, void *ts)
51 {
52 	return do_vdso_gettime(ptr_vdso_gettime, clk_id, ts);
53 }
54 
vdso_gettime64(clockid_t clk_id,void * ts)55 static inline int vdso_gettime64(clockid_t clk_id, void *ts)
56 {
57 	return do_vdso_gettime(ptr_vdso_gettime64, clk_id, ts);
58 }
59 
my_gettimeofday(clockid_t clk_id,void * ts)60 static inline int my_gettimeofday(clockid_t clk_id, void *ts)
61 {
62 	struct timeval tval;
63 
64 	if (clk_id != CLOCK_REALTIME)
65 		tst_brk(TBROK, "%s: Invalid clk_id, exiting", tst_clock_name(clk_id));
66 
67 	if (gettimeofday(&tval, NULL) < 0)
68 		tst_brk(TBROK | TERRNO, "gettimeofday() failed");
69 
70 	/*
71 	 * The array defines the type to TST_LIBC_TIMESPEC and so we can cast
72 	 * this into struct timespec.
73 	 */
74 	*((struct timespec *)ts) = tst_timespec_from_us(tst_timeval_to_us(tval));
75 	return 0;
76 }
77 
78 static struct time64_variants variants[] = {
79 	{ .clock_gettime = libc_clock_gettime, .ts_type = TST_LIBC_TIMESPEC, .desc = "vDSO or syscall with libc spec"},
80 
81 #if (__NR_clock_gettime != __LTP__NR_INVALID_SYSCALL)
82 	{ .clock_gettime = sys_clock_gettime, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
83 	{ .clock_gettime = vdso_gettime, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "vDSO with old kernel spec"},
84 #endif
85 
86 #if (__NR_clock_gettime64 != __LTP__NR_INVALID_SYSCALL)
87 	{ .clock_gettime = sys_clock_gettime64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
88 	{ .clock_gettime = vdso_gettime64, .ts_type = TST_KERN_TIMESPEC, .desc = "vDSO time64 with kernel spec"},
89 #endif
90 	{ .clock_gettime = my_gettimeofday, .ts_type = TST_LIBC_TIMESPEC, .desc = "gettimeofday"},
91 };
92 
setup(void)93 static void setup(void)
94 {
95 	struct timespec res;
96 
97 	clock_getres(CLOCK_REALTIME, &res);
98 	precise_delta = 5 + res.tv_nsec / 1000000;
99 
100 	clock_getres(CLOCK_REALTIME_COARSE, &res);
101 	coarse_delta = 5 + res.tv_nsec / 1000000;
102 
103 	if (tst_is_virt(VIRT_ANY)) {
104 		tst_res(TINFO, "Running in a virtual machine, multiply the delta by 10.");
105 		precise_delta *= 10;
106 		coarse_delta *= 10;
107 	}
108 
109 	find_clock_gettime_vdso(&ptr_vdso_gettime, &ptr_vdso_gettime64);
110 }
111 
run(unsigned int i)112 static void run(unsigned int i)
113 {
114 	struct tst_ts ts;
115 	long long start, end = 0, diff, slack;
116 	struct time64_variants *tv;
117 	int count = 10000, ret;
118 	unsigned int j;
119 
120 	if (clks[i] == CLOCK_REALTIME_COARSE || clks[i] == CLOCK_MONOTONIC_COARSE)
121 		delta = coarse_delta;
122 	else
123 		delta = precise_delta;
124 
125 	do {
126 		for (j = 0; j < ARRAY_SIZE(variants); j++) {
127 			/* Refresh time in start */
128 			start = end;
129 
130 			tv = &variants[j];
131 			ts.type = tv->ts_type;
132 
133 			/* Do gettimeofday() test only for CLOCK_REALTIME */
134 			if (tv->clock_gettime == my_gettimeofday && clks[i] != CLOCK_REALTIME)
135 				continue;
136 
137 			ret = tv->clock_gettime(clks[i], tst_ts_get(&ts));
138 			if (ret) {
139 				/*
140 				 * _vdso_gettime() sets error to ENOSYS if vdso
141 				 * isn't available.
142 				 */
143 				if (errno == ENOSYS)
144 					continue;
145 
146 				tst_res(TFAIL | TERRNO, "%s: clock_gettime() failed (%d)",
147 					tst_clock_name(clks[i]), j);
148 				return;
149 			}
150 
151 			end = tst_ts_to_ns(ts);
152 
153 			/* Skip comparison on first traversal */
154 			if (count == 10000 && !j)
155 				continue;
156 
157 			/*
158 			 * gettimeofday() doesn't capture time less than 1 us,
159 			 * add 999 to it.
160 			 */
161 			if (tv->clock_gettime == my_gettimeofday)
162 				slack = 999;
163 			else
164 				slack = 0;
165 
166 			diff = end + slack - start;
167 			if (diff < 0) {
168 				tst_res(TFAIL, "%s: Time travelled backwards (%d): %lld ns",
169 					tst_clock_name(clks[i]), j, diff);
170 				return;
171 			}
172 
173 			diff /= 1000000;
174 
175 			if (diff >= delta) {
176 				tst_res(TFAIL, "%s(%s): Difference between successive readings greater than %lld ms (%d): %lld",
177 					tst_clock_name(clks[i]), tv->desc, delta, j, diff);
178 				return;
179 			}
180 		}
181 	} while (--count);
182 
183 	tst_res(TPASS, "%s: Difference between successive readings is reasonable for following variants:",
184 			tst_clock_name(clks[i]));
185 	for (j = 0; j < ARRAY_SIZE(variants); j++) {
186 		if (variants[j].clock_gettime == my_gettimeofday && clks[i] != CLOCK_REALTIME)
187 			continue;
188 		tst_res(TINFO, "\t- %s", variants[j].desc);
189 	}
190 }
191 
192 static struct tst_test test = {
193 	.test = run,
194 	.setup = setup,
195 	.tcnt = ARRAY_SIZE(clks),
196 	.tags = (const struct tst_tag[]) {
197 		{"linux-git", "5b43bd184530"},
198 		{}
199 	}
200 };
201