1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2020 Linaro Limited. All rights reserved.
4 * Author: Viresh Kumar<viresh.kumar@linaro.org>
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 <hca@linux.ibm.com>
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 = 5;
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 if (tst_is_virt(VIRT_ANY)) {
96 tst_res(TINFO, "Running in a virtual machine, multiply the delta by 10.");
97 delta *= 10;
98 }
99
100 find_clock_gettime_vdso(&ptr_vdso_gettime, &ptr_vdso_gettime64);
101 }
102
run(unsigned int i)103 static void run(unsigned int i)
104 {
105 struct tst_ts ts;
106 long long start, end = 0, diff, slack;
107 struct time64_variants *tv;
108 int count = 10000, ret;
109 unsigned int j;
110
111 do {
112 for (j = 0; j < ARRAY_SIZE(variants); j++) {
113 /* Refresh time in start */
114 start = end;
115
116 tv = &variants[j];
117 ts.type = tv->ts_type;
118
119 /* Do gettimeofday() test only for CLOCK_REALTIME */
120 if (tv->clock_gettime == my_gettimeofday && clks[i] != CLOCK_REALTIME)
121 continue;
122
123 ret = tv->clock_gettime(clks[i], tst_ts_get(&ts));
124 if (ret) {
125 /*
126 * _vdso_gettime() sets error to ENOSYS if vdso
127 * isn't available.
128 */
129 if (errno == ENOSYS)
130 continue;
131
132 tst_res(TFAIL | TERRNO, "%s: clock_gettime() failed (%d)",
133 tst_clock_name(clks[i]), j);
134 return;
135 }
136
137 end = tst_ts_to_ns(ts);
138
139 /* Skip comparison on first traversal */
140 if (count == 10000 && !j)
141 continue;
142
143 /*
144 * gettimeofday() doesn't capture time less than 1 us,
145 * add 999 to it.
146 */
147 if (tv->clock_gettime == my_gettimeofday)
148 slack = 999;
149 else
150 slack = 0;
151
152 diff = end + slack - start;
153 if (diff < 0) {
154 tst_res(TFAIL, "%s: Time travelled backwards (%d): %lld ns",
155 tst_clock_name(clks[i]), j, diff);
156 return;
157 }
158
159 diff /= 1000000;
160
161 if (diff >= delta) {
162 tst_res(TFAIL, "%s(%s): Difference between successive readings greater than %lld ms (%d): %lld",
163 tst_clock_name(clks[i]), tv->desc, delta, j, diff);
164 return;
165 }
166 }
167 } while (--count);
168
169 tst_res(TPASS, "%s: Difference between successive readings is reasonable for following variants:",
170 tst_clock_name(clks[i]));
171 for (j = 0; j < ARRAY_SIZE(variants); j++) {
172 if (variants[j].clock_gettime == my_gettimeofday && clks[i] != CLOCK_REALTIME)
173 continue;
174 tst_res(TINFO, "\t- %s", variants[j].desc);
175 }
176 }
177
178 static struct tst_test test = {
179 .test = run,
180 .setup = setup,
181 .tcnt = ARRAY_SIZE(clks),
182 .tags = (const struct tst_tag[]) {
183 {"linux-git", "5b43bd184530"},
184 {}
185 }
186 };
187