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-hash.c (6786B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2013  Davidlohr Bueso <davidlohr@hp.com>
      4 *
      5 * futex-hash: Stress the hell out of the Linux kernel futex uaddr hashing.
      6 *
      7 * This program is particularly useful for measuring the kernel's futex hash
      8 * table/function implementation. In order for it to make sense, use with as
      9 * many threads and futexes as possible.
     10 */
     11
     12/* For the CLR_() macros */
     13#include <string.h>
     14#include <pthread.h>
     15
     16#include <errno.h>
     17#include <signal.h>
     18#include <stdlib.h>
     19#include <linux/compiler.h>
     20#include <linux/kernel.h>
     21#include <linux/zalloc.h>
     22#include <sys/time.h>
     23#include <sys/mman.h>
     24#include <perf/cpumap.h>
     25
     26#include "../util/stat.h"
     27#include <subcmd/parse-options.h>
     28#include "bench.h"
     29#include "futex.h"
     30
     31#include <err.h>
     32
     33static bool done = false;
     34static int futex_flag = 0;
     35
     36struct timeval bench__start, bench__end, bench__runtime;
     37static pthread_mutex_t thread_lock;
     38static unsigned int threads_starting;
     39static struct stats throughput_stats;
     40static pthread_cond_t thread_parent, thread_worker;
     41
     42struct worker {
     43	int tid;
     44	u_int32_t *futex;
     45	pthread_t thread;
     46	unsigned long ops;
     47};
     48
     49static struct bench_futex_parameters params = {
     50	.nfutexes = 1024,
     51	.runtime  = 10,
     52};
     53
     54static const struct option options[] = {
     55	OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
     56	OPT_UINTEGER('r', "runtime", &params.runtime, "Specify runtime (in seconds)"),
     57	OPT_UINTEGER('f', "futexes", &params.nfutexes, "Specify amount of futexes per threads"),
     58	OPT_BOOLEAN( 's', "silent",  &params.silent, "Silent mode: do not display data/details"),
     59	OPT_BOOLEAN( 'S', "shared",  &params.fshared, "Use shared futexes instead of private ones"),
     60	OPT_BOOLEAN( 'm', "mlockall", &params.mlockall, "Lock all current and future memory"),
     61	OPT_END()
     62};
     63
     64static const char * const bench_futex_hash_usage[] = {
     65	"perf bench futex hash <options>",
     66	NULL
     67};
     68
     69static void *workerfn(void *arg)
     70{
     71	int ret;
     72	struct worker *w = (struct worker *) arg;
     73	unsigned int i;
     74	unsigned long ops = w->ops; /* avoid cacheline bouncing */
     75
     76	pthread_mutex_lock(&thread_lock);
     77	threads_starting--;
     78	if (!threads_starting)
     79		pthread_cond_signal(&thread_parent);
     80	pthread_cond_wait(&thread_worker, &thread_lock);
     81	pthread_mutex_unlock(&thread_lock);
     82
     83	do {
     84		for (i = 0; i < params.nfutexes; i++, ops++) {
     85			/*
     86			 * We want the futex calls to fail in order to stress
     87			 * the hashing of uaddr and not measure other steps,
     88			 * such as internal waitqueue handling, thus enlarging
     89			 * the critical region protected by hb->lock.
     90			 */
     91			ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag);
     92			if (!params.silent &&
     93			    (!ret || errno != EAGAIN || errno != EWOULDBLOCK))
     94				warn("Non-expected futex return call");
     95		}
     96	}  while (!done);
     97
     98	w->ops = ops;
     99	return NULL;
    100}
    101
    102static void toggle_done(int sig __maybe_unused,
    103			siginfo_t *info __maybe_unused,
    104			void *uc __maybe_unused)
    105{
    106	/* inform all threads that we're done for the day */
    107	done = true;
    108	gettimeofday(&bench__end, NULL);
    109	timersub(&bench__end, &bench__start, &bench__runtime);
    110}
    111
    112static void print_summary(void)
    113{
    114	unsigned long avg = avg_stats(&throughput_stats);
    115	double stddev = stddev_stats(&throughput_stats);
    116
    117	printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
    118	       !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
    119	       (int)bench__runtime.tv_sec);
    120}
    121
    122int bench_futex_hash(int argc, const char **argv)
    123{
    124	int ret = 0;
    125	cpu_set_t *cpuset;
    126	struct sigaction act;
    127	unsigned int i;
    128	pthread_attr_t thread_attr;
    129	struct worker *worker = NULL;
    130	struct perf_cpu_map *cpu;
    131	int nrcpus;
    132	size_t size;
    133
    134	argc = parse_options(argc, argv, options, bench_futex_hash_usage, 0);
    135	if (argc) {
    136		usage_with_options(bench_futex_hash_usage, options);
    137		exit(EXIT_FAILURE);
    138	}
    139
    140	cpu = perf_cpu_map__new(NULL);
    141	if (!cpu)
    142		goto errmem;
    143
    144	memset(&act, 0, sizeof(act));
    145	sigfillset(&act.sa_mask);
    146	act.sa_sigaction = toggle_done;
    147	sigaction(SIGINT, &act, NULL);
    148
    149	if (params.mlockall) {
    150		if (mlockall(MCL_CURRENT | MCL_FUTURE))
    151			err(EXIT_FAILURE, "mlockall");
    152	}
    153
    154	if (!params.nthreads) /* default to the number of CPUs */
    155		params.nthreads = perf_cpu_map__nr(cpu);
    156
    157	worker = calloc(params.nthreads, sizeof(*worker));
    158	if (!worker)
    159		goto errmem;
    160
    161	if (!params.fshared)
    162		futex_flag = FUTEX_PRIVATE_FLAG;
    163
    164	printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
    165	       getpid(), params.nthreads, params.nfutexes, params.fshared ? "shared":"private", params.runtime);
    166
    167	init_stats(&throughput_stats);
    168	pthread_mutex_init(&thread_lock, NULL);
    169	pthread_cond_init(&thread_parent, NULL);
    170	pthread_cond_init(&thread_worker, NULL);
    171
    172	threads_starting = params.nthreads;
    173	pthread_attr_init(&thread_attr);
    174	gettimeofday(&bench__start, NULL);
    175
    176	nrcpus = perf_cpu_map__nr(cpu);
    177	cpuset = CPU_ALLOC(nrcpus);
    178	BUG_ON(!cpuset);
    179	size = CPU_ALLOC_SIZE(nrcpus);
    180
    181	for (i = 0; i < params.nthreads; i++) {
    182		worker[i].tid = i;
    183		worker[i].futex = calloc(params.nfutexes, sizeof(*worker[i].futex));
    184		if (!worker[i].futex)
    185			goto errmem;
    186
    187		CPU_ZERO_S(size, cpuset);
    188
    189		CPU_SET_S(perf_cpu_map__cpu(cpu, i % perf_cpu_map__nr(cpu)).cpu, size, cpuset);
    190		ret = pthread_attr_setaffinity_np(&thread_attr, size, cpuset);
    191		if (ret) {
    192			CPU_FREE(cpuset);
    193			err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
    194		}
    195		ret = pthread_create(&worker[i].thread, &thread_attr, workerfn,
    196				     (void *)(struct worker *) &worker[i]);
    197		if (ret) {
    198			CPU_FREE(cpuset);
    199			err(EXIT_FAILURE, "pthread_create");
    200		}
    201
    202	}
    203	CPU_FREE(cpuset);
    204	pthread_attr_destroy(&thread_attr);
    205
    206	pthread_mutex_lock(&thread_lock);
    207	while (threads_starting)
    208		pthread_cond_wait(&thread_parent, &thread_lock);
    209	pthread_cond_broadcast(&thread_worker);
    210	pthread_mutex_unlock(&thread_lock);
    211
    212	sleep(params.runtime);
    213	toggle_done(0, NULL, NULL);
    214
    215	for (i = 0; i < params.nthreads; i++) {
    216		ret = pthread_join(worker[i].thread, NULL);
    217		if (ret)
    218			err(EXIT_FAILURE, "pthread_join");
    219	}
    220
    221	/* cleanup & report results */
    222	pthread_cond_destroy(&thread_parent);
    223	pthread_cond_destroy(&thread_worker);
    224	pthread_mutex_destroy(&thread_lock);
    225
    226	for (i = 0; i < params.nthreads; i++) {
    227		unsigned long t = bench__runtime.tv_sec > 0 ?
    228			worker[i].ops / bench__runtime.tv_sec : 0;
    229		update_stats(&throughput_stats, t);
    230		if (!params.silent) {
    231			if (params.nfutexes == 1)
    232				printf("[thread %2d] futex: %p [ %ld ops/sec ]\n",
    233				       worker[i].tid, &worker[i].futex[0], t);
    234			else
    235				printf("[thread %2d] futexes: %p ... %p [ %ld ops/sec ]\n",
    236				       worker[i].tid, &worker[i].futex[0],
    237				       &worker[i].futex[params.nfutexes-1], t);
    238		}
    239
    240		zfree(&worker[i].futex);
    241	}
    242
    243	print_summary();
    244
    245	free(worker);
    246	free(cpu);
    247	return ret;
    248errmem:
    249	err(EXIT_FAILURE, "calloc");
    250}