Merge tag 'linux-kselftest-fixes-5.8-rc4' of git://git.kernel.org/pub/scm/linux/kerne...
[linux-2.6-microblaze.git] / tools / testing / selftests / timens / timens.c
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
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
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
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
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 }