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