1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /******************************************************************************
4 * Copyright © International Business Machines Corp., 2006-2008
7 * This test excercises the futex syscall op codes needed for requeuing
8 * priority inheritance aware POSIX condition variables and mutexes.
11 * Sripathi Kodi <sripathik@in.ibm.com>
12 * Darren Hart <dvhart@linux.intel.com>
15 * 2008-Jan-13: Initial version by Sripathi Kodi <sripathik@in.ibm.com>
16 * 2009-Nov-6: futex test adaptation by Darren Hart <dvhart@linux.intel.com>
18 *****************************************************************************/
28 #include "futextest.h"
31 #define TEST_NAME "futex-requeue-pi"
32 #define MAX_WAKE_ITERS 1000
34 #define SIGNAL_PERIOD_US 100
36 atomic_t waiters_blocked
= ATOMIC_INITIALIZER
;
37 atomic_t waiters_woken
= ATOMIC_INITIALIZER
;
39 futex_t f1
= FUTEX_INITIALIZER
;
40 futex_t f2
= FUTEX_INITIALIZER
;
41 futex_t wake_complete
= FUTEX_INITIALIZER
;
43 /* Test option defaults */
44 static long timeout_ns
;
51 struct timespec
*timeout
;
55 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
57 void usage(char *prog
)
59 printf("Usage: %s\n", prog
);
60 printf(" -b Broadcast wakeup (all waiters)\n");
61 printf(" -c Use color\n");
62 printf(" -h Display this help message\n");
63 printf(" -l Lock the pi futex across requeue\n");
64 printf(" -o Use a third party pi futex owner during requeue (cancels -l)\n");
65 printf(" -t N Timeout in nanoseconds (default: 0)\n");
66 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
67 VQUIET
, VCRITICAL
, VINFO
);
70 int create_rt_thread(pthread_t
*pth
, void*(*func
)(void *), void *arg
,
74 struct sched_param schedp
;
77 pthread_attr_init(&attr
);
78 memset(&schedp
, 0, sizeof(schedp
));
80 ret
= pthread_attr_setinheritsched(&attr
, PTHREAD_EXPLICIT_SCHED
);
82 error("pthread_attr_setinheritsched\n", ret
);
86 ret
= pthread_attr_setschedpolicy(&attr
, policy
);
88 error("pthread_attr_setschedpolicy\n", ret
);
92 schedp
.sched_priority
= prio
;
93 ret
= pthread_attr_setschedparam(&attr
, &schedp
);
95 error("pthread_attr_setschedparam\n", ret
);
99 ret
= pthread_create(pth
, &attr
, func
, arg
);
101 error("pthread_create\n", ret
);
108 void *waiterfn(void *arg
)
110 struct thread_arg
*args
= (struct thread_arg
*)arg
;
113 info("Waiter %ld: running\n", args
->id
);
114 /* Each thread sleeps for a different amount of time
115 * This is to avoid races, because we don't lock the
116 * external mutex here */
117 usleep(1000 * (long)args
->id
);
120 atomic_inc(&waiters_blocked
);
121 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
123 args
->ret
= futex_wait_requeue_pi(&f1
, old_val
, &f2
, args
->timeout
,
126 info("waiter %ld woke with %d %s\n", args
->id
, args
->ret
,
127 args
->ret
< 0 ? strerror(errno
) : "");
128 atomic_inc(&waiters_woken
);
130 if (args
->timeout
&& errno
== ETIMEDOUT
)
133 args
->ret
= RET_ERROR
;
134 error("futex_wait_requeue_pi\n", errno
);
136 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
138 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
140 info("Waiter %ld: exiting with %d\n", args
->id
, args
->ret
);
141 pthread_exit((void *)&args
->ret
);
144 void *broadcast_wakerfn(void *arg
)
146 struct thread_arg
*args
= (struct thread_arg
*)arg
;
147 int nr_requeue
= INT_MAX
;
153 info("Waker: waiting for waiters to block\n");
154 while (waiters_blocked
.val
< THREAD_MAX
)
158 info("Waker: Calling broadcast\n");
160 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2
, &f2
);
161 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
165 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
, nr_wake
, nr_requeue
,
168 args
->ret
= RET_ERROR
;
169 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
170 } else if (++i
< MAX_WAKE_ITERS
) {
171 task_count
+= args
->ret
;
172 if (task_count
< THREAD_MAX
- waiters_woken
.val
)
173 goto continue_requeue
;
175 error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
176 0, MAX_WAKE_ITERS
, task_count
, THREAD_MAX
);
177 args
->ret
= RET_ERROR
;
180 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
183 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
186 args
->ret
= task_count
;
188 info("Waker: exiting with %d\n", args
->ret
);
189 pthread_exit((void *)&args
->ret
);
192 void *signal_wakerfn(void *arg
)
194 struct thread_arg
*args
= (struct thread_arg
*)arg
;
195 unsigned int old_val
;
201 info("Waker: waiting for waiters to block\n");
202 while (waiters_blocked
.val
< THREAD_MAX
)
206 while (task_count
< THREAD_MAX
&& waiters_woken
.val
< THREAD_MAX
) {
207 info("task_count: %d, waiters_woken: %d\n",
208 task_count
, waiters_woken
.val
);
210 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
212 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
214 info("Waker: Calling signal\n");
217 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
,
222 info("futex: %x\n", f2
);
224 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
226 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
228 info("futex: %x\n", f2
);
230 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
231 args
->ret
= RET_ERROR
;
235 task_count
+= args
->ret
;
236 usleep(SIGNAL_PERIOD_US
);
238 /* we have to loop at least THREAD_MAX times */
239 if (i
> MAX_WAKE_ITERS
+ THREAD_MAX
) {
240 error("max signaling iterations (%d) reached, giving up on pending waiters.\n",
241 0, MAX_WAKE_ITERS
+ THREAD_MAX
);
242 args
->ret
= RET_ERROR
;
247 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
250 args
->ret
= task_count
;
252 info("Waker: exiting with %d\n", args
->ret
);
253 info("Waker: waiters_woken: %d\n", waiters_woken
.val
);
254 pthread_exit((void *)&args
->ret
);
257 void *third_party_blocker(void *arg
)
259 struct thread_arg
*args
= (struct thread_arg
*)arg
;
262 args
->ret
= futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
265 args
->ret
= futex_wait(&wake_complete
, wake_complete
, NULL
,
267 ret2
= futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
270 if (args
->ret
|| ret2
) {
271 error("third_party_blocker() futex error", 0);
272 args
->ret
= RET_ERROR
;
275 pthread_exit((void *)&args
->ret
);
278 int unit_test(int broadcast
, long lock
, int third_party_owner
, long timeout_ns
)
280 void *(*wakerfn
)(void *) = signal_wakerfn
;
281 struct thread_arg blocker_arg
= THREAD_ARG_INITIALIZER
;
282 struct thread_arg waker_arg
= THREAD_ARG_INITIALIZER
;
283 pthread_t waiter
[THREAD_MAX
], waker
, blocker
;
284 struct timespec ts
, *tsp
= NULL
;
285 struct thread_arg args
[THREAD_MAX
];
287 int i
, ret
= RET_PASS
;
292 info("timeout_ns = %ld\n", timeout_ns
);
293 ret
= clock_gettime(CLOCK_MONOTONIC
, &ts
);
294 secs
= (ts
.tv_nsec
+ timeout_ns
) / 1000000000;
295 ts
.tv_nsec
= ((int64_t)ts
.tv_nsec
+ timeout_ns
) % 1000000000;
297 info("ts.tv_sec = %ld\n", ts
.tv_sec
);
298 info("ts.tv_nsec = %ld\n", ts
.tv_nsec
);
303 wakerfn
= broadcast_wakerfn
;
305 if (third_party_owner
) {
306 if (create_rt_thread(&blocker
, third_party_blocker
,
307 (void *)&blocker_arg
, SCHED_FIFO
, 1)) {
308 error("Creating third party blocker thread failed\n",
315 atomic_set(&waiters_woken
, 0);
316 for (i
= 0; i
< THREAD_MAX
; i
++) {
318 args
[i
].timeout
= tsp
;
319 info("Starting thread %d\n", i
);
320 if (create_rt_thread(&waiter
[i
], waiterfn
, (void *)&args
[i
],
322 error("Creating waiting thread failed\n", errno
);
327 waker_arg
.lock
= lock
;
328 if (create_rt_thread(&waker
, wakerfn
, (void *)&waker_arg
,
330 error("Creating waker thread failed\n", errno
);
335 /* Wait for threads to finish */
336 /* Store the first error or failure encountered in waiter_ret */
337 waiter_ret
= &args
[0].ret
;
338 for (i
= 0; i
< THREAD_MAX
; i
++)
339 pthread_join(waiter
[i
],
340 *waiter_ret
? NULL
: (void **)&waiter_ret
);
342 if (third_party_owner
)
343 pthread_join(blocker
, NULL
);
344 pthread_join(waker
, NULL
);
350 else if (waker_arg
.ret
< 0)
352 else if (blocker_arg
.ret
)
353 ret
= blocker_arg
.ret
;
359 int main(int argc
, char *argv
[])
363 while ((c
= getopt(argc
, argv
, "bchlot:v:")) != -1) {
372 usage(basename(argv
[0]));
382 timeout_ns
= atoi(optarg
);
385 log_verbosity(atoi(optarg
));
388 usage(basename(argv
[0]));
395 ksft_print_msg("%s: Test requeue functionality\n", basename(argv
[0]));
397 "\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
398 broadcast
, locked
, owner
, timeout_ns
);
401 * FIXME: unit_test is obsolete now that we parse options and the
402 * various style of runs are done by run.sh - simplify the code and move
403 * unit_test into main()
405 ret
= unit_test(broadcast
, locked
, owner
, timeout_ns
);
407 print_result(TEST_NAME
, ret
);