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 		pr_err("[WARN]\tfailed to find vDSO\n");
30 		return;
31 	}
32 
33 	vdso_clock_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
34 	if (!vdso_clock_gettime)
35 		pr_err("Warning: failed to find clock_gettime in vDSO\n");
36 
37 }
38 
39 static void test(clock_t clockid, char *clockstr, bool in_ns)
40 {
41 	struct timespec tp, start;
42 	long i = 0;
43 	const int timeout = 3;
44 
45 	vdso_clock_gettime(clockid, &start);
46 	tp = start;
47 	for (tp = start; start.tv_sec + timeout > tp.tv_sec ||
48 			 (start.tv_sec + timeout == tp.tv_sec &&
49 			  start.tv_nsec > tp.tv_nsec); i++) {
50 		vdso_clock_gettime(clockid, &tp);
51 	}
52 
53 	ksft_test_result_pass("%s:\tclock: %10s\tcycles:\t%10ld\n",
54 			      in_ns ? "ns" : "host", clockstr, i);
55 }
56 
57 int main(int argc, char *argv[])
58 {
59 	time_t offset = 10;
60 	int nsfd;
61 
62 	ksft_set_plan(8);
63 
64 	fill_function_pointers();
65 
66 	test(CLOCK_MONOTONIC, "monotonic", false);
67 	test(CLOCK_MONOTONIC_COARSE, "monotonic-coarse", false);
68 	test(CLOCK_MONOTONIC_RAW, "monotonic-raw", false);
69 	test(CLOCK_BOOTTIME, "boottime", false);
70 
71 	nscheck();
72 
73 	if (unshare_timens())
74 		return 1;
75 
76 	nsfd = open("/proc/self/ns/time_for_children", O_RDONLY);
77 	if (nsfd < 0)
78 		return pr_perror("Can't open a time namespace");
79 
80 	if (_settime(CLOCK_MONOTONIC, offset))
81 		return 1;
82 	if (_settime(CLOCK_BOOTTIME, offset))
83 		return 1;
84 
85 	if (setns(nsfd, CLONE_NEWTIME))
86 		return pr_perror("setns");
87 
88 	test(CLOCK_MONOTONIC, "monotonic", true);
89 	test(CLOCK_MONOTONIC_COARSE, "monotonic-coarse", true);
90 	test(CLOCK_MONOTONIC_RAW, "monotonic-raw", true);
91 	test(CLOCK_BOOTTIME, "boottime", true);
92 
93 	ksft_exit_pass();
94 	return 0;
95 }
96