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(fd: 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(fd: 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 | msg: "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(fd: 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 | msg: "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(msg: "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 | |