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