timens.c (4243B)
1// SPDX-License-Identifier: GPL-2.0 2#define _GNU_SOURCE 3#include <errno.h> 4#include <fcntl.h> 5#include <sched.h> 6#include <stdio.h> 7#include <stdbool.h> 8#include <sys/stat.h> 9#include <sys/syscall.h> 10#include <sys/types.h> 11#include <time.h> 12#include <unistd.h> 13#include <string.h> 14 15#include "log.h" 16#include "timens.h" 17 18/* 19 * Test shouldn't be run for a day, so add 10 days to child 20 * time and check parent's time to be in the same day. 21 */ 22#define DAY_IN_SEC (60*60*24) 23#define TEN_DAYS_IN_SEC (10*DAY_IN_SEC) 24 25struct test_clock { 26 clockid_t id; 27 char *name; 28 /* 29 * off_id is -1 if a clock has own offset, or it contains an index 30 * which contains a right offset of this clock. 31 */ 32 int off_id; 33 time_t offset; 34}; 35 36#define ct(clock, off_id) { clock, #clock, off_id } 37static struct test_clock clocks[] = { 38 ct(CLOCK_BOOTTIME, -1), 39 ct(CLOCK_BOOTTIME_ALARM, 1), 40 ct(CLOCK_MONOTONIC, -1), 41 ct(CLOCK_MONOTONIC_COARSE, 1), 42 ct(CLOCK_MONOTONIC_RAW, 1), 43}; 44#undef ct 45 46static int child_ns, parent_ns = -1; 47 48static int switch_ns(int fd) 49{ 50 if (setns(fd, CLONE_NEWTIME)) { 51 pr_perror("setns()"); 52 return -1; 53 } 54 55 return 0; 56} 57 58static int init_namespaces(void) 59{ 60 char path[] = "/proc/self/ns/time_for_children"; 61 struct stat st1, st2; 62 63 if (parent_ns == -1) { 64 parent_ns = open(path, O_RDONLY); 65 if (parent_ns <= 0) 66 return pr_perror("Unable to open %s", path); 67 } 68 69 if (fstat(parent_ns, &st1)) 70 return pr_perror("Unable to stat the parent timens"); 71 72 if (unshare_timens()) 73 return -1; 74 75 child_ns = open(path, O_RDONLY); 76 if (child_ns <= 0) 77 return pr_perror("Unable to open %s", path); 78 79 if (fstat(child_ns, &st2)) 80 return pr_perror("Unable to stat the timens"); 81 82 if (st1.st_ino == st2.st_ino) 83 return pr_perror("The same child_ns after CLONE_NEWTIME"); 84 85 return 0; 86} 87 88static int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset) 89{ 90 struct timespec child_ts_new, parent_ts_old, cur_ts; 91 char *entry = raw_syscall ? "syscall" : "vdso"; 92 double precision = 0.0; 93 94 if (check_skip(clocks[clock_index].id)) 95 return 0; 96 97 switch (clocks[clock_index].id) { 98 case CLOCK_MONOTONIC_COARSE: 99 case CLOCK_MONOTONIC_RAW: 100 precision = -2.0; 101 break; 102 } 103 104 if (switch_ns(parent_ns)) 105 return pr_err("switch_ns(%d)", child_ns); 106 107 if (_gettime(clocks[clock_index].id, &parent_ts_old, raw_syscall)) 108 return -1; 109 110 child_ts_new.tv_nsec = parent_ts_old.tv_nsec; 111 child_ts_new.tv_sec = parent_ts_old.tv_sec + offset; 112 113 if (switch_ns(child_ns)) 114 return pr_err("switch_ns(%d)", child_ns); 115 116 if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall)) 117 return -1; 118 119 if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) { 120 ksft_test_result_fail( 121 "Child's %s (%s) time has not changed: %lu -> %lu [%lu]\n", 122 clocks[clock_index].name, entry, parent_ts_old.tv_sec, 123 child_ts_new.tv_sec, cur_ts.tv_sec); 124 return -1; 125 } 126 127 if (switch_ns(parent_ns)) 128 return pr_err("switch_ns(%d)", parent_ns); 129 130 if (_gettime(clocks[clock_index].id, &cur_ts, raw_syscall)) 131 return -1; 132 133 if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) { 134 ksft_test_result_fail( 135 "Parent's %s (%s) time has changed: %lu -> %lu [%lu]\n", 136 clocks[clock_index].name, entry, parent_ts_old.tv_sec, 137 child_ts_new.tv_sec, cur_ts.tv_sec); 138 /* Let's play nice and put it closer to original */ 139 clock_settime(clocks[clock_index].id, &cur_ts); 140 return -1; 141 } 142 143 ksft_test_result_pass("Passed for %s (%s)\n", 144 clocks[clock_index].name, entry); 145 return 0; 146} 147 148int main(int argc, char *argv[]) 149{ 150 unsigned int i; 151 time_t offset; 152 int ret = 0; 153 154 nscheck(); 155 156 check_supported_timers(); 157 158 ksft_set_plan(ARRAY_SIZE(clocks) * 2); 159 160 if (init_namespaces()) 161 return 1; 162 163 /* Offsets have to be set before tasks enter the namespace. */ 164 for (i = 0; i < ARRAY_SIZE(clocks); i++) { 165 if (clocks[i].off_id != -1) 166 continue; 167 offset = TEN_DAYS_IN_SEC + i * 1000; 168 clocks[i].offset = offset; 169 if (_settime(clocks[i].id, offset)) 170 return 1; 171 } 172 173 for (i = 0; i < ARRAY_SIZE(clocks); i++) { 174 if (clocks[i].off_id != -1) 175 offset = clocks[clocks[i].off_id].offset; 176 else 177 offset = clocks[i].offset; 178 ret |= test_gettime(i, true, offset); 179 ret |= test_gettime(i, false, offset); 180 } 181 182 if (ret) 183 ksft_exit_fail(); 184 185 ksft_exit_pass(); 186 return !!ret; 187}