1 /****************************************************************************** 2 * 3 * Copyright © International Business Machines Corp., 2006-2008 4 * 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. 9 * 10 * DESCRIPTION 11 * This test excercises the futex syscall op codes needed for requeuing 12 * priority inheritance aware POSIX condition variables and mutexes. 13 * 14 * AUTHORS 15 * Sripathi Kodi <sripathik@in.ibm.com> 16 * Darren Hart <dvhart@linux.intel.com> 17 * 18 * HISTORY 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> 21 * 22 *****************************************************************************/ 23 24 #include <errno.h> 25 #include <limits.h> 26 #include <pthread.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <signal.h> 30 #include <string.h> 31 #include "atomic.h" 32 #include "futextest.h" 33 #include "logging.h" 34 35 #define TEST_NAME "futex-requeue-pi" 36 #define MAX_WAKE_ITERS 1000 37 #define THREAD_MAX 10 38 #define SIGNAL_PERIOD_US 100 39 40 atomic_t waiters_blocked = ATOMIC_INITIALIZER; 41 atomic_t waiters_woken = ATOMIC_INITIALIZER; 42 43 futex_t f1 = FUTEX_INITIALIZER; 44 futex_t f2 = FUTEX_INITIALIZER; 45 futex_t wake_complete = FUTEX_INITIALIZER; 46 47 /* Test option defaults */ 48 static long timeout_ns; 49 static int broadcast; 50 static int owner; 51 static int locked; 52 53 struct thread_arg { 54 long id; 55 struct timespec *timeout; 56 int lock; 57 int ret; 58 }; 59 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 } 60 61 void usage(char *prog) 62 { 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); 72 } 73 74 int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, 75 int policy, int prio) 76 { 77 int ret; 78 struct sched_param schedp; 79 pthread_attr_t attr; 80 81 pthread_attr_init(&attr); 82 memset(&schedp, 0, sizeof(schedp)); 83 84 ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); 85 if (ret) { 86 error("pthread_attr_setinheritsched\n", ret); 87 return -1; 88 } 89 90 ret = pthread_attr_setschedpolicy(&attr, policy); 91 if (ret) { 92 error("pthread_attr_setschedpolicy\n", ret); 93 return -1; 94 } 95 96 schedp.sched_priority = prio; 97 ret = pthread_attr_setschedparam(&attr, &schedp); 98 if (ret) { 99 error("pthread_attr_setschedparam\n", ret); 100 return -1; 101 } 102 103 ret = pthread_create(pth, &attr, func, arg); 104 if (ret) { 105 error("pthread_create\n", ret); 106 return -1; 107 } 108 return 0; 109 } 110 111 112 void *waiterfn(void *arg) 113 { 114 struct thread_arg *args = (struct thread_arg *)arg; 115 futex_t old_val; 116 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); 122 123 old_val = f1; 124 atomic_inc(&waiters_blocked); 125 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n", 126 &f1, f1, &f2); 127 args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout, 128 FUTEX_PRIVATE_FLAG); 129 130 info("waiter %ld woke with %d %s\n", args->id, args->ret, 131 args->ret < 0 ? strerror(errno) : ""); 132 atomic_inc(&waiters_woken); 133 if (args->ret < 0) { 134 if (args->timeout && errno == ETIMEDOUT) 135 args->ret = 0; 136 else { 137 args->ret = RET_ERROR; 138 error("futex_wait_requeue_pi\n", errno); 139 } 140 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 141 } 142 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 143 144 info("Waiter %ld: exiting with %d\n", args->id, args->ret); 145 pthread_exit((void *)&args->ret); 146 } 147 148 void *broadcast_wakerfn(void *arg) 149 { 150 struct thread_arg *args = (struct thread_arg *)arg; 151 int nr_requeue = INT_MAX; 152 int task_count = 0; 153 futex_t old_val; 154 int nr_wake = 1; 155 int i = 0; 156 157 info("Waker: waiting for waiters to block\n"); 158 while (waiters_blocked.val < THREAD_MAX) 159 usleep(1000); 160 usleep(1000); 161 162 info("Waker: Calling broadcast\n"); 163 if (args->lock) { 164 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); 165 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 166 } 167 continue_requeue: 168 old_val = f1; 169 args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, 170 FUTEX_PRIVATE_FLAG); 171 if (args->ret < 0) { 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; 178 } else { 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; 182 } 183 184 futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); 185 186 if (args->lock) 187 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 188 189 if (args->ret > 0) 190 args->ret = task_count; 191 192 info("Waker: exiting with %d\n", args->ret); 193 pthread_exit((void *)&args->ret); 194 } 195 196 void *signal_wakerfn(void *arg) 197 { 198 struct thread_arg *args = (struct thread_arg *)arg; 199 unsigned int old_val; 200 int nr_requeue = 0; 201 int task_count = 0; 202 int nr_wake = 1; 203 int i = 0; 204 205 info("Waker: waiting for waiters to block\n"); 206 while (waiters_blocked.val < THREAD_MAX) 207 usleep(1000); 208 usleep(1000); 209 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); 213 if (args->lock) { 214 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", 215 f2, &f2); 216 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 217 } 218 info("Waker: Calling signal\n"); 219 /* cond_signal */ 220 old_val = f1; 221 args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, 222 nr_wake, nr_requeue, 223 FUTEX_PRIVATE_FLAG); 224 if (args->ret < 0) 225 args->ret = -errno; 226 info("futex: %x\n", f2); 227 if (args->lock) { 228 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", 229 f2, &f2); 230 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 231 } 232 info("futex: %x\n", f2); 233 if (args->ret < 0) { 234 error("FUTEX_CMP_REQUEUE_PI failed\n", errno); 235 args->ret = RET_ERROR; 236 break; 237 } 238 239 task_count += args->ret; 240 usleep(SIGNAL_PERIOD_US); 241 i++; 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; 247 break; 248 } 249 } 250 251 futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); 252 253 if (args->ret >= 0) 254 args->ret = task_count; 255 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); 259 } 260 261 void *third_party_blocker(void *arg) 262 { 263 struct thread_arg *args = (struct thread_arg *)arg; 264 int ret2 = 0; 265 266 args->ret = futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 267 if (args->ret) 268 goto out; 269 args->ret = futex_wait(&wake_complete, wake_complete, NULL, 270 FUTEX_PRIVATE_FLAG); 271 ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 272 273 out: 274 if (args->ret || ret2) { 275 error("third_party_blocker() futex error", 0); 276 args->ret = RET_ERROR; 277 } 278 279 pthread_exit((void *)&args->ret); 280 } 281 282 int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns) 283 { 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]; 290 int *waiter_ret; 291 int i, ret = RET_PASS; 292 293 if (timeout_ns) { 294 time_t secs; 295 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; 300 ts.tv_sec += secs; 301 info("ts.tv_sec = %ld\n", ts.tv_sec); 302 info("ts.tv_nsec = %ld\n", ts.tv_nsec); 303 tsp = &ts; 304 } 305 306 if (broadcast) 307 wakerfn = broadcast_wakerfn; 308 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", 313 errno); 314 ret = RET_ERROR; 315 goto out; 316 } 317 } 318 319 atomic_set(&waiters_woken, 0); 320 for (i = 0; i < THREAD_MAX; i++) { 321 args[i].id = i; 322 args[i].timeout = tsp; 323 info("Starting thread %d\n", i); 324 if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i], 325 SCHED_FIFO, 1)) { 326 error("Creating waiting thread failed\n", errno); 327 ret = RET_ERROR; 328 goto out; 329 } 330 } 331 waker_arg.lock = lock; 332 if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg, 333 SCHED_FIFO, 1)) { 334 error("Creating waker thread failed\n", errno); 335 ret = RET_ERROR; 336 goto out; 337 } 338 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); 345 346 if (third_party_owner) 347 pthread_join(blocker, NULL); 348 pthread_join(waker, NULL); 349 350 out: 351 if (!ret) { 352 if (*waiter_ret) 353 ret = *waiter_ret; 354 else if (waker_arg.ret < 0) 355 ret = waker_arg.ret; 356 else if (blocker_arg.ret) 357 ret = blocker_arg.ret; 358 } 359 360 return ret; 361 } 362 363 int main(int argc, char *argv[]) 364 { 365 int c, ret; 366 367 while ((c = getopt(argc, argv, "bchlot:v:")) != -1) { 368 switch (c) { 369 case 'b': 370 broadcast = 1; 371 break; 372 case 'c': 373 log_color(1); 374 break; 375 case 'h': 376 usage(basename(argv[0])); 377 exit(0); 378 case 'l': 379 locked = 1; 380 break; 381 case 'o': 382 owner = 1; 383 locked = 0; 384 break; 385 case 't': 386 timeout_ns = atoi(optarg); 387 break; 388 case 'v': 389 log_verbosity(atoi(optarg)); 390 break; 391 default: 392 usage(basename(argv[0])); 393 exit(1); 394 } 395 } 396 397 ksft_print_header(); 398 ksft_print_msg("%s: Test requeue functionality\n", basename(argv[0])); 399 ksft_print_msg( 400 "\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n", 401 broadcast, locked, owner, timeout_ns); 402 403 /* 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() 407 */ 408 ret = unit_test(broadcast, locked, owner, timeout_ns); 409 410 print_result(TEST_NAME, ret); 411 return ret; 412 } 413