1// SPDX-License-Identifier: GPL-2.0
2#define _GNU_SOURCE
3#include <sched.h>
4
5#include <linux/unistd.h>
6#include <linux/futex.h>
7#include <stdio.h>
8#include <string.h>
9#include <sys/syscall.h>
10#include <sys/types.h>
11#include <sys/wait.h>
12#include <time.h>
13#include <unistd.h>
14
15#include "log.h"
16#include "timens.h"
17
18#define NSEC_PER_SEC 1000000000ULL
19
20static int run_test(int clockid)
21{
22	int futex_op = FUTEX_WAIT_BITSET;
23	struct timespec timeout, end;
24	int val = 0;
25
26	if (clockid == CLOCK_REALTIME)
27		futex_op |= FUTEX_CLOCK_REALTIME;
28
29	clock_gettime(clockid, &timeout);
30	timeout.tv_nsec += NSEC_PER_SEC / 10; // 100ms
31	if (timeout.tv_nsec > NSEC_PER_SEC) {
32		timeout.tv_sec++;
33		timeout.tv_nsec -= NSEC_PER_SEC;
34	}
35
36	if (syscall(__NR_futex, &val, futex_op, 0,
37		    &timeout, 0, FUTEX_BITSET_MATCH_ANY) >= 0) {
38		ksft_test_result_fail("futex didn't return ETIMEDOUT\n");
39		return 1;
40	}
41
42	if (errno != ETIMEDOUT) {
43		ksft_test_result_fail("futex didn't return ETIMEDOUT: %s\n",
44							strerror(errno));
45		return 1;
46	}
47
48	clock_gettime(clockid, &end);
49
50	if (end.tv_sec < timeout.tv_sec ||
51	    (end.tv_sec == timeout.tv_sec && end.tv_nsec < timeout.tv_nsec)) {
52		ksft_test_result_fail("futex slept less than 100ms\n");
53		return 1;
54	}
55
56
57	ksft_test_result_pass("futex with the %d clockid\n", clockid);
58
59	return 0;
60}
61
62int main(int argc, char *argv[])
63{
64	int status, len, fd;
65	char buf[4096];
66	pid_t pid;
67	struct timespec mtime_now;
68
69	nscheck();
70
71	ksft_set_plan(2);
72
73	clock_gettime(CLOCK_MONOTONIC, &mtime_now);
74
75	if (unshare_timens())
76		return 1;
77
78	len = snprintf(buf, sizeof(buf), "%d %d 0",
79			CLOCK_MONOTONIC, 70 * 24 * 3600);
80	fd = open("/proc/self/timens_offsets", O_WRONLY);
81	if (fd < 0)
82		return pr_perror("/proc/self/timens_offsets");
83
84	if (write(fd, buf, len) != len)
85		return pr_perror("/proc/self/timens_offsets");
86
87	close(fd);
88
89	pid = fork();
90	if (pid < 0)
91		return pr_perror("Unable to fork");
92	if (pid == 0) {
93		int ret = 0;
94
95		ret |= run_test(CLOCK_REALTIME);
96		ret |= run_test(CLOCK_MONOTONIC);
97		if (ret)
98			ksft_exit_fail();
99		ksft_exit_pass();
100		return 0;
101	}
102
103	if (waitpid(pid, &status, 0) != pid)
104		return pr_perror("Unable to wait the child process");
105
106	if (WIFEXITED(status))
107		return WEXITSTATUS(status);
108
109	return 1;
110}
111