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 MAX_WAKE_ITERS 1000 36 #define THREAD_MAX 10 37 #define SIGNAL_PERIOD_US 100 38 39 atomic_t waiters_blocked = ATOMIC_INITIALIZER; 40 atomic_t waiters_woken = ATOMIC_INITIALIZER; 41 42 futex_t f1 = FUTEX_INITIALIZER; 43 futex_t f2 = FUTEX_INITIALIZER; 44 futex_t wake_complete = FUTEX_INITIALIZER; 45 46 /* Test option defaults */ 47 static long timeout_ns; 48 static int broadcast; 49 static int owner; 50 static int locked; 51 52 struct thread_arg { 53 long id; 54 struct timespec *timeout; 55 int lock; 56 int ret; 57 }; 58 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 } 59 60 void usage(char *prog) 61 { 62 printf("Usage: %s\n", prog); 63 printf(" -b Broadcast wakeup (all waiters)\n"); 64 printf(" -c Use color\n"); 65 printf(" -h Display this help message\n"); 66 printf(" -l Lock the pi futex across requeue\n"); 67 printf(" -o Use a third party pi futex owner during requeue (cancels -l)\n"); 68 printf(" -t N Timeout in nanoseconds (default: 0)\n"); 69 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n", 70 VQUIET, VCRITICAL, VINFO); 71 } 72 73 int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, 74 int policy, int prio) 75 { 76 int ret; 77 struct sched_param schedp; 78 pthread_attr_t attr; 79 80 pthread_attr_init(&attr); 81 memset(&schedp, 0, sizeof(schedp)); 82 83 ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); 84 if (ret) { 85 error("pthread_attr_setinheritsched\n", ret); 86 return -1; 87 } 88 89 ret = pthread_attr_setschedpolicy(&attr, policy); 90 if (ret) { 91 error("pthread_attr_setschedpolicy\n", ret); 92 return -1; 93 } 94 95 schedp.sched_priority = prio; 96 ret = pthread_attr_setschedparam(&attr, &schedp); 97 if (ret) { 98 error("pthread_attr_setschedparam\n", ret); 99 return -1; 100 } 101 102 ret = pthread_create(pth, &attr, func, arg); 103 if (ret) { 104 error("pthread_create\n", ret); 105 return -1; 106 } 107 return 0; 108 } 109 110 111 void *waiterfn(void *arg) 112 { 113 struct thread_arg *args = (struct thread_arg *)arg; 114 futex_t old_val; 115 116 info("Waiter %ld: running\n", args->id); 117 /* Each thread sleeps for a different amount of time 118 * This is to avoid races, because we don't lock the 119 * external mutex here */ 120 usleep(1000 * (long)args->id); 121 122 old_val = f1; 123 atomic_inc(&waiters_blocked); 124 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n", 125 &f1, f1, &f2); 126 args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout, 127 FUTEX_PRIVATE_FLAG); 128 129 info("waiter %ld woke with %d %s\n", args->id, args->ret, 130 args->ret < 0 ? strerror(errno) : ""); 131 atomic_inc(&waiters_woken); 132 if (args->ret < 0) { 133 if (args->timeout && errno == ETIMEDOUT) 134 args->ret = 0; 135 else { 136 args->ret = RET_ERROR; 137 error("futex_wait_requeue_pi\n", errno); 138 } 139 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 140 } 141 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 142 143 info("Waiter %ld: exiting with %d\n", args->id, args->ret); 144 pthread_exit((void *)&args->ret); 145 } 146 147 void *broadcast_wakerfn(void *arg) 148 { 149 struct thread_arg *args = (struct thread_arg *)arg; 150 int nr_requeue = INT_MAX; 151 int task_count = 0; 152 futex_t old_val; 153 int nr_wake = 1; 154 int i = 0; 155 156 info("Waker: waiting for waiters to block\n"); 157 while (waiters_blocked.val < THREAD_MAX) 158 usleep(1000); 159 usleep(1000); 160 161 info("Waker: Calling broadcast\n"); 162 if (args->lock) { 163 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); 164 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 165 } 166 continue_requeue: 167 old_val = f1; 168 args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, 169 FUTEX_PRIVATE_FLAG); 170 if (args->ret < 0) { 171 args->ret = RET_ERROR; 172 error("FUTEX_CMP_REQUEUE_PI failed\n", errno); 173 } else if (++i < MAX_WAKE_ITERS) { 174 task_count += args->ret; 175 if (task_count < THREAD_MAX - waiters_woken.val) 176 goto continue_requeue; 177 } else { 178 error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n", 179 0, MAX_WAKE_ITERS, task_count, THREAD_MAX); 180 args->ret = RET_ERROR; 181 } 182 183 futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); 184 185 if (args->lock) 186 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 187 188 if (args->ret > 0) 189 args->ret = task_count; 190 191 info("Waker: exiting with %d\n", args->ret); 192 pthread_exit((void *)&args->ret); 193 } 194 195 void *signal_wakerfn(void *arg) 196 { 197 struct thread_arg *args = (struct thread_arg *)arg; 198 unsigned int old_val; 199 int nr_requeue = 0; 200 int task_count = 0; 201 int nr_wake = 1; 202 int i = 0; 203 204 info("Waker: waiting for waiters to block\n"); 205 while (waiters_blocked.val < THREAD_MAX) 206 usleep(1000); 207 usleep(1000); 208 209 while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) { 210 info("task_count: %d, waiters_woken: %d\n", 211 task_count, waiters_woken.val); 212 if (args->lock) { 213 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", 214 f2, &f2); 215 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 216 } 217 info("Waker: Calling signal\n"); 218 /* cond_signal */ 219 old_val = f1; 220 args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, 221 nr_wake, nr_requeue, 222 FUTEX_PRIVATE_FLAG); 223 if (args->ret < 0) 224 args->ret = -errno; 225 info("futex: %x\n", f2); 226 if (args->lock) { 227 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", 228 f2, &f2); 229 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 230 } 231 info("futex: %x\n", f2); 232 if (args->ret < 0) { 233 error("FUTEX_CMP_REQUEUE_PI failed\n", errno); 234 args->ret = RET_ERROR; 235 break; 236 } 237 238 task_count += args->ret; 239 usleep(SIGNAL_PERIOD_US); 240 i++; 241 /* we have to loop at least THREAD_MAX times */ 242 if (i > MAX_WAKE_ITERS + THREAD_MAX) { 243 error("max signaling iterations (%d) reached, giving up on pending waiters.\n", 244 0, MAX_WAKE_ITERS + THREAD_MAX); 245 args->ret = RET_ERROR; 246 break; 247 } 248 } 249 250 futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); 251 252 if (args->ret >= 0) 253 args->ret = task_count; 254 255 info("Waker: exiting with %d\n", args->ret); 256 info("Waker: waiters_woken: %d\n", waiters_woken.val); 257 pthread_exit((void *)&args->ret); 258 } 259 260 void *third_party_blocker(void *arg) 261 { 262 struct thread_arg *args = (struct thread_arg *)arg; 263 int ret2 = 0; 264 265 args->ret = futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 266 if (args->ret) 267 goto out; 268 args->ret = futex_wait(&wake_complete, wake_complete, NULL, 269 FUTEX_PRIVATE_FLAG); 270 ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 271 272 out: 273 if (args->ret || ret2) { 274 error("third_party_blocker() futex error", 0); 275 args->ret = RET_ERROR; 276 } 277 278 pthread_exit((void *)&args->ret); 279 } 280 281 int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns) 282 { 283 void *(*wakerfn)(void *) = signal_wakerfn; 284 struct thread_arg blocker_arg = THREAD_ARG_INITIALIZER; 285 struct thread_arg waker_arg = THREAD_ARG_INITIALIZER; 286 pthread_t waiter[THREAD_MAX], waker, blocker; 287 struct timespec ts, *tsp = NULL; 288 struct thread_arg args[THREAD_MAX]; 289 int *waiter_ret; 290 int i, ret = RET_PASS; 291 292 if (timeout_ns) { 293 time_t secs; 294 295 info("timeout_ns = %ld\n", timeout_ns); 296 ret = clock_gettime(CLOCK_MONOTONIC, &ts); 297 secs = (ts.tv_nsec + timeout_ns) / 1000000000; 298 ts.tv_nsec = ((int64_t)ts.tv_nsec + timeout_ns) % 1000000000; 299 ts.tv_sec += secs; 300 info("ts.tv_sec = %ld\n", ts.tv_sec); 301 info("ts.tv_nsec = %ld\n", ts.tv_nsec); 302 tsp = &ts; 303 } 304 305 if (broadcast) 306 wakerfn = broadcast_wakerfn; 307 308 if (third_party_owner) { 309 if (create_rt_thread(&blocker, third_party_blocker, 310 (void *)&blocker_arg, SCHED_FIFO, 1)) { 311 error("Creating third party blocker thread failed\n", 312 errno); 313 ret = RET_ERROR; 314 goto out; 315 } 316 } 317 318 atomic_set(&waiters_woken, 0); 319 for (i = 0; i < THREAD_MAX; i++) { 320 args[i].id = i; 321 args[i].timeout = tsp; 322 info("Starting thread %d\n", i); 323 if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i], 324 SCHED_FIFO, 1)) { 325 error("Creating waiting thread failed\n", errno); 326 ret = RET_ERROR; 327 goto out; 328 } 329 } 330 waker_arg.lock = lock; 331 if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg, 332 SCHED_FIFO, 1)) { 333 error("Creating waker thread failed\n", errno); 334 ret = RET_ERROR; 335 goto out; 336 } 337 338 /* Wait for threads to finish */ 339 /* Store the first error or failure encountered in waiter_ret */ 340 waiter_ret = &args[0].ret; 341 for (i = 0; i < THREAD_MAX; i++) 342 pthread_join(waiter[i], 343 *waiter_ret ? NULL : (void **)&waiter_ret); 344 345 if (third_party_owner) 346 pthread_join(blocker, NULL); 347 pthread_join(waker, NULL); 348 349 out: 350 if (!ret) { 351 if (*waiter_ret) 352 ret = *waiter_ret; 353 else if (waker_arg.ret < 0) 354 ret = waker_arg.ret; 355 else if (blocker_arg.ret) 356 ret = blocker_arg.ret; 357 } 358 359 return ret; 360 } 361 362 int main(int argc, char *argv[]) 363 { 364 int c, ret; 365 366 while ((c = getopt(argc, argv, "bchlot:v:")) != -1) { 367 switch (c) { 368 case 'b': 369 broadcast = 1; 370 break; 371 case 'c': 372 log_color(1); 373 break; 374 case 'h': 375 usage(basename(argv[0])); 376 exit(0); 377 case 'l': 378 locked = 1; 379 break; 380 case 'o': 381 owner = 1; 382 locked = 0; 383 break; 384 case 't': 385 timeout_ns = atoi(optarg); 386 break; 387 case 'v': 388 log_verbosity(atoi(optarg)); 389 break; 390 default: 391 usage(basename(argv[0])); 392 exit(1); 393 } 394 } 395 396 printf("%s: Test requeue functionality\n", basename(argv[0])); 397 printf("\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n", 398 broadcast, locked, owner, timeout_ns); 399 400 /* 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() 404 */ 405 ret = unit_test(broadcast, locked, owner, timeout_ns); 406 407 print_result(ret); 408 return ret; 409 } 410