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
27struct 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 }
39static 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
48static int child_ns, parent_ns = -1;
49
50static 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
60static 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
90static 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
150int 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