1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3
4 Copyright (c) 2020 Cyril Hrubis <chrubis@suse.cz>
5
6 */
7 /*
8
9 Test that timerfd adds correctly an offset with absolute expiration time.
10
11 After a call to unshare(CLONE_NEWTIME) a new timer namespace is created, the
12 process that has called the unshare() can adjust offsets for CLOCK_MONOTONIC
13 and CLOCK_BOOTTIME for its children by writing to the '/proc/self/timens_offsets'.
14
15 */
16
17 #include <stdlib.h>
18 #include "time64_variants.h"
19 #include "tst_safe_clocks.h"
20 #include "tst_safe_timerfd.h"
21 #include "tst_timer.h"
22 #include "lapi/namespaces_constants.h"
23
24 #define SLEEP_US 40000
25
26 static struct tcase {
27 int clk_id;
28 int clk_off;
29 int off;
30 } tcases[] = {
31 {CLOCK_MONOTONIC, CLOCK_MONOTONIC, 10},
32 {CLOCK_BOOTTIME, CLOCK_BOOTTIME, 10},
33
34 {CLOCK_MONOTONIC, CLOCK_MONOTONIC, -10},
35 {CLOCK_BOOTTIME, CLOCK_BOOTTIME, -10},
36 };
37
38 static struct time64_variants variants[] = {
39 #if (__NR_timerfd_settime != __LTP__NR_INVALID_SYSCALL)
40 { .clock_gettime = sys_clock_gettime, .tfd_settime = sys_timerfd_settime, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
41 #endif
42
43 #if (__NR_timerfd_settime64 != __LTP__NR_INVALID_SYSCALL)
44 { .clock_gettime = sys_clock_gettime64, .tfd_settime = sys_timerfd_settime64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
45 #endif
46 };
47
setup(void)48 static void setup(void)
49 {
50 tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
51 }
52
verify_timerfd(unsigned int n)53 static void verify_timerfd(unsigned int n)
54 {
55 struct time64_variants *tv = &variants[tst_variant];
56 struct tst_ts start, end;
57 struct tst_its it;
58 struct tcase *tc = &tcases[n];
59
60 start.type = end.type = it.type = tv->ts_type;
61 SAFE_UNSHARE(CLONE_NEWTIME);
62
63 SAFE_FILE_PRINTF("/proc/self/timens_offsets", "%d %d 0",
64 tc->clk_off, tc->off);
65
66 if (tv->clock_gettime(tc->clk_id, tst_ts_get(&start))) {
67 tst_res(TFAIL | TERRNO, "clock_gettime(2) failed for clock %s",
68 tst_clock_name(tc->clk_id));
69 return;
70 }
71
72 end = tst_ts_add_us(start, 1000000 * tc->off + SLEEP_US);
73 tst_its_set_interval_sec(&it, 0);
74 tst_its_set_interval_nsec(&it, 0);
75 tst_its_set_value_from_ts(&it, end);
76
77 if (!SAFE_FORK()) {
78 uint64_t exp;
79 int fd = SAFE_TIMERFD_CREATE(tc->clk_id, 0);
80
81 if (tv->tfd_settime(fd, TFD_TIMER_ABSTIME, tst_its_get(&it), NULL)) {
82 tst_res(TFAIL | TERRNO, "timerfd_settime() failed");
83 return;
84 }
85
86 SAFE_READ(1, fd, &exp, sizeof(exp));
87
88 if (exp != 1)
89 tst_res(TFAIL, "Got %llu expirations", (long long unsigned)exp);
90
91 SAFE_CLOSE(fd);
92 exit(0);
93 }
94
95 SAFE_WAIT(NULL);
96
97 if (tv->clock_gettime(CLOCK_MONOTONIC, tst_ts_get(&end))) {
98 tst_res(TFAIL | TERRNO, "clock_gettime(2) failed for clock %s",
99 tst_clock_name(CLOCK_MONOTONIC));
100 return;
101 }
102
103 long long diff = tst_ts_diff_us(end, start);
104
105 if (diff > 5 * SLEEP_US) {
106 tst_res(TFAIL, "timerfd %s slept too long %lli",
107 tst_clock_name(tc->clk_id), diff);
108 return;
109 }
110
111 if (diff < SLEEP_US) {
112 tst_res(TFAIL, "timerfd %s slept too short %lli",
113 tst_clock_name(tc->clk_id), diff);
114 return;
115 }
116
117 tst_res(TPASS, "timerfd %s slept correctly %lli",
118 tst_clock_name(tc->clk_id), diff);
119 }
120
121 static struct tst_test test = {
122 .tcnt = ARRAY_SIZE(tcases),
123 .test = verify_timerfd,
124 .test_variants = ARRAY_SIZE(variants),
125 .setup = setup,
126 .needs_root = 1,
127 .forks_child = 1,
128 .needs_kconfigs = (const char *[]) {
129 "CONFIG_TIME_NS=y",
130 NULL
131 }
132 };
133