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