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 *****************************************************************************/
30 #include "futextest.h"
33 #define TEST_NAME "futex-requeue-pi"
34 #define MAX_WAKE_ITERS 1000
36 #define SIGNAL_PERIOD_US 100
38 atomic_t waiters_blocked
= ATOMIC_INITIALIZER
;
39 atomic_t waiters_woken
= ATOMIC_INITIALIZER
;
41 futex_t f1
= FUTEX_INITIALIZER
;
42 futex_t f2
= FUTEX_INITIALIZER
;
43 futex_t wake_complete
= FUTEX_INITIALIZER
;
45 /* Test option defaults */
46 static long timeout_ns
;
53 struct timespec
*timeout
;
57 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
59 void usage(char *prog
)
61 printf("Usage: %s\n", prog
);
62 printf(" -b Broadcast wakeup (all waiters)\n");
63 printf(" -c Use color\n");
64 printf(" -h Display this help message\n");
65 printf(" -l Lock the pi futex across requeue\n");
66 printf(" -o Use a third party pi futex owner during requeue (cancels -l)\n");
67 printf(" -t N Timeout in nanoseconds (default: 0)\n");
68 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
69 VQUIET
, VCRITICAL
, VINFO
);
72 int create_rt_thread(pthread_t
*pth
, void*(*func
)(void *), void *arg
,
76 struct sched_param schedp
;
79 pthread_attr_init(&attr
);
80 memset(&schedp
, 0, sizeof(schedp
));
82 ret
= pthread_attr_setinheritsched(&attr
, PTHREAD_EXPLICIT_SCHED
);
84 error("pthread_attr_setinheritsched\n", ret
);
88 ret
= pthread_attr_setschedpolicy(&attr
, policy
);
90 error("pthread_attr_setschedpolicy\n", ret
);
94 schedp
.sched_priority
= prio
;
95 ret
= pthread_attr_setschedparam(&attr
, &schedp
);
97 error("pthread_attr_setschedparam\n", ret
);
101 ret
= pthread_create(pth
, &attr
, func
, arg
);
103 error("pthread_create\n", ret
);
110 void *waiterfn(void *arg
)
112 struct thread_arg
*args
= (struct thread_arg
*)arg
;
115 info("Waiter %ld: running\n", args
->id
);
116 /* Each thread sleeps for a different amount of time
117 * This is to avoid races, because we don't lock the
118 * external mutex here */
119 usleep(1000 * (long)args
->id
);
122 atomic_inc(&waiters_blocked
);
123 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
125 args
->ret
= futex_wait_requeue_pi(&f1
, old_val
, &f2
, args
->timeout
,
128 info("waiter %ld woke with %d %s\n", args
->id
, args
->ret
,
129 args
->ret
< 0 ? strerror(errno
) : "");
130 atomic_inc(&waiters_woken
);
132 if (args
->timeout
&& errno
== ETIMEDOUT
)
135 args
->ret
= RET_ERROR
;
136 error("futex_wait_requeue_pi\n", errno
);
138 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
140 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
142 info("Waiter %ld: exiting with %d\n", args
->id
, args
->ret
);
143 pthread_exit((void *)&args
->ret
);
146 void *broadcast_wakerfn(void *arg
)
148 struct thread_arg
*args
= (struct thread_arg
*)arg
;
149 int nr_requeue
= INT_MAX
;
155 info("Waker: waiting for waiters to block\n");
156 while (waiters_blocked
.val
< THREAD_MAX
)
160 info("Waker: Calling broadcast\n");
162 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2
, &f2
);
163 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
167 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
, nr_wake
, nr_requeue
,
170 args
->ret
= RET_ERROR
;
171 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
172 } else if (++i
< MAX_WAKE_ITERS
) {
173 task_count
+= args
->ret
;
174 if (task_count
< THREAD_MAX
- waiters_woken
.val
)
175 goto continue_requeue
;
177 error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
178 0, MAX_WAKE_ITERS
, task_count
, THREAD_MAX
);
179 args
->ret
= RET_ERROR
;
182 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
185 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
188 args
->ret
= task_count
;
190 info("Waker: exiting with %d\n", args
->ret
);
191 pthread_exit((void *)&args
->ret
);
194 void *signal_wakerfn(void *arg
)
196 struct thread_arg
*args
= (struct thread_arg
*)arg
;
197 unsigned int old_val
;
203 info("Waker: waiting for waiters to block\n");
204 while (waiters_blocked
.val
< THREAD_MAX
)
208 while (task_count
< THREAD_MAX
&& waiters_woken
.val
< THREAD_MAX
) {
209 info("task_count: %d, waiters_woken: %d\n",
210 task_count
, waiters_woken
.val
);
212 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
214 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
216 info("Waker: Calling signal\n");
219 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
,
224 info("futex: %x\n", f2
);
226 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
228 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
230 info("futex: %x\n", f2
);
232 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
233 args
->ret
= RET_ERROR
;
237 task_count
+= args
->ret
;
238 usleep(SIGNAL_PERIOD_US
);
240 /* we have to loop at least THREAD_MAX times */
241 if (i
> MAX_WAKE_ITERS
+ THREAD_MAX
) {
242 error("max signaling iterations (%d) reached, giving up on pending waiters.\n",
243 0, MAX_WAKE_ITERS
+ THREAD_MAX
);
244 args
->ret
= RET_ERROR
;
249 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
252 args
->ret
= task_count
;
254 info("Waker: exiting with %d\n", args
->ret
);
255 info("Waker: waiters_woken: %d\n", waiters_woken
.val
);
256 pthread_exit((void *)&args
->ret
);
259 void *third_party_blocker(void *arg
)
261 struct thread_arg
*args
= (struct thread_arg
*)arg
;
264 args
->ret
= futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
267 args
->ret
= futex_wait(&wake_complete
, wake_complete
, NULL
,
269 ret2
= futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
272 if (args
->ret
|| ret2
) {
273 error("third_party_blocker() futex error", 0);
274 args
->ret
= RET_ERROR
;
277 pthread_exit((void *)&args
->ret
);
280 int unit_test(int broadcast
, long lock
, int third_party_owner
, long timeout_ns
)
282 void *(*wakerfn
)(void *) = signal_wakerfn
;
283 struct thread_arg blocker_arg
= THREAD_ARG_INITIALIZER
;
284 struct thread_arg waker_arg
= THREAD_ARG_INITIALIZER
;
285 pthread_t waiter
[THREAD_MAX
], waker
, blocker
;
286 struct timespec ts
, *tsp
= NULL
;
287 struct thread_arg args
[THREAD_MAX
];
289 int i
, ret
= RET_PASS
;
294 info("timeout_ns = %ld\n", timeout_ns
);
295 ret
= clock_gettime(CLOCK_MONOTONIC
, &ts
);
296 secs
= (ts
.tv_nsec
+ timeout_ns
) / 1000000000;
297 ts
.tv_nsec
= ((int64_t)ts
.tv_nsec
+ timeout_ns
) % 1000000000;
299 info("ts.tv_sec = %ld\n", ts
.tv_sec
);
300 info("ts.tv_nsec = %ld\n", ts
.tv_nsec
);
305 wakerfn
= broadcast_wakerfn
;
307 if (third_party_owner
) {
308 if (create_rt_thread(&blocker
, third_party_blocker
,
309 (void *)&blocker_arg
, SCHED_FIFO
, 1)) {
310 error("Creating third party blocker thread failed\n",
317 atomic_set(&waiters_woken
, 0);
318 for (i
= 0; i
< THREAD_MAX
; i
++) {
320 args
[i
].timeout
= tsp
;
321 info("Starting thread %d\n", i
);
322 if (create_rt_thread(&waiter
[i
], waiterfn
, (void *)&args
[i
],
324 error("Creating waiting thread failed\n", errno
);
329 waker_arg
.lock
= lock
;
330 if (create_rt_thread(&waker
, wakerfn
, (void *)&waker_arg
,
332 error("Creating waker thread failed\n", errno
);
337 /* Wait for threads to finish */
338 /* Store the first error or failure encountered in waiter_ret */
339 waiter_ret
= &args
[0].ret
;
340 for (i
= 0; i
< THREAD_MAX
; i
++)
341 pthread_join(waiter
[i
],
342 *waiter_ret
? NULL
: (void **)&waiter_ret
);
344 if (third_party_owner
)
345 pthread_join(blocker
, NULL
);
346 pthread_join(waker
, NULL
);
352 else if (waker_arg
.ret
< 0)
354 else if (blocker_arg
.ret
)
355 ret
= blocker_arg
.ret
;
361 int main(int argc
, char *argv
[])
366 while ((c
= getopt(argc
, argv
, "bchlot:v:")) != -1) {
375 usage(basename(argv
[0]));
385 timeout_ns
= atoi(optarg
);
388 log_verbosity(atoi(optarg
));
391 usage(basename(argv
[0]));
398 ksft_print_msg("%s: Test requeue functionality\n", basename(argv
[0]));
400 "\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
401 broadcast
, locked
, owner
, timeout_ns
);
403 ret
= asprintf(&test_name
,
404 "%s broadcast=%d locked=%d owner=%d timeout=%ldns",
405 TEST_NAME
, broadcast
, locked
, owner
, timeout_ns
);
407 ksft_print_msg("Failed to generate test name\n");
408 test_name
= TEST_NAME
;
412 * FIXME: unit_test is obsolete now that we parse options and the
413 * various style of runs are done by run.sh - simplify the code and move
414 * unit_test into main()
416 ret
= unit_test(broadcast
, locked
, owner
, timeout_ns
);
418 print_result(test_name
, ret
);