xref: /linux/tools/testing/selftests/timens/timens.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <sched.h>
6 #include <stdio.h>
7 #include <stdbool.h>
8 #include <sys/stat.h>
9 #include <sys/syscall.h>
10 #include <sys/types.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include <string.h>
14 
15 #include "log.h"
16 #include "timens.h"
17 
18 /*
19  * Test shouldn't be run for a day, so add 10 days to child
20  * time and check parent's time to be in the same day.
21  */
22 #define DAY_IN_SEC			(60*60*24)
23 #define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
24 
25 struct test_clock {
26 	clockid_t id;
27 	char *name;
28 	/*
29 	 * off_id is -1 if a clock has own offset, or it contains an index
30 	 * which contains a right offset of this clock.
31 	 */
32 	int off_id;
33 	time_t offset;
34 };
35 
36 #define ct(clock, off_id)	{ clock, #clock, off_id }
37 static struct test_clock clocks[] = {
38 	ct(CLOCK_BOOTTIME, -1),
39 	ct(CLOCK_BOOTTIME_ALARM, 1),
40 	ct(CLOCK_MONOTONIC, -1),
41 	ct(CLOCK_MONOTONIC_COARSE, 1),
42 	ct(CLOCK_MONOTONIC_RAW, 1),
43 };
44 #undef ct
45 
46 static int child_ns, parent_ns = -1;
47 
48 static int switch_ns(int fd)
49 {
50 	if (setns(fd, CLONE_NEWTIME)) {
51 		pr_perror("setns()");
52 		return -1;
53 	}
54 
55 	return 0;
56 }
57 
58 static int init_namespaces(void)
59 {
60 	char path[] = "/proc/self/ns/time_for_children";
61 	struct stat st1, st2;
62 
63 	if (parent_ns == -1) {
64 		parent_ns = open(path, O_RDONLY);
65 		if (parent_ns <= 0)
66 			return pr_perror("Unable to open %s", path);
67 	}
68 
69 	if (fstat(parent_ns, &st1))
70 		return pr_perror("Unable to stat the parent timens");
71 
72 	if (unshare_timens())
73 		return  -1;
74 
75 	child_ns = open(path, O_RDONLY);
76 	if (child_ns <= 0)
77 		return pr_perror("Unable to open %s", path);
78 
79 	if (fstat(child_ns, &st2))
80 		return pr_perror("Unable to stat the timens");
81 
82 	if (st1.st_ino == st2.st_ino)
83 		return pr_perror("The same child_ns after CLONE_NEWTIME");
84 
85 	return 0;
86 }
87 
88 static int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset)
89 {
90 	struct timespec child_ts_new, parent_ts_old, cur_ts;
91 	char *entry = raw_syscall ? "syscall" : "vdso";
92 	double precision = 0.0;
93 
94 	if (check_skip(clocks[clock_index].id))
95 		return 0;
96 
97 	switch (clocks[clock_index].id) {
98 	case CLOCK_MONOTONIC_COARSE:
99 	case CLOCK_MONOTONIC_RAW:
100 		precision = -2.0;
101 		break;
102 	}
103 
104 	if (switch_ns(parent_ns))
105 		return pr_err("switch_ns(%d)", child_ns);
106 
107 	if (_gettime(clocks[clock_index].id, &parent_ts_old, raw_syscall))
108 		return -1;
109 
110 	child_ts_new.tv_nsec = parent_ts_old.tv_nsec;
111 	child_ts_new.tv_sec = parent_ts_old.tv_sec + offset;
112 
113 	if (switch_ns(child_ns))
114 		return pr_err("switch_ns(%d)", child_ns);
115 
116 	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
117 		return -1;
118 
119 	if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) {
120 		ksft_test_result_fail(
121 			"Child's %s (%s) time has not changed: %lu -> %lu [%lu]\n",
122 			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
123 			child_ts_new.tv_sec, cur_ts.tv_sec);
124 		return -1;
125 	}
126 
127 	if (switch_ns(parent_ns))
128 		return pr_err("switch_ns(%d)", parent_ns);
129 
130 	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
131 		return -1;
132 
133 	if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) {
134 		ksft_test_result_fail(
135 			"Parent's %s (%s) time has changed: %lu -> %lu [%lu]\n",
136 			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
137 			child_ts_new.tv_sec, cur_ts.tv_sec);
138 		/* Let's play nice and put it closer to original */
139 		clock_settime(clocks[clock_index].id, &cur_ts);
140 		return -1;
141 	}
142 
143 	ksft_test_result_pass("Passed for %s (%s)\n",
144 				clocks[clock_index].name, entry);
145 	return 0;
146 }
147 
148 int main(int argc, char *argv[])
149 {
150 	unsigned int i;
151 	time_t offset;
152 	int ret = 0;
153 
154 	nscheck();
155 
156 	check_supported_timers();
157 
158 	ksft_set_plan(ARRAY_SIZE(clocks) * 2);
159 
160 	if (init_namespaces())
161 		return 1;
162 
163 	/* Offsets have to be set before tasks enter the namespace. */
164 	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
165 		if (clocks[i].off_id != -1)
166 			continue;
167 		offset = TEN_DAYS_IN_SEC + i * 1000;
168 		clocks[i].offset = offset;
169 		if (_settime(clocks[i].id, offset))
170 			return 1;
171 	}
172 
173 	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
174 		if (clocks[i].off_id != -1)
175 			offset = clocks[clocks[i].off_id].offset;
176 		else
177 			offset = clocks[i].offset;
178 		ret |= test_gettime(i, true, offset);
179 		ret |= test_gettime(i, false, offset);
180 	}
181 
182 	if (ret)
183 		ksft_exit_fail();
184 
185 	ksft_exit_pass();
186 	return !!ret;
187 }
188