1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /****************************************************************************** 3 * 4 * Copyright © International Business Machines Corp., 2006-2008 5 * 6 * DESCRIPTION 7 * This test excercises the futex syscall op codes needed for requeuing 8 * priority inheritance aware POSIX condition variables and mutexes. 9 * 10 * AUTHORS 11 * Sripathi Kodi <sripathik@in.ibm.com> 12 * Darren Hart <dvhart@linux.intel.com> 13 * 14 * HISTORY 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> 17 * 18 *****************************************************************************/ 19 20 #define _GNU_SOURCE 21 22 #include <errno.h> 23 #include <limits.h> 24 #include <pthread.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <signal.h> 28 #include <string.h> 29 #include "atomic.h" 30 #include "futextest.h" 31 #include "logging.h" 32 33 #define TEST_NAME "futex-requeue-pi" 34 #define MAX_WAKE_ITERS 1000 35 #define THREAD_MAX 10 36 #define SIGNAL_PERIOD_US 100 37 38 atomic_t waiters_blocked = ATOMIC_INITIALIZER; 39 atomic_t waiters_woken = ATOMIC_INITIALIZER; 40 41 futex_t f1 = FUTEX_INITIALIZER; 42 futex_t f2 = FUTEX_INITIALIZER; 43 futex_t wake_complete = FUTEX_INITIALIZER; 44 45 /* Test option defaults */ 46 static long timeout_ns; 47 static int broadcast; 48 static int owner; 49 static int locked; 50 51 struct thread_arg { 52 long id; 53 struct timespec *timeout; 54 int lock; 55 int ret; 56 }; 57 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 } 58 59 void usage(char *prog) 60 { 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); 70 } 71 72 int create_rt_thread(pthread_t *pth, void*(*func)(void *), void *arg, 73 int policy, int prio) 74 { 75 int ret; 76 struct sched_param schedp; 77 pthread_attr_t attr; 78 79 pthread_attr_init(&attr); 80 memset(&schedp, 0, sizeof(schedp)); 81 82 ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); 83 if (ret) { 84 error("pthread_attr_setinheritsched\n", ret); 85 return -1; 86 } 87 88 ret = pthread_attr_setschedpolicy(&attr, policy); 89 if (ret) { 90 error("pthread_attr_setschedpolicy\n", ret); 91 return -1; 92 } 93 94 schedp.sched_priority = prio; 95 ret = pthread_attr_setschedparam(&attr, &schedp); 96 if (ret) { 97 error("pthread_attr_setschedparam\n", ret); 98 return -1; 99 } 100 101 ret = pthread_create(pth, &attr, func, arg); 102 if (ret) { 103 error("pthread_create\n", ret); 104 return -1; 105 } 106 return 0; 107 } 108 109 110 void *waiterfn(void *arg) 111 { 112 struct thread_arg *args = (struct thread_arg *)arg; 113 futex_t old_val; 114 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); 120 121 old_val = f1; 122 atomic_inc(&waiters_blocked); 123 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n", 124 &f1, f1, &f2); 125 args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout, 126 FUTEX_PRIVATE_FLAG); 127 128 info("waiter %ld woke with %d %s\n", args->id, args->ret, 129 args->ret < 0 ? strerror(errno) : ""); 130 atomic_inc(&waiters_woken); 131 if (args->ret < 0) { 132 if (args->timeout && errno == ETIMEDOUT) 133 args->ret = 0; 134 else { 135 args->ret = RET_ERROR; 136 error("futex_wait_requeue_pi\n", errno); 137 } 138 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 139 } 140 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 141 142 info("Waiter %ld: exiting with %d\n", args->id, args->ret); 143 pthread_exit((void *)&args->ret); 144 } 145 146 void *broadcast_wakerfn(void *arg) 147 { 148 struct thread_arg *args = (struct thread_arg *)arg; 149 int nr_requeue = INT_MAX; 150 int task_count = 0; 151 futex_t old_val; 152 int nr_wake = 1; 153 int i = 0; 154 155 info("Waker: waiting for waiters to block\n"); 156 while (waiters_blocked.val < THREAD_MAX) 157 usleep(1000); 158 usleep(1000); 159 160 info("Waker: Calling broadcast\n"); 161 if (args->lock) { 162 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); 163 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 164 } 165 continue_requeue: 166 old_val = f1; 167 args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, 168 FUTEX_PRIVATE_FLAG); 169 if (args->ret < 0) { 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; 176 } else { 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; 180 } 181 182 futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); 183 184 if (args->lock) 185 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 186 187 if (args->ret > 0) 188 args->ret = task_count; 189 190 info("Waker: exiting with %d\n", args->ret); 191 pthread_exit((void *)&args->ret); 192 } 193 194 void *signal_wakerfn(void *arg) 195 { 196 struct thread_arg *args = (struct thread_arg *)arg; 197 unsigned int old_val; 198 int nr_requeue = 0; 199 int task_count = 0; 200 int nr_wake = 1; 201 int i = 0; 202 203 info("Waker: waiting for waiters to block\n"); 204 while (waiters_blocked.val < THREAD_MAX) 205 usleep(1000); 206 usleep(1000); 207 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); 211 if (args->lock) { 212 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", 213 f2, &f2); 214 futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 215 } 216 info("Waker: Calling signal\n"); 217 /* cond_signal */ 218 old_val = f1; 219 args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, 220 nr_wake, nr_requeue, 221 FUTEX_PRIVATE_FLAG); 222 if (args->ret < 0) 223 args->ret = -errno; 224 info("futex: %x\n", f2); 225 if (args->lock) { 226 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", 227 f2, &f2); 228 futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 229 } 230 info("futex: %x\n", f2); 231 if (args->ret < 0) { 232 error("FUTEX_CMP_REQUEUE_PI failed\n", errno); 233 args->ret = RET_ERROR; 234 break; 235 } 236 237 task_count += args->ret; 238 usleep(SIGNAL_PERIOD_US); 239 i++; 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; 245 break; 246 } 247 } 248 249 futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); 250 251 if (args->ret >= 0) 252 args->ret = task_count; 253 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); 257 } 258 259 void *third_party_blocker(void *arg) 260 { 261 struct thread_arg *args = (struct thread_arg *)arg; 262 int ret2 = 0; 263 264 args->ret = futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); 265 if (args->ret) 266 goto out; 267 args->ret = futex_wait(&wake_complete, wake_complete, NULL, 268 FUTEX_PRIVATE_FLAG); 269 ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); 270 271 out: 272 if (args->ret || ret2) { 273 error("third_party_blocker() futex error", 0); 274 args->ret = RET_ERROR; 275 } 276 277 pthread_exit((void *)&args->ret); 278 } 279 280 int unit_test(int broadcast, long lock, int third_party_owner, long timeout_ns) 281 { 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]; 288 int *waiter_ret; 289 int i, ret = RET_PASS; 290 291 if (timeout_ns) { 292 time_t secs; 293 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; 298 ts.tv_sec += secs; 299 info("ts.tv_sec = %ld\n", ts.tv_sec); 300 info("ts.tv_nsec = %ld\n", ts.tv_nsec); 301 tsp = &ts; 302 } 303 304 if (broadcast) 305 wakerfn = broadcast_wakerfn; 306 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", 311 errno); 312 ret = RET_ERROR; 313 goto out; 314 } 315 } 316 317 atomic_set(&waiters_woken, 0); 318 for (i = 0; i < THREAD_MAX; i++) { 319 args[i].id = i; 320 args[i].timeout = tsp; 321 info("Starting thread %d\n", i); 322 if (create_rt_thread(&waiter[i], waiterfn, (void *)&args[i], 323 SCHED_FIFO, 1)) { 324 error("Creating waiting thread failed\n", errno); 325 ret = RET_ERROR; 326 goto out; 327 } 328 } 329 waker_arg.lock = lock; 330 if (create_rt_thread(&waker, wakerfn, (void *)&waker_arg, 331 SCHED_FIFO, 1)) { 332 error("Creating waker thread failed\n", errno); 333 ret = RET_ERROR; 334 goto out; 335 } 336 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); 343 344 if (third_party_owner) 345 pthread_join(blocker, NULL); 346 pthread_join(waker, NULL); 347 348 out: 349 if (!ret) { 350 if (*waiter_ret) 351 ret = *waiter_ret; 352 else if (waker_arg.ret < 0) 353 ret = waker_arg.ret; 354 else if (blocker_arg.ret) 355 ret = blocker_arg.ret; 356 } 357 358 return ret; 359 } 360 361 int main(int argc, char *argv[]) 362 { 363 const char *test_name; 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 ksft_print_header(); 397 ksft_set_plan(1); 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 ret = asprintf(&test_name, 404 "%s broadcast=%d locked=%d owner=%d timeout=%ldns", 405 TEST_NAME, broadcast, locked, owner, timeout_ns); 406 if (ret < 0) { 407 ksft_print_msg("Failed to generate test name\n"); 408 test_name = TEST_NAME; 409 } 410 411 /* 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() 415 */ 416 ret = unit_test(broadcast, locked, owner, timeout_ns); 417 418 print_result(test_name, ret); 419 return ret; 420 } 421