1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #define _GNU_SOURCE |
3 | #include <sys/types.h> |
4 | #include <sys/stat.h> |
5 | #include <errno.h> |
6 | #include <fcntl.h> |
7 | #include <sched.h> |
8 | #include <time.h> |
9 | #include <stdio.h> |
10 | #include <unistd.h> |
11 | #include <sys/syscall.h> |
12 | #include <dlfcn.h> |
13 | |
14 | #include "log.h" |
15 | #include "timens.h" |
16 | |
17 | typedef int (*vgettime_t)(clockid_t, struct timespec *); |
18 | |
19 | vgettime_t vdso_clock_gettime; |
20 | |
21 | static void fill_function_pointers(void) |
22 | { |
23 | void *vdso = dlopen("linux-vdso.so.1" , |
24 | RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); |
25 | if (!vdso) |
26 | vdso = dlopen("linux-gate.so.1" , |
27 | RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); |
28 | if (!vdso) |
29 | vdso = dlopen("linux-vdso32.so.1" , |
30 | RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); |
31 | if (!vdso) |
32 | vdso = dlopen("linux-vdso64.so.1" , |
33 | RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); |
34 | if (!vdso) { |
35 | pr_err("[WARN]\tfailed to find vDSO\n" ); |
36 | return; |
37 | } |
38 | |
39 | vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime" ); |
40 | if (!vdso_clock_gettime) |
41 | vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__kernel_clock_gettime" ); |
42 | if (!vdso_clock_gettime) |
43 | pr_err("Warning: failed to find clock_gettime in vDSO\n" ); |
44 | |
45 | } |
46 | |
47 | static void test(clock_t clockid, char *clockstr, bool in_ns) |
48 | { |
49 | struct timespec tp, start; |
50 | long i = 0; |
51 | const int timeout = 3; |
52 | |
53 | vdso_clock_gettime(clockid, &start); |
54 | tp = start; |
55 | for (tp = start; start.tv_sec + timeout > tp.tv_sec || |
56 | (start.tv_sec + timeout == tp.tv_sec && |
57 | start.tv_nsec > tp.tv_nsec); i++) { |
58 | vdso_clock_gettime(clockid, &tp); |
59 | } |
60 | |
61 | ksft_test_result_pass(msg: "%s:\tclock: %10s\tcycles:\t%10ld\n" , |
62 | in_ns ? "ns" : "host" , clockstr, i); |
63 | } |
64 | |
65 | int main(int argc, char *argv[]) |
66 | { |
67 | time_t offset = 10; |
68 | int nsfd; |
69 | |
70 | ksft_set_plan(plan: 8); |
71 | |
72 | fill_function_pointers(); |
73 | |
74 | test(CLOCK_MONOTONIC, "monotonic" , false); |
75 | test(CLOCK_MONOTONIC_COARSE, "monotonic-coarse" , false); |
76 | test(CLOCK_MONOTONIC_RAW, "monotonic-raw" , false); |
77 | test(CLOCK_BOOTTIME, "boottime" , false); |
78 | |
79 | nscheck(); |
80 | |
81 | if (unshare_timens()) |
82 | return 1; |
83 | |
84 | nsfd = open("/proc/self/ns/time_for_children" , O_RDONLY); |
85 | if (nsfd < 0) |
86 | return pr_perror("Can't open a time namespace" ); |
87 | |
88 | if (_settime(CLOCK_MONOTONIC, offset)) |
89 | return 1; |
90 | if (_settime(CLOCK_BOOTTIME, offset)) |
91 | return 1; |
92 | |
93 | if (setns(nsfd, CLONE_NEWTIME)) |
94 | return pr_perror("setns" ); |
95 | |
96 | test(CLOCK_MONOTONIC, "monotonic" , true); |
97 | test(CLOCK_MONOTONIC_COARSE, "monotonic-coarse" , true); |
98 | test(CLOCK_MONOTONIC_RAW, "monotonic-raw" , true); |
99 | test(CLOCK_BOOTTIME, "boottime" , true); |
100 | |
101 | ksft_exit_pass(); |
102 | return 0; |
103 | } |
104 | |