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

context_switch.c (9044B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Context switch microbenchmark.
      4 *
      5 * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
      6 */
      7
      8#define _GNU_SOURCE
      9#include <errno.h>
     10#include <sched.h>
     11#include <string.h>
     12#include <stdio.h>
     13#include <unistd.h>
     14#include <stdlib.h>
     15#include <getopt.h>
     16#include <signal.h>
     17#include <assert.h>
     18#include <pthread.h>
     19#include <limits.h>
     20#include <sys/time.h>
     21#include <sys/syscall.h>
     22#include <sys/sysinfo.h>
     23#include <sys/types.h>
     24#include <sys/shm.h>
     25#include <linux/futex.h>
     26#ifdef __powerpc__
     27#include <altivec.h>
     28#endif
     29#include "utils.h"
     30
     31static unsigned int timeout = 30;
     32
     33static int touch_vdso;
     34struct timeval tv;
     35
     36static int touch_fp = 1;
     37double fp;
     38
     39static int touch_vector = 1;
     40vector int a, b, c;
     41
     42#ifdef __powerpc__
     43static int touch_altivec = 1;
     44
     45/*
     46 * Note: LTO (Link Time Optimisation) doesn't play well with this function
     47 * attribute. Be very careful enabling LTO for this test.
     48 */
     49static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
     50{
     51	c = a + b;
     52}
     53#endif
     54
     55static void touch(void)
     56{
     57	if (touch_vdso)
     58		gettimeofday(&tv, NULL);
     59
     60	if (touch_fp)
     61		fp += 0.1;
     62
     63#ifdef __powerpc__
     64	if (touch_altivec)
     65		altivec_touch_fn();
     66#endif
     67
     68	if (touch_vector)
     69		c = a + b;
     70
     71	asm volatile("# %0 %1 %2": : "r"(&tv), "r"(&fp), "r"(&c));
     72}
     73
     74static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu)
     75{
     76	int rc;
     77	pthread_t tid;
     78	cpu_set_t cpuset;
     79	pthread_attr_t attr;
     80
     81	CPU_ZERO(&cpuset);
     82	CPU_SET(cpu, &cpuset);
     83
     84	rc = pthread_attr_init(&attr);
     85	if (rc) {
     86		errno = rc;
     87		perror("pthread_attr_init");
     88		exit(1);
     89	}
     90
     91	rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
     92	if (rc)	{
     93		errno = rc;
     94		perror("pthread_attr_setaffinity_np");
     95		exit(1);
     96	}
     97
     98	rc = pthread_create(&tid, &attr, fn, arg);
     99	if (rc) {
    100		errno = rc;
    101		perror("pthread_create");
    102		exit(1);
    103	}
    104}
    105
    106static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu)
    107{
    108	int pid, ncpus;
    109	cpu_set_t *cpuset;
    110	size_t size;
    111
    112	pid = fork();
    113	if (pid == -1) {
    114		perror("fork");
    115		exit(1);
    116	}
    117
    118	if (pid)
    119		return;
    120
    121	ncpus = get_nprocs();
    122	size = CPU_ALLOC_SIZE(ncpus);
    123	cpuset = CPU_ALLOC(ncpus);
    124	if (!cpuset) {
    125		perror("malloc");
    126		exit(1);
    127	}
    128	CPU_ZERO_S(size, cpuset);
    129	CPU_SET_S(cpu, size, cpuset);
    130
    131	if (sched_setaffinity(0, size, cpuset)) {
    132		perror("sched_setaffinity");
    133		CPU_FREE(cpuset);
    134		exit(1);
    135	}
    136
    137	CPU_FREE(cpuset);
    138	fn(arg);
    139
    140	exit(0);
    141}
    142
    143static unsigned long iterations;
    144static unsigned long iterations_prev;
    145
    146static void sigalrm_handler(int junk)
    147{
    148	unsigned long i = iterations;
    149
    150	printf("%ld\n", i - iterations_prev);
    151	iterations_prev = i;
    152
    153	if (--timeout == 0)
    154		kill(0, SIGUSR1);
    155
    156	alarm(1);
    157}
    158
    159static void sigusr1_handler(int junk)
    160{
    161	exit(0);
    162}
    163
    164struct actions {
    165	void (*setup)(int, int);
    166	void *(*thread1)(void *);
    167	void *(*thread2)(void *);
    168};
    169
    170#define READ 0
    171#define WRITE 1
    172
    173static int pipe_fd1[2];
    174static int pipe_fd2[2];
    175
    176static void pipe_setup(int cpu1, int cpu2)
    177{
    178	if (pipe(pipe_fd1) || pipe(pipe_fd2))
    179		exit(1);
    180}
    181
    182static void *pipe_thread1(void *arg)
    183{
    184	signal(SIGALRM, sigalrm_handler);
    185	alarm(1);
    186
    187	while (1) {
    188		assert(read(pipe_fd1[READ], &c, 1) == 1);
    189		touch();
    190
    191		assert(write(pipe_fd2[WRITE], &c, 1) == 1);
    192		touch();
    193
    194		iterations += 2;
    195	}
    196
    197	return NULL;
    198}
    199
    200static void *pipe_thread2(void *arg)
    201{
    202	while (1) {
    203		assert(write(pipe_fd1[WRITE], &c, 1) == 1);
    204		touch();
    205
    206		assert(read(pipe_fd2[READ], &c, 1) == 1);
    207		touch();
    208	}
    209
    210	return NULL;
    211}
    212
    213static struct actions pipe_actions = {
    214	.setup = pipe_setup,
    215	.thread1 = pipe_thread1,
    216	.thread2 = pipe_thread2,
    217};
    218
    219static void yield_setup(int cpu1, int cpu2)
    220{
    221	if (cpu1 != cpu2) {
    222		fprintf(stderr, "Both threads must be on the same CPU for yield test\n");
    223		exit(1);
    224	}
    225}
    226
    227static void *yield_thread1(void *arg)
    228{
    229	signal(SIGALRM, sigalrm_handler);
    230	alarm(1);
    231
    232	while (1) {
    233		sched_yield();
    234		touch();
    235
    236		iterations += 2;
    237	}
    238
    239	return NULL;
    240}
    241
    242static void *yield_thread2(void *arg)
    243{
    244	while (1) {
    245		sched_yield();
    246		touch();
    247	}
    248
    249	return NULL;
    250}
    251
    252static struct actions yield_actions = {
    253	.setup = yield_setup,
    254	.thread1 = yield_thread1,
    255	.thread2 = yield_thread2,
    256};
    257
    258static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
    259		      void *addr2, int val3)
    260{
    261	return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
    262}
    263
    264static unsigned long cmpxchg(unsigned long *p, unsigned long expected,
    265			     unsigned long desired)
    266{
    267	unsigned long exp = expected;
    268
    269	__atomic_compare_exchange_n(p, &exp, desired, 0,
    270				    __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
    271	return exp;
    272}
    273
    274static unsigned long xchg(unsigned long *p, unsigned long val)
    275{
    276	return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST);
    277}
    278
    279static int processes;
    280
    281static int mutex_lock(unsigned long *m)
    282{
    283	int c;
    284	int flags = FUTEX_WAIT;
    285	if (!processes)
    286		flags |= FUTEX_PRIVATE_FLAG;
    287
    288	c = cmpxchg(m, 0, 1);
    289	if (!c)
    290		return 0;
    291
    292	if (c == 1)
    293		c = xchg(m, 2);
    294
    295	while (c) {
    296		sys_futex(m, flags, 2, NULL, NULL, 0);
    297		c = xchg(m, 2);
    298	}
    299
    300	return 0;
    301}
    302
    303static int mutex_unlock(unsigned long *m)
    304{
    305	int flags = FUTEX_WAKE;
    306	if (!processes)
    307		flags |= FUTEX_PRIVATE_FLAG;
    308
    309	if (*m == 2)
    310		*m = 0;
    311	else if (xchg(m, 0) == 1)
    312		return 0;
    313
    314	sys_futex(m, flags, 1, NULL, NULL, 0);
    315
    316	return 0;
    317}
    318
    319static unsigned long *m1, *m2;
    320
    321static void futex_setup(int cpu1, int cpu2)
    322{
    323	if (!processes) {
    324		static unsigned long _m1, _m2;
    325		m1 = &_m1;
    326		m2 = &_m2;
    327	} else {
    328		int shmid;
    329		void *shmaddr;
    330
    331		shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W);
    332		if (shmid < 0) {
    333			perror("shmget");
    334			exit(1);
    335		}
    336
    337		shmaddr = shmat(shmid, NULL, 0);
    338		if (shmaddr == (char *)-1) {
    339			perror("shmat");
    340			shmctl(shmid, IPC_RMID, NULL);
    341			exit(1);
    342		}
    343
    344		shmctl(shmid, IPC_RMID, NULL);
    345
    346		m1 = shmaddr;
    347		m2 = shmaddr + sizeof(*m1);
    348	}
    349
    350	*m1 = 0;
    351	*m2 = 0;
    352
    353	mutex_lock(m1);
    354	mutex_lock(m2);
    355}
    356
    357static void *futex_thread1(void *arg)
    358{
    359	signal(SIGALRM, sigalrm_handler);
    360	alarm(1);
    361
    362	while (1) {
    363		mutex_lock(m2);
    364		mutex_unlock(m1);
    365
    366		iterations += 2;
    367	}
    368
    369	return NULL;
    370}
    371
    372static void *futex_thread2(void *arg)
    373{
    374	while (1) {
    375		mutex_unlock(m2);
    376		mutex_lock(m1);
    377	}
    378
    379	return NULL;
    380}
    381
    382static struct actions futex_actions = {
    383	.setup = futex_setup,
    384	.thread1 = futex_thread1,
    385	.thread2 = futex_thread2,
    386};
    387
    388static struct option options[] = {
    389	{ "test", required_argument, 0, 't' },
    390	{ "process", no_argument, &processes, 1 },
    391	{ "timeout", required_argument, 0, 's' },
    392	{ "vdso", no_argument, &touch_vdso, 1 },
    393	{ "no-fp", no_argument, &touch_fp, 0 },
    394#ifdef __powerpc__
    395	{ "no-altivec", no_argument, &touch_altivec, 0 },
    396#endif
    397	{ "no-vector", no_argument, &touch_vector, 0 },
    398	{ 0, },
    399};
    400
    401static void usage(void)
    402{
    403	fprintf(stderr, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
    404	fprintf(stderr, "\t\t--test=X\tpipe, futex or yield (default)\n");
    405	fprintf(stderr, "\t\t--process\tUse processes (default threads)\n");
    406	fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
    407	fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n");
    408	fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n");
    409#ifdef __powerpc__
    410	fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n");
    411#endif
    412	fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n");
    413}
    414
    415int main(int argc, char *argv[])
    416{
    417	signed char c;
    418	struct actions *actions = &yield_actions;
    419	int cpu1;
    420	int cpu2;
    421	static void (*start_fn)(void *(*fn)(void *), void *arg, unsigned long cpu);
    422
    423	while (1) {
    424		int option_index = 0;
    425
    426		c = getopt_long(argc, argv, "", options, &option_index);
    427
    428		if (c == -1)
    429			break;
    430
    431		switch (c) {
    432		case 0:
    433			if (options[option_index].flag != 0)
    434				break;
    435
    436			usage();
    437			exit(1);
    438			break;
    439
    440		case 't':
    441			if (!strcmp(optarg, "pipe")) {
    442				actions = &pipe_actions;
    443			} else if (!strcmp(optarg, "yield")) {
    444				actions = &yield_actions;
    445			} else if (!strcmp(optarg, "futex")) {
    446				actions = &futex_actions;
    447			} else {
    448				usage();
    449				exit(1);
    450			}
    451			break;
    452
    453		case 's':
    454			timeout = atoi(optarg);
    455			break;
    456
    457		default:
    458			usage();
    459			exit(1);
    460		}
    461	}
    462
    463	if (processes)
    464		start_fn = start_process_on;
    465	else
    466		start_fn = start_thread_on;
    467
    468	if (((argc - optind) != 2)) {
    469		cpu1 = cpu2 = pick_online_cpu();
    470	} else {
    471		cpu1 = atoi(argv[optind++]);
    472		cpu2 = atoi(argv[optind++]);
    473	}
    474
    475	printf("Using %s with ", processes ? "processes" : "threads");
    476
    477	if (actions == &pipe_actions)
    478		printf("pipe");
    479	else if (actions == &yield_actions)
    480		printf("yield");
    481	else
    482		printf("futex");
    483
    484	if (!have_hwcap(PPC_FEATURE_HAS_ALTIVEC))
    485		touch_altivec = 0;
    486
    487	if (!have_hwcap(PPC_FEATURE_HAS_VSX))
    488		touch_vector = 0;
    489
    490	printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
    491	       cpu1, cpu2, touch_fp ?  "yes" : "no", touch_altivec ? "yes" : "no",
    492	       touch_vector ? "yes" : "no", touch_vdso ? "yes" : "no");
    493
    494	/* Create a new process group so we can signal everyone for exit */
    495	setpgid(getpid(), getpid());
    496
    497	signal(SIGUSR1, sigusr1_handler);
    498
    499	actions->setup(cpu1, cpu2);
    500
    501	start_fn(actions->thread1, NULL, cpu1);
    502	start_fn(actions->thread2, NULL, cpu2);
    503
    504	while (1)
    505		sleep(3600);
    506
    507	return 0;
    508}