cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

futex_requeue_pi.c (10339B)


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