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
20 #include "futextest.h"
21 #include "futex2test.h"
22 #include "../../kselftest_harness.h"
23
24 static long timeout_ns = 100000; /* 100us default timeout */
25 static futex_t futex_pi;
26 static pthread_barrier_t barrier;
27
28 /*
29 * Get a PI lock and hold it forever, so the main thread lock_pi will block
30 * and we can test the timeout
31 */
get_pi_lock(void * arg)32 void *get_pi_lock(void *arg)
33 {
34 int ret;
35 volatile futex_t lock = 0;
36
37 ret = futex_lock_pi(&futex_pi, NULL, 0, 0);
38 if (ret != 0)
39 ksft_exit_fail_msg("futex_lock_pi failed\n");
40
41 pthread_barrier_wait(&barrier);
42
43 /* Blocks forever */
44 ret = futex_wait(&lock, 0, NULL, 0);
45 ksft_exit_fail_msg("futex_wait failed\n");
46
47 return NULL;
48 }
49
50 /*
51 * Check if the function returned the expected error
52 */
test_timeout(int res,char * test_name,int err)53 static void test_timeout(int res, char *test_name, int err)
54 {
55 if (!res || errno != err) {
56 ksft_test_result_fail("%s returned %d\n", test_name,
57 res < 0 ? errno : res);
58 } else {
59 ksft_test_result_pass("%s succeeds\n", test_name);
60 }
61 }
62
63 /*
64 * Calculate absolute timeout and correct overflow
65 */
futex_get_abs_timeout(clockid_t clockid,struct timespec * to,long timeout_ns)66 static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
67 long timeout_ns)
68 {
69 if (clock_gettime(clockid, to))
70 ksft_exit_fail_msg("clock_gettime failed\n");
71
72 to->tv_nsec += timeout_ns;
73
74 if (to->tv_nsec >= 1000000000) {
75 to->tv_sec++;
76 to->tv_nsec -= 1000000000;
77 }
78
79 return 0;
80 }
81
TEST(wait_bitset)82 TEST(wait_bitset)
83 {
84 futex_t f1 = FUTEX_INITIALIZER;
85 struct timespec to;
86 int res;
87
88 /* initialize relative timeout */
89 to.tv_sec = 0;
90 to.tv_nsec = timeout_ns;
91
92 res = futex_wait(&f1, f1, &to, 0);
93 test_timeout(res, "futex_wait relative", ETIMEDOUT);
94
95 /* FUTEX_WAIT_BITSET with CLOCK_REALTIME */
96 if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
97 ksft_test_result_error("get_time error");
98 res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME);
99 test_timeout(res, "futex_wait_bitset realtime", ETIMEDOUT);
100
101 /* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */
102 if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
103 ksft_test_result_error("get_time error");
104 res = futex_wait_bitset(&f1, f1, &to, 1, 0);
105 test_timeout(res, "futex_wait_bitset monotonic", ETIMEDOUT);
106 }
107
TEST(requeue_pi)108 TEST(requeue_pi)
109 {
110 futex_t f1 = FUTEX_INITIALIZER;
111 struct timespec to;
112 int res;
113
114 /* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */
115 if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
116 ksft_test_result_error("get_time error");
117 res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME);
118 test_timeout(res, "futex_wait_requeue_pi realtime", ETIMEDOUT);
119
120 /* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */
121 if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
122 ksft_test_result_error("get_time error");
123 res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0);
124 test_timeout(res, "futex_wait_requeue_pi monotonic", ETIMEDOUT);
125
126 }
127
TEST(lock_pi)128 TEST(lock_pi)
129 {
130 struct timespec to;
131 pthread_t thread;
132 int res;
133
134 /* Create a thread that will lock forever so any waiter will timeout */
135 pthread_barrier_init(&barrier, NULL, 2);
136 pthread_create(&thread, NULL, get_pi_lock, NULL);
137
138 /* Wait until the other thread calls futex_lock_pi() */
139 pthread_barrier_wait(&barrier);
140 pthread_barrier_destroy(&barrier);
141
142 /*
143 * FUTEX_LOCK_PI with CLOCK_REALTIME
144 * Due to historical reasons, FUTEX_LOCK_PI supports only realtime
145 * clock, but requires the caller to not set CLOCK_REALTIME flag.
146 *
147 * If you call FUTEX_LOCK_PI with a monotonic clock, it'll be
148 * interpreted as a realtime clock, and (unless you mess your machine's
149 * time or your time machine) the monotonic clock value is always
150 * smaller than realtime and the syscall will timeout immediately.
151 */
152 if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
153 ksft_test_result_error("get_time error");
154 res = futex_lock_pi(&futex_pi, &to, 0, 0);
155 test_timeout(res, "futex_lock_pi realtime", ETIMEDOUT);
156
157 /* Test operations that don't support FUTEX_CLOCK_REALTIME */
158 res = futex_lock_pi(&futex_pi, NULL, 0, FUTEX_CLOCK_REALTIME);
159 test_timeout(res, "futex_lock_pi invalid timeout flag", ENOSYS);
160 }
161
TEST(waitv)162 TEST(waitv)
163 {
164 futex_t f1 = FUTEX_INITIALIZER;
165 struct futex_waitv waitv = {
166 .uaddr = (uintptr_t)&f1,
167 .val = f1,
168 .flags = FUTEX_32,
169 .__reserved = 0,
170 };
171 struct timespec to;
172 int res;
173
174 /* futex_waitv with CLOCK_MONOTONIC */
175 if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
176 ksft_test_result_error("get_time error");
177 res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC);
178 test_timeout(res, "futex_waitv monotonic", ETIMEDOUT);
179
180 /* futex_waitv with CLOCK_REALTIME */
181 if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
182 ksft_test_result_error("get_time error");
183 res = futex_waitv(&waitv, 1, 0, &to, CLOCK_REALTIME);
184 test_timeout(res, "futex_waitv realtime", ETIMEDOUT);
185 }
186
187 TEST_HARNESS_MAIN
188