18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#define _GNU_SOURCE
38c2ecf20Sopenharmony_ci#include <errno.h>
48c2ecf20Sopenharmony_ci#include <fcntl.h>
58c2ecf20Sopenharmony_ci#include <sched.h>
68c2ecf20Sopenharmony_ci#include <stdio.h>
78c2ecf20Sopenharmony_ci#include <stdbool.h>
88c2ecf20Sopenharmony_ci#include <sys/stat.h>
98c2ecf20Sopenharmony_ci#include <sys/syscall.h>
108c2ecf20Sopenharmony_ci#include <sys/types.h>
118c2ecf20Sopenharmony_ci#include <time.h>
128c2ecf20Sopenharmony_ci#include <unistd.h>
138c2ecf20Sopenharmony_ci#include <string.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "log.h"
168c2ecf20Sopenharmony_ci#include "timens.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * Test shouldn't be run for a day, so add 10 days to child
208c2ecf20Sopenharmony_ci * time and check parent's time to be in the same day.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#define DAY_IN_SEC			(60*60*24)
238c2ecf20Sopenharmony_ci#define TEN_DAYS_IN_SEC			(10*DAY_IN_SEC)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct test_clock {
288c2ecf20Sopenharmony_ci	clockid_t id;
298c2ecf20Sopenharmony_ci	char *name;
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * off_id is -1 if a clock has own offset, or it contains an index
328c2ecf20Sopenharmony_ci	 * which contains a right offset of this clock.
338c2ecf20Sopenharmony_ci	 */
348c2ecf20Sopenharmony_ci	int off_id;
358c2ecf20Sopenharmony_ci	time_t offset;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define ct(clock, off_id)	{ clock, #clock, off_id }
398c2ecf20Sopenharmony_cistatic struct test_clock clocks[] = {
408c2ecf20Sopenharmony_ci	ct(CLOCK_BOOTTIME, -1),
418c2ecf20Sopenharmony_ci	ct(CLOCK_BOOTTIME_ALARM, 1),
428c2ecf20Sopenharmony_ci	ct(CLOCK_MONOTONIC, -1),
438c2ecf20Sopenharmony_ci	ct(CLOCK_MONOTONIC_COARSE, 1),
448c2ecf20Sopenharmony_ci	ct(CLOCK_MONOTONIC_RAW, 1),
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci#undef ct
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int child_ns, parent_ns = -1;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int switch_ns(int fd)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	if (setns(fd, CLONE_NEWTIME)) {
538c2ecf20Sopenharmony_ci		pr_perror("setns()");
548c2ecf20Sopenharmony_ci		return -1;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int init_namespaces(void)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	char path[] = "/proc/self/ns/time_for_children";
638c2ecf20Sopenharmony_ci	struct stat st1, st2;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (parent_ns == -1) {
668c2ecf20Sopenharmony_ci		parent_ns = open(path, O_RDONLY);
678c2ecf20Sopenharmony_ci		if (parent_ns <= 0)
688c2ecf20Sopenharmony_ci			return pr_perror("Unable to open %s", path);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (fstat(parent_ns, &st1))
728c2ecf20Sopenharmony_ci		return pr_perror("Unable to stat the parent timens");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (unshare_timens())
758c2ecf20Sopenharmony_ci		return  -1;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	child_ns = open(path, O_RDONLY);
788c2ecf20Sopenharmony_ci	if (child_ns <= 0)
798c2ecf20Sopenharmony_ci		return pr_perror("Unable to open %s", path);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (fstat(child_ns, &st2))
828c2ecf20Sopenharmony_ci		return pr_perror("Unable to stat the timens");
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (st1.st_ino == st2.st_ino)
858c2ecf20Sopenharmony_ci		return pr_perror("The same child_ns after CLONE_NEWTIME");
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct timespec child_ts_new, parent_ts_old, cur_ts;
938c2ecf20Sopenharmony_ci	char *entry = raw_syscall ? "syscall" : "vdso";
948c2ecf20Sopenharmony_ci	double precision = 0.0;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (check_skip(clocks[clock_index].id))
978c2ecf20Sopenharmony_ci		return 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	switch (clocks[clock_index].id) {
1008c2ecf20Sopenharmony_ci	case CLOCK_MONOTONIC_COARSE:
1018c2ecf20Sopenharmony_ci	case CLOCK_MONOTONIC_RAW:
1028c2ecf20Sopenharmony_ci		precision = -2.0;
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (switch_ns(parent_ns))
1078c2ecf20Sopenharmony_ci		return pr_err("switch_ns(%d)", child_ns);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (_gettime(clocks[clock_index].id, &parent_ts_old, raw_syscall))
1108c2ecf20Sopenharmony_ci		return -1;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	child_ts_new.tv_nsec = parent_ts_old.tv_nsec;
1138c2ecf20Sopenharmony_ci	child_ts_new.tv_sec = parent_ts_old.tv_sec + offset;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (switch_ns(child_ns))
1168c2ecf20Sopenharmony_ci		return pr_err("switch_ns(%d)", child_ns);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
1198c2ecf20Sopenharmony_ci		return -1;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) {
1228c2ecf20Sopenharmony_ci		ksft_test_result_fail(
1238c2ecf20Sopenharmony_ci			"Child's %s (%s) time has not changed: %lu -> %lu [%lu]\n",
1248c2ecf20Sopenharmony_ci			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
1258c2ecf20Sopenharmony_ci			child_ts_new.tv_sec, cur_ts.tv_sec);
1268c2ecf20Sopenharmony_ci		return -1;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (switch_ns(parent_ns))
1308c2ecf20Sopenharmony_ci		return pr_err("switch_ns(%d)", parent_ns);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall))
1338c2ecf20Sopenharmony_ci		return -1;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) {
1368c2ecf20Sopenharmony_ci		ksft_test_result_fail(
1378c2ecf20Sopenharmony_ci			"Parent's %s (%s) time has changed: %lu -> %lu [%lu]\n",
1388c2ecf20Sopenharmony_ci			clocks[clock_index].name, entry, parent_ts_old.tv_sec,
1398c2ecf20Sopenharmony_ci			child_ts_new.tv_sec, cur_ts.tv_sec);
1408c2ecf20Sopenharmony_ci		/* Let's play nice and put it closer to original */
1418c2ecf20Sopenharmony_ci		clock_settime(clocks[clock_index].id, &cur_ts);
1428c2ecf20Sopenharmony_ci		return -1;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	ksft_test_result_pass("Passed for %s (%s)\n",
1468c2ecf20Sopenharmony_ci				clocks[clock_index].name, entry);
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	unsigned int i;
1538c2ecf20Sopenharmony_ci	time_t offset;
1548c2ecf20Sopenharmony_ci	int ret = 0;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	nscheck();
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	check_supported_timers();
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ksft_set_plan(ARRAY_SIZE(clocks) * 2);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (init_namespaces())
1638c2ecf20Sopenharmony_ci		return 1;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Offsets have to be set before tasks enter the namespace. */
1668c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
1678c2ecf20Sopenharmony_ci		if (clocks[i].off_id != -1)
1688c2ecf20Sopenharmony_ci			continue;
1698c2ecf20Sopenharmony_ci		offset = TEN_DAYS_IN_SEC + i * 1000;
1708c2ecf20Sopenharmony_ci		clocks[i].offset = offset;
1718c2ecf20Sopenharmony_ci		if (_settime(clocks[i].id, offset))
1728c2ecf20Sopenharmony_ci			return 1;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
1768c2ecf20Sopenharmony_ci		if (clocks[i].off_id != -1)
1778c2ecf20Sopenharmony_ci			offset = clocks[clocks[i].off_id].offset;
1788c2ecf20Sopenharmony_ci		else
1798c2ecf20Sopenharmony_ci			offset = clocks[i].offset;
1808c2ecf20Sopenharmony_ci		ret |= test_gettime(i, true, offset);
1818c2ecf20Sopenharmony_ci		ret |= test_gettime(i, false, offset);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (ret)
1858c2ecf20Sopenharmony_ci		ksft_exit_fail();
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ksft_exit_pass();
1888c2ecf20Sopenharmony_ci	return !!ret;
1898c2ecf20Sopenharmony_ci}
190