brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 96554e2 Raw
509 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Context switch microbenchmark.4 *5 * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM6 */7 8#define _GNU_SOURCE9#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#endif29#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 function47 * 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#endif54 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#endif67 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 0171#define WRITE 1172 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#endif397	{ "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#endif412	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	else466		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	else482		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}509