1 /******************************************************************************
3 * Copyright © International Business Machines Corp., 2006-2008
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
11 * This test excercises the futex syscall op codes needed for requeuing
12 * priority inheritance aware POSIX condition variables and mutexes.
15 * Sripathi Kodi <sripathik@in.ibm.com>
16 * Darren Hart <dvhart@linux.intel.com>
19 * 2008-Jan-13: Initial version by Sripathi Kodi <sripathik@in.ibm.com>
20 * 2009-Nov-6: futex test adaptation by Darren Hart <dvhart@linux.intel.com>
22 *****************************************************************************/
32 #include "futextest.h"
35 #define TEST_NAME "futex-requeue-pi"
36 #define MAX_WAKE_ITERS 1000
38 #define SIGNAL_PERIOD_US 100
40 atomic_t waiters_blocked
= ATOMIC_INITIALIZER
;
41 atomic_t waiters_woken
= ATOMIC_INITIALIZER
;
43 futex_t f1
= FUTEX_INITIALIZER
;
44 futex_t f2
= FUTEX_INITIALIZER
;
45 futex_t wake_complete
= FUTEX_INITIALIZER
;
47 /* Test option defaults */
48 static long timeout_ns
;
55 struct timespec
*timeout
;
59 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
61 void usage(char *prog
)
63 printf("Usage: %s\n", prog
);
64 printf(" -b Broadcast wakeup (all waiters)\n");
65 printf(" -c Use color\n");
66 printf(" -h Display this help message\n");
67 printf(" -l Lock the pi futex across requeue\n");
68 printf(" -o Use a third party pi futex owner during requeue (cancels -l)\n");
69 printf(" -t N Timeout in nanoseconds (default: 0)\n");
70 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
71 VQUIET
, VCRITICAL
, VINFO
);
74 int create_rt_thread(pthread_t
*pth
, void*(*func
)(void *), void *arg
,
78 struct sched_param schedp
;
81 pthread_attr_init(&attr
);
82 memset(&schedp
, 0, sizeof(schedp
));
84 ret
= pthread_attr_setinheritsched(&attr
, PTHREAD_EXPLICIT_SCHED
);
86 error("pthread_attr_setinheritsched\n", ret
);
90 ret
= pthread_attr_setschedpolicy(&attr
, policy
);
92 error("pthread_attr_setschedpolicy\n", ret
);
96 schedp
.sched_priority
= prio
;
97 ret
= pthread_attr_setschedparam(&attr
, &schedp
);
99 error("pthread_attr_setschedparam\n", ret
);
103 ret
= pthread_create(pth
, &attr
, func
, arg
);
105 error("pthread_create\n", ret
);
112 void *waiterfn(void *arg
)
114 struct thread_arg
*args
= (struct thread_arg
*)arg
;
117 info("Waiter %ld: running\n", args
->id
);
118 /* Each thread sleeps for a different amount of time
119 * This is to avoid races, because we don't lock the
120 * external mutex here */
121 usleep(1000 * (long)args
->id
);
124 atomic_inc(&waiters_blocked
);
125 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
127 args
->ret
= futex_wait_requeue_pi(&f1
, old_val
, &f2
, args
->timeout
,
130 info("waiter %ld woke with %d %s\n", args
->id
, args
->ret
,
131 args
->ret
< 0 ? strerror(errno
) : "");
132 atomic_inc(&waiters_woken
);
134 if (args
->timeout
&& errno
== ETIMEDOUT
)
137 args
->ret
= RET_ERROR
;
138 error("futex_wait_requeue_pi\n", errno
);
140 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
142 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
144 info("Waiter %ld: exiting with %d\n", args
->id
, args
->ret
);
145 pthread_exit((void *)&args
->ret
);
148 void *broadcast_wakerfn(void *arg
)
150 struct thread_arg
*args
= (struct thread_arg
*)arg
;
151 int nr_requeue
= INT_MAX
;
157 info("Waker: waiting for waiters to block\n");
158 while (waiters_blocked
.val
< THREAD_MAX
)
162 info("Waker: Calling broadcast\n");
164 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2
, &f2
);
165 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
169 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
, nr_wake
, nr_requeue
,
172 args
->ret
= RET_ERROR
;
173 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
174 } else if (++i
< MAX_WAKE_ITERS
) {
175 task_count
+= args
->ret
;
176 if (task_count
< THREAD_MAX
- waiters_woken
.val
)
177 goto continue_requeue
;
179 error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
180 0, MAX_WAKE_ITERS
, task_count
, THREAD_MAX
);
181 args
->ret
= RET_ERROR
;
184 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
187 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
190 args
->ret
= task_count
;
192 info("Waker: exiting with %d\n", args
->ret
);
193 pthread_exit((void *)&args
->ret
);
196 void *signal_wakerfn(void *arg
)
198 struct thread_arg
*args
= (struct thread_arg
*)arg
;
199 unsigned int old_val
;
205 info("Waker: waiting for waiters to block\n");
206 while (waiters_blocked
.val
< THREAD_MAX
)
210 while (task_count
< THREAD_MAX
&& waiters_woken
.val
< THREAD_MAX
) {
211 info("task_count: %d, waiters_woken: %d\n",
212 task_count
, waiters_woken
.val
);
214 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
216 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
218 info("Waker: Calling signal\n");
221 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
,
226 info("futex: %x\n", f2
);
228 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
230 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
232 info("futex: %x\n", f2
);
234 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
235 args
->ret
= RET_ERROR
;
239 task_count
+= args
->ret
;
240 usleep(SIGNAL_PERIOD_US
);
242 /* we have to loop at least THREAD_MAX times */
243 if (i
> MAX_WAKE_ITERS
+ THREAD_MAX
) {
244 error("max signaling iterations (%d) reached, giving up on pending waiters.\n",
245 0, MAX_WAKE_ITERS
+ THREAD_MAX
);
246 args
->ret
= RET_ERROR
;
251 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
254 args
->ret
= task_count
;
256 info("Waker: exiting with %d\n", args
->ret
);
257 info("Waker: waiters_woken: %d\n", waiters_woken
.val
);
258 pthread_exit((void *)&args
->ret
);
261 void *third_party_blocker(void *arg
)
263 struct thread_arg
*args
= (struct thread_arg
*)arg
;
266 args
->ret
= futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
269 args
->ret
= futex_wait(&wake_complete
, wake_complete
, NULL
,
271 ret2
= futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
274 if (args
->ret
|| ret2
) {
275 error("third_party_blocker() futex error", 0);
276 args
->ret
= RET_ERROR
;
279 pthread_exit((void *)&args
->ret
);
282 int unit_test(int broadcast
, long lock
, int third_party_owner
, long timeout_ns
)
284 void *(*wakerfn
)(void *) = signal_wakerfn
;
285 struct thread_arg blocker_arg
= THREAD_ARG_INITIALIZER
;
286 struct thread_arg waker_arg
= THREAD_ARG_INITIALIZER
;
287 pthread_t waiter
[THREAD_MAX
], waker
, blocker
;
288 struct timespec ts
, *tsp
= NULL
;
289 struct thread_arg args
[THREAD_MAX
];
291 int i
, ret
= RET_PASS
;
296 info("timeout_ns = %ld\n", timeout_ns
);
297 ret
= clock_gettime(CLOCK_MONOTONIC
, &ts
);
298 secs
= (ts
.tv_nsec
+ timeout_ns
) / 1000000000;
299 ts
.tv_nsec
= ((int64_t)ts
.tv_nsec
+ timeout_ns
) % 1000000000;
301 info("ts.tv_sec = %ld\n", ts
.tv_sec
);
302 info("ts.tv_nsec = %ld\n", ts
.tv_nsec
);
307 wakerfn
= broadcast_wakerfn
;
309 if (third_party_owner
) {
310 if (create_rt_thread(&blocker
, third_party_blocker
,
311 (void *)&blocker_arg
, SCHED_FIFO
, 1)) {
312 error("Creating third party blocker thread failed\n",
319 atomic_set(&waiters_woken
, 0);
320 for (i
= 0; i
< THREAD_MAX
; i
++) {
322 args
[i
].timeout
= tsp
;
323 info("Starting thread %d\n", i
);
324 if (create_rt_thread(&waiter
[i
], waiterfn
, (void *)&args
[i
],
326 error("Creating waiting thread failed\n", errno
);
331 waker_arg
.lock
= lock
;
332 if (create_rt_thread(&waker
, wakerfn
, (void *)&waker_arg
,
334 error("Creating waker thread failed\n", errno
);
339 /* Wait for threads to finish */
340 /* Store the first error or failure encountered in waiter_ret */
341 waiter_ret
= &args
[0].ret
;
342 for (i
= 0; i
< THREAD_MAX
; i
++)
343 pthread_join(waiter
[i
],
344 *waiter_ret
? NULL
: (void **)&waiter_ret
);
346 if (third_party_owner
)
347 pthread_join(blocker
, NULL
);
348 pthread_join(waker
, NULL
);
354 else if (waker_arg
.ret
< 0)
356 else if (blocker_arg
.ret
)
357 ret
= blocker_arg
.ret
;
363 int main(int argc
, char *argv
[])
367 while ((c
= getopt(argc
, argv
, "bchlot:v:")) != -1) {
376 usage(basename(argv
[0]));
386 timeout_ns
= atoi(optarg
);
389 log_verbosity(atoi(optarg
));
392 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
);
404 * FIXME: unit_test is obsolete now that we parse options and the
405 * various style of runs are done by run.sh - simplify the code and move
406 * unit_test into main()
408 ret
= unit_test(broadcast
, locked
, owner
, timeout_ns
);
410 print_result(TEST_NAME
, ret
);