xref: /openbmc/linux/tools/testing/selftests/timens/timens.c (revision 558ae0355a91c7d28fdf4c0011bee6ebb5118632)
161c57676SDmitry Safonov // SPDX-License-Identifier: GPL-2.0
261c57676SDmitry Safonov #define _GNU_SOURCE
361c57676SDmitry Safonov #include <errno.h>
461c57676SDmitry Safonov #include <fcntl.h>
561c57676SDmitry Safonov #include <sched.h>
661c57676SDmitry Safonov #include <stdio.h>
761c57676SDmitry Safonov #include <stdbool.h>
861c57676SDmitry Safonov #include <sys/stat.h>
961c57676SDmitry Safonov #include <sys/syscall.h>
1061c57676SDmitry Safonov #include <sys/types.h>
1161c57676SDmitry Safonov #include <time.h>
1261c57676SDmitry Safonov #include <unistd.h>
1361c57676SDmitry Safonov #include <string.h>
1461c57676SDmitry Safonov 
1561c57676SDmitry Safonov #include "log.h"
1661c57676SDmitry Safonov #include "timens.h"
1761c57676SDmitry Safonov 
1861c57676SDmitry Safonov /*
1961c57676SDmitry Safonov  * Test shouldn't be run for a day, so add 10 days to child
2061c57676SDmitry Safonov  * time and check parent's time to be in the same day.
2161c57676SDmitry Safonov  */
2261c57676SDmitry Safonov #define DAY_IN_SEC			(60*60*24)
2361c57676SDmitry Safonov #define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
2461c57676SDmitry Safonov 
2561c57676SDmitry Safonov #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
2661c57676SDmitry Safonov 
2761c57676SDmitry Safonov struct test_clock {
2861c57676SDmitry Safonov 	clockid_t id;
2961c57676SDmitry Safonov 	char *name;
3061c57676SDmitry Safonov 	/*
3161c57676SDmitry Safonov 	 * off_id is -1 if a clock has own offset, or it contains an index
3261c57676SDmitry Safonov 	 * which contains a right offset of this clock.
3361c57676SDmitry Safonov 	 */
3461c57676SDmitry Safonov 	int off_id;
3561c57676SDmitry Safonov 	time_t offset;
3661c57676SDmitry Safonov };
3761c57676SDmitry Safonov 
3861c57676SDmitry Safonov #define ct(clock, off_id)	{ clock, #clock, off_id }
3961c57676SDmitry Safonov static struct test_clock clocks[] = {
4061c57676SDmitry Safonov 	ct(CLOCK_BOOTTIME, -1),
4161c57676SDmitry Safonov 	ct(CLOCK_BOOTTIME_ALARM, 1),
4261c57676SDmitry Safonov 	ct(CLOCK_MONOTONIC, -1),
4361c57676SDmitry Safonov 	ct(CLOCK_MONOTONIC_COARSE, 1),
4461c57676SDmitry Safonov 	ct(CLOCK_MONOTONIC_RAW, 1),
4561c57676SDmitry Safonov };
4661c57676SDmitry Safonov #undef ct
4761c57676SDmitry Safonov 
4861c57676SDmitry Safonov static int child_ns, parent_ns = -1;
4961c57676SDmitry Safonov 
5061c57676SDmitry Safonov static int switch_ns(int fd)
5161c57676SDmitry Safonov {
5261c57676SDmitry Safonov 	if (setns(fd, CLONE_NEWTIME)) {
5361c57676SDmitry Safonov 		pr_perror("setns()");
5461c57676SDmitry Safonov 		return -1;
5561c57676SDmitry Safonov 	}
5661c57676SDmitry Safonov 
5761c57676SDmitry Safonov 	return 0;
5861c57676SDmitry Safonov }
5961c57676SDmitry Safonov 
6061c57676SDmitry Safonov static int init_namespaces(void)
6161c57676SDmitry Safonov {
6261c57676SDmitry Safonov 	char path[] = "/proc/self/ns/time_for_children";
6361c57676SDmitry Safonov 	struct stat st1, st2;
6461c57676SDmitry Safonov 
6561c57676SDmitry Safonov 	if (parent_ns == -1) {
6661c57676SDmitry Safonov 		parent_ns = open(path, O_RDONLY);
6761c57676SDmitry Safonov 		if (parent_ns <= 0)
6861c57676SDmitry Safonov 			return pr_perror("Unable to open %s", path);
6961c57676SDmitry Safonov 	}
7061c57676SDmitry Safonov 
7161c57676SDmitry Safonov 	if (fstat(parent_ns, &st1))
7261c57676SDmitry Safonov 		return pr_perror("Unable to stat the parent timens");
7361c57676SDmitry Safonov 
7461c57676SDmitry Safonov 	if (unshare_timens())
7561c57676SDmitry Safonov 		return  -1;
7661c57676SDmitry Safonov 
7761c57676SDmitry Safonov 	child_ns = open(path, O_RDONLY);
7861c57676SDmitry Safonov 	if (child_ns <= 0)
7961c57676SDmitry Safonov 		return pr_perror("Unable to open %s", path);
8061c57676SDmitry Safonov 
8161c57676SDmitry Safonov 	if (fstat(child_ns, &st2))
8261c57676SDmitry Safonov 		return pr_perror("Unable to stat the timens");
8361c57676SDmitry Safonov 
8461c57676SDmitry Safonov 	if (st1.st_ino == st2.st_ino)
8561c57676SDmitry Safonov 		return pr_perror("The same child_ns after CLONE_NEWTIME");
8661c57676SDmitry Safonov 
8761c57676SDmitry Safonov 	return 0;
8861c57676SDmitry Safonov }
8961c57676SDmitry Safonov 
9061c57676SDmitry Safonov static int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset)
9161c57676SDmitry Safonov {
9261c57676SDmitry Safonov 	struct timespec child_ts_new, parent_ts_old, cur_ts;
9361c57676SDmitry Safonov 	char *entry = raw_syscall ? "syscall" : "vdso";
9461c57676SDmitry Safonov 	double precision = 0.0;
9561c57676SDmitry Safonov 
9661c57676SDmitry Safonov 	if (check_skip(clocks[clock_index].id))
9761c57676SDmitry Safonov 		return 0;
9861c57676SDmitry Safonov 
9961c57676SDmitry Safonov 	switch (clocks[clock_index].id) {
10061c57676SDmitry Safonov 	case CLOCK_MONOTONIC_COARSE:
10161c57676SDmitry Safonov 	case CLOCK_MONOTONIC_RAW:
10261c57676SDmitry Safonov 		precision = -2.0;
10361c57676SDmitry Safonov 		break;
10461c57676SDmitry Safonov 	}
10561c57676SDmitry Safonov 
10661c57676SDmitry Safonov 	if (switch_ns(parent_ns))
10761c57676SDmitry Safonov 		return pr_err("switch_ns(%d)", child_ns);
10861c57676SDmitry Safonov 
10961c57676SDmitry Safonov 	if (_gettime(clocks[clock_index].id, &parent_ts_old, raw_syscall))
11061c57676SDmitry Safonov 		return -1;
11161c57676SDmitry Safonov 
11261c57676SDmitry Safonov 	child_ts_new.tv_nsec = parent_ts_old.tv_nsec;
11361c57676SDmitry Safonov 	child_ts_new.tv_sec = parent_ts_old.tv_sec + offset;
11461c57676SDmitry Safonov 
11561c57676SDmitry Safonov 	if (switch_ns(child_ns))
11661c57676SDmitry Safonov 		return pr_err("switch_ns(%d)", child_ns);
11761c57676SDmitry Safonov 
11861c57676SDmitry Safonov 	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
11961c57676SDmitry Safonov 		return -1;
12061c57676SDmitry Safonov 
12161c57676SDmitry Safonov 	if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) {
12261c57676SDmitry Safonov 		ksft_test_result_fail(
12361c57676SDmitry Safonov 			"Child's %s (%s) time has not changed: %lu -> %lu [%lu]\n",
12461c57676SDmitry Safonov 			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
12561c57676SDmitry Safonov 			child_ts_new.tv_sec, cur_ts.tv_sec);
12661c57676SDmitry Safonov 		return -1;
12761c57676SDmitry Safonov 	}
12861c57676SDmitry Safonov 
12961c57676SDmitry Safonov 	if (switch_ns(parent_ns))
13061c57676SDmitry Safonov 		return pr_err("switch_ns(%d)", parent_ns);
13161c57676SDmitry Safonov 
13261c57676SDmitry Safonov 	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
13361c57676SDmitry Safonov 		return -1;
13461c57676SDmitry Safonov 
13561c57676SDmitry Safonov 	if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) {
13661c57676SDmitry Safonov 		ksft_test_result_fail(
13761c57676SDmitry Safonov 			"Parent's %s (%s) time has changed: %lu -> %lu [%lu]\n",
13861c57676SDmitry Safonov 			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
13961c57676SDmitry Safonov 			child_ts_new.tv_sec, cur_ts.tv_sec);
14061c57676SDmitry Safonov 		/* Let's play nice and put it closer to original */
14161c57676SDmitry Safonov 		clock_settime(clocks[clock_index].id, &cur_ts);
14261c57676SDmitry Safonov 		return -1;
14361c57676SDmitry Safonov 	}
14461c57676SDmitry Safonov 
14561c57676SDmitry Safonov 	ksft_test_result_pass("Passed for %s (%s)\n",
14661c57676SDmitry Safonov 				clocks[clock_index].name, entry);
14761c57676SDmitry Safonov 	return 0;
14861c57676SDmitry Safonov }
14961c57676SDmitry Safonov 
15061c57676SDmitry Safonov int main(int argc, char *argv[])
15161c57676SDmitry Safonov {
15261c57676SDmitry Safonov 	unsigned int i;
15361c57676SDmitry Safonov 	time_t offset;
15461c57676SDmitry Safonov 	int ret = 0;
15561c57676SDmitry Safonov 
15661c57676SDmitry Safonov 	nscheck();
15761c57676SDmitry Safonov 
158*558ae035SAndrei Vagin 	check_supported_timers();
15961c57676SDmitry Safonov 
16061c57676SDmitry Safonov 	ksft_set_plan(ARRAY_SIZE(clocks) * 2);
16161c57676SDmitry Safonov 
16261c57676SDmitry Safonov 	if (init_namespaces())
16361c57676SDmitry Safonov 		return 1;
16461c57676SDmitry Safonov 
16561c57676SDmitry Safonov 	/* Offsets have to be set before tasks enter the namespace. */
16661c57676SDmitry Safonov 	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
16761c57676SDmitry Safonov 		if (clocks[i].off_id != -1)
16861c57676SDmitry Safonov 			continue;
16961c57676SDmitry Safonov 		offset = TEN_DAYS_IN_SEC + i * 1000;
17061c57676SDmitry Safonov 		clocks[i].offset = offset;
17161c57676SDmitry Safonov 		if (_settime(clocks[i].id, offset))
17261c57676SDmitry Safonov 			return 1;
17361c57676SDmitry Safonov 	}
17461c57676SDmitry Safonov 
17561c57676SDmitry Safonov 	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
17661c57676SDmitry Safonov 		if (clocks[i].off_id != -1)
17761c57676SDmitry Safonov 			offset = clocks[clocks[i].off_id].offset;
17861c57676SDmitry Safonov 		else
17961c57676SDmitry Safonov 			offset = clocks[i].offset;
18061c57676SDmitry Safonov 		ret |= test_gettime(i, true, offset);
18161c57676SDmitry Safonov 		ret |= test_gettime(i, false, offset);
18261c57676SDmitry Safonov 	}
18361c57676SDmitry Safonov 
18461c57676SDmitry Safonov 	if (ret)
18561c57676SDmitry Safonov 		ksft_exit_fail();
18661c57676SDmitry Safonov 
18761c57676SDmitry Safonov 	ksft_exit_pass();
18861c57676SDmitry Safonov 	return !!ret;
18961c57676SDmitry Safonov }
190