1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /****************************************************************************** |
3 | * |
4 | * Copyright © International Business Machines Corp., 2009 |
5 | * |
6 | * DESCRIPTION |
7 | * Block on a futex and wait for timeout. |
8 | * |
9 | * AUTHOR |
10 | * Darren Hart <dvhart@linux.intel.com> |
11 | * |
12 | * HISTORY |
13 | * 2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com> |
14 | * 2021-Apr-26: More test cases by André Almeida <andrealmeid@collabora.com> |
15 | * |
16 | *****************************************************************************/ |
17 | |
18 | #include <pthread.h> |
19 | #include "futextest.h" |
20 | #include "futex2test.h" |
21 | #include "logging.h" |
22 | |
23 | #define TEST_NAME "futex-wait-timeout" |
24 | |
25 | static long timeout_ns = 100000; /* 100us default timeout */ |
26 | static futex_t futex_pi; |
27 | static pthread_barrier_t barrier; |
28 | |
29 | void usage(char *prog) |
30 | { |
31 | printf("Usage: %s\n" , prog); |
32 | printf(" -c Use color\n" ); |
33 | printf(" -h Display this help message\n" ); |
34 | printf(" -t N Timeout in nanoseconds (default: 100,000)\n" ); |
35 | printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n" , |
36 | VQUIET, VCRITICAL, VINFO); |
37 | } |
38 | |
39 | /* |
40 | * Get a PI lock and hold it forever, so the main thread lock_pi will block |
41 | * and we can test the timeout |
42 | */ |
43 | void *get_pi_lock(void *arg) |
44 | { |
45 | int ret; |
46 | volatile futex_t lock = 0; |
47 | |
48 | ret = futex_lock_pi(&futex_pi, NULL, 0, 0); |
49 | if (ret != 0) |
50 | error("futex_lock_pi failed\n" , ret); |
51 | |
52 | pthread_barrier_wait(&barrier); |
53 | |
54 | /* Blocks forever */ |
55 | ret = futex_wait(&lock, 0, NULL, 0); |
56 | error("futex_wait failed\n" , ret); |
57 | |
58 | return NULL; |
59 | } |
60 | |
61 | /* |
62 | * Check if the function returned the expected error |
63 | */ |
64 | static void test_timeout(int res, int *ret, char *test_name, int err) |
65 | { |
66 | if (!res || errno != err) { |
67 | ksft_test_result_fail("%s returned %d\n" , test_name, |
68 | res < 0 ? errno : res); |
69 | *ret = RET_FAIL; |
70 | } else { |
71 | ksft_test_result_pass("%s succeeds\n" , test_name); |
72 | } |
73 | } |
74 | |
75 | /* |
76 | * Calculate absolute timeout and correct overflow |
77 | */ |
78 | static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to, |
79 | long timeout_ns) |
80 | { |
81 | if (clock_gettime(clockid, to)) { |
82 | error("clock_gettime failed\n" , errno); |
83 | return errno; |
84 | } |
85 | |
86 | to->tv_nsec += timeout_ns; |
87 | |
88 | if (to->tv_nsec >= 1000000000) { |
89 | to->tv_sec++; |
90 | to->tv_nsec -= 1000000000; |
91 | } |
92 | |
93 | return 0; |
94 | } |
95 | |
96 | int main(int argc, char *argv[]) |
97 | { |
98 | futex_t f1 = FUTEX_INITIALIZER; |
99 | int res, ret = RET_PASS; |
100 | struct timespec to; |
101 | pthread_t thread; |
102 | int c; |
103 | struct futex_waitv waitv = { |
104 | .uaddr = (uintptr_t)&f1, |
105 | .val = f1, |
106 | .flags = FUTEX_32, |
107 | .__reserved = 0 |
108 | }; |
109 | |
110 | while ((c = getopt(argc, argv, "cht:v:" )) != -1) { |
111 | switch (c) { |
112 | case 'c': |
113 | log_color(1); |
114 | break; |
115 | case 'h': |
116 | usage(prog: basename(argv[0])); |
117 | exit(0); |
118 | case 't': |
119 | timeout_ns = atoi(optarg); |
120 | break; |
121 | case 'v': |
122 | log_verbosity(atoi(optarg)); |
123 | break; |
124 | default: |
125 | usage(prog: basename(argv[0])); |
126 | exit(1); |
127 | } |
128 | } |
129 | |
130 | ksft_print_header(); |
131 | ksft_set_plan(9); |
132 | ksft_print_msg("%s: Block on a futex and wait for timeout\n" , |
133 | basename(argv[0])); |
134 | ksft_print_msg("\tArguments: timeout=%ldns\n" , timeout_ns); |
135 | |
136 | pthread_barrier_init(&barrier, NULL, 2); |
137 | pthread_create(&thread, NULL, get_pi_lock, NULL); |
138 | |
139 | /* initialize relative timeout */ |
140 | to.tv_sec = 0; |
141 | to.tv_nsec = timeout_ns; |
142 | |
143 | res = futex_wait(&f1, f1, &to, 0); |
144 | test_timeout(res, ret: &ret, test_name: "futex_wait relative" , err: ETIMEDOUT); |
145 | |
146 | /* FUTEX_WAIT_BITSET with CLOCK_REALTIME */ |
147 | if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) |
148 | return RET_FAIL; |
149 | res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME); |
150 | test_timeout(res, &ret, "futex_wait_bitset realtime" , ETIMEDOUT); |
151 | |
152 | /* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */ |
153 | if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) |
154 | return RET_FAIL; |
155 | res = futex_wait_bitset(&f1, f1, &to, 1, 0); |
156 | test_timeout(res, &ret, "futex_wait_bitset monotonic" , ETIMEDOUT); |
157 | |
158 | /* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */ |
159 | if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) |
160 | return RET_FAIL; |
161 | res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME); |
162 | test_timeout(res, &ret, "futex_wait_requeue_pi realtime" , ETIMEDOUT); |
163 | |
164 | /* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */ |
165 | if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) |
166 | return RET_FAIL; |
167 | res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0); |
168 | test_timeout(res, &ret, "futex_wait_requeue_pi monotonic" , ETIMEDOUT); |
169 | |
170 | /* Wait until the other thread calls futex_lock_pi() */ |
171 | pthread_barrier_wait(&barrier); |
172 | pthread_barrier_destroy(&barrier); |
173 | /* |
174 | * FUTEX_LOCK_PI with CLOCK_REALTIME |
175 | * Due to historical reasons, FUTEX_LOCK_PI supports only realtime |
176 | * clock, but requires the caller to not set CLOCK_REALTIME flag. |
177 | * |
178 | * If you call FUTEX_LOCK_PI with a monotonic clock, it'll be |
179 | * interpreted as a realtime clock, and (unless you mess your machine's |
180 | * time or your time machine) the monotonic clock value is always |
181 | * smaller than realtime and the syscall will timeout immediately. |
182 | */ |
183 | if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) |
184 | return RET_FAIL; |
185 | res = futex_lock_pi(&futex_pi, &to, 0, 0); |
186 | test_timeout(res, &ret, "futex_lock_pi realtime" , ETIMEDOUT); |
187 | |
188 | /* Test operations that don't support FUTEX_CLOCK_REALTIME */ |
189 | res = futex_lock_pi(&futex_pi, NULL, 0, FUTEX_CLOCK_REALTIME); |
190 | test_timeout(res, &ret, "futex_lock_pi invalid timeout flag" , ENOSYS); |
191 | |
192 | /* futex_waitv with CLOCK_MONOTONIC */ |
193 | if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) |
194 | return RET_FAIL; |
195 | res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC); |
196 | test_timeout(res, &ret, "futex_waitv monotonic" , ETIMEDOUT); |
197 | |
198 | /* futex_waitv with CLOCK_REALTIME */ |
199 | if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) |
200 | return RET_FAIL; |
201 | res = futex_waitv(&waitv, 1, 0, &to, CLOCK_REALTIME); |
202 | test_timeout(res, &ret, "futex_waitv realtime" , ETIMEDOUT); |
203 | |
204 | ksft_print_cnts(); |
205 | return ret; |
206 | } |
207 | |