162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#define _GNU_SOURCE
362306a36Sopenharmony_ci#include <errno.h>
462306a36Sopenharmony_ci#include <fcntl.h>
562306a36Sopenharmony_ci#include <sched.h>
662306a36Sopenharmony_ci#include <stdio.h>
762306a36Sopenharmony_ci#include <stdbool.h>
862306a36Sopenharmony_ci#include <sys/stat.h>
962306a36Sopenharmony_ci#include <sys/syscall.h>
1062306a36Sopenharmony_ci#include <sys/types.h>
1162306a36Sopenharmony_ci#include <time.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <string.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "log.h"
1662306a36Sopenharmony_ci#include "timens.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * Test shouldn't be run for a day, so add 10 days to child
2062306a36Sopenharmony_ci * time and check parent's time to be in the same day.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci#define DAY_IN_SEC			(60*60*24)
2362306a36Sopenharmony_ci#define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct test_clock {
2662306a36Sopenharmony_ci	clockid_t id;
2762306a36Sopenharmony_ci	char *name;
2862306a36Sopenharmony_ci	/*
2962306a36Sopenharmony_ci	 * off_id is -1 if a clock has own offset, or it contains an index
3062306a36Sopenharmony_ci	 * which contains a right offset of this clock.
3162306a36Sopenharmony_ci	 */
3262306a36Sopenharmony_ci	int off_id;
3362306a36Sopenharmony_ci	time_t offset;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define ct(clock, off_id)	{ clock, #clock, off_id }
3762306a36Sopenharmony_cistatic struct test_clock clocks[] = {
3862306a36Sopenharmony_ci	ct(CLOCK_BOOTTIME, -1),
3962306a36Sopenharmony_ci	ct(CLOCK_BOOTTIME_ALARM, 1),
4062306a36Sopenharmony_ci	ct(CLOCK_MONOTONIC, -1),
4162306a36Sopenharmony_ci	ct(CLOCK_MONOTONIC_COARSE, 1),
4262306a36Sopenharmony_ci	ct(CLOCK_MONOTONIC_RAW, 1),
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci#undef ct
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int child_ns, parent_ns = -1;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int switch_ns(int fd)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	if (setns(fd, CLONE_NEWTIME)) {
5162306a36Sopenharmony_ci		pr_perror("setns()");
5262306a36Sopenharmony_ci		return -1;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int init_namespaces(void)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	char path[] = "/proc/self/ns/time_for_children";
6162306a36Sopenharmony_ci	struct stat st1, st2;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (parent_ns == -1) {
6462306a36Sopenharmony_ci		parent_ns = open(path, O_RDONLY);
6562306a36Sopenharmony_ci		if (parent_ns <= 0)
6662306a36Sopenharmony_ci			return pr_perror("Unable to open %s", path);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (fstat(parent_ns, &st1))
7062306a36Sopenharmony_ci		return pr_perror("Unable to stat the parent timens");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (unshare_timens())
7362306a36Sopenharmony_ci		return  -1;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	child_ns = open(path, O_RDONLY);
7662306a36Sopenharmony_ci	if (child_ns <= 0)
7762306a36Sopenharmony_ci		return pr_perror("Unable to open %s", path);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (fstat(child_ns, &st2))
8062306a36Sopenharmony_ci		return pr_perror("Unable to stat the timens");
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (st1.st_ino == st2.st_ino)
8362306a36Sopenharmony_ci		return pr_perror("The same child_ns after CLONE_NEWTIME");
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct timespec child_ts_new, parent_ts_old, cur_ts;
9162306a36Sopenharmony_ci	char *entry = raw_syscall ? "syscall" : "vdso";
9262306a36Sopenharmony_ci	double precision = 0.0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (check_skip(clocks[clock_index].id))
9562306a36Sopenharmony_ci		return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	switch (clocks[clock_index].id) {
9862306a36Sopenharmony_ci	case CLOCK_MONOTONIC_COARSE:
9962306a36Sopenharmony_ci	case CLOCK_MONOTONIC_RAW:
10062306a36Sopenharmony_ci		precision = -2.0;
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (switch_ns(parent_ns))
10562306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", child_ns);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (_gettime(clocks[clock_index].id, &parent_ts_old, raw_syscall))
10862306a36Sopenharmony_ci		return -1;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	child_ts_new.tv_nsec = parent_ts_old.tv_nsec;
11162306a36Sopenharmony_ci	child_ts_new.tv_sec = parent_ts_old.tv_sec + offset;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (switch_ns(child_ns))
11462306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", child_ns);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
11762306a36Sopenharmony_ci		return -1;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) {
12062306a36Sopenharmony_ci		ksft_test_result_fail(
12162306a36Sopenharmony_ci			"Child's %s (%s) time has not changed: %lu -> %lu [%lu]\n",
12262306a36Sopenharmony_ci			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
12362306a36Sopenharmony_ci			child_ts_new.tv_sec, cur_ts.tv_sec);
12462306a36Sopenharmony_ci		return -1;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (switch_ns(parent_ns))
12862306a36Sopenharmony_ci		return pr_err("switch_ns(%d)", parent_ns);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
13162306a36Sopenharmony_ci		return -1;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) {
13462306a36Sopenharmony_ci		ksft_test_result_fail(
13562306a36Sopenharmony_ci			"Parent's %s (%s) time has changed: %lu -> %lu [%lu]\n",
13662306a36Sopenharmony_ci			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
13762306a36Sopenharmony_ci			child_ts_new.tv_sec, cur_ts.tv_sec);
13862306a36Sopenharmony_ci		/* Let's play nice and put it closer to original */
13962306a36Sopenharmony_ci		clock_settime(clocks[clock_index].id, &cur_ts);
14062306a36Sopenharmony_ci		return -1;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ksft_test_result_pass("Passed for %s (%s)\n",
14462306a36Sopenharmony_ci				clocks[clock_index].name, entry);
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciint main(int argc, char *argv[])
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	unsigned int i;
15162306a36Sopenharmony_ci	time_t offset;
15262306a36Sopenharmony_ci	int ret = 0;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	nscheck();
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	check_supported_timers();
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	ksft_set_plan(ARRAY_SIZE(clocks) * 2);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (init_namespaces())
16162306a36Sopenharmony_ci		return 1;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Offsets have to be set before tasks enter the namespace. */
16462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
16562306a36Sopenharmony_ci		if (clocks[i].off_id != -1)
16662306a36Sopenharmony_ci			continue;
16762306a36Sopenharmony_ci		offset = TEN_DAYS_IN_SEC + i * 1000;
16862306a36Sopenharmony_ci		clocks[i].offset = offset;
16962306a36Sopenharmony_ci		if (_settime(clocks[i].id, offset))
17062306a36Sopenharmony_ci			return 1;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
17462306a36Sopenharmony_ci		if (clocks[i].off_id != -1)
17562306a36Sopenharmony_ci			offset = clocks[clocks[i].off_id].offset;
17662306a36Sopenharmony_ci		else
17762306a36Sopenharmony_ci			offset = clocks[i].offset;
17862306a36Sopenharmony_ci		ret |= test_gettime(i, true, offset);
17962306a36Sopenharmony_ci		ret |= test_gettime(i, false, offset);
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (ret)
18362306a36Sopenharmony_ci		ksft_exit_fail();
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ksft_exit_pass();
18662306a36Sopenharmony_ci	return !!ret;
18762306a36Sopenharmony_ci}
188