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