brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · 215c6cb Raw
421 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/******************************************************************************3 *4 *   Copyright © International Business Machines  Corp., 2006-20085 *6 * DESCRIPTION7 *      This test excercises the futex syscall op codes needed for requeuing8 *      priority inheritance aware POSIX condition variables and mutexes.9 *10 * AUTHORS11 *      Sripathi Kodi <sripathik@in.ibm.com>12 *      Darren Hart <dvhart@linux.intel.com>13 *14 * HISTORY15 *      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_SOURCE21 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 100035#define THREAD_MAX 1036#define SIGNAL_PERIOD_US 10037 38atomic_t waiters_blocked = ATOMIC_INITIALIZER;39atomic_t waiters_woken = ATOMIC_INITIALIZER;40 41futex_t f1 = FUTEX_INITIALIZER;42futex_t f2 = FUTEX_INITIALIZER;43futex_t wake_complete = FUTEX_INITIALIZER;44 45/* Test option defaults */46static long timeout_ns;47static int broadcast;48static int owner;49static int locked;50 51struct 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 59void 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 72int 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 110void *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 time117	 * This is to avoid races, because we don't lock the118	 * 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 146void *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 194void *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 259void *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 280int 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 348out: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 361int main(int argc, char *argv[])362{363	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 the413	 * various style of runs are done by run.sh - simplify the code and move414	 * 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