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