162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Context switch microbenchmark.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright 2018, Anton Blanchard, IBM Corp.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define _GNU_SOURCE
1062306a36Sopenharmony_ci#include <assert.h>
1162306a36Sopenharmony_ci#include <errno.h>
1262306a36Sopenharmony_ci#include <getopt.h>
1362306a36Sopenharmony_ci#include <limits.h>
1462306a36Sopenharmony_ci#include <linux/futex.h>
1562306a36Sopenharmony_ci#include <pthread.h>
1662306a36Sopenharmony_ci#include <sched.h>
1762306a36Sopenharmony_ci#include <signal.h>
1862306a36Sopenharmony_ci#include <stdio.h>
1962306a36Sopenharmony_ci#include <stdlib.h>
2062306a36Sopenharmony_ci#include <string.h>
2162306a36Sopenharmony_ci#include <sys/shm.h>
2262306a36Sopenharmony_ci#include <sys/syscall.h>
2362306a36Sopenharmony_ci#include <sys/time.h>
2462306a36Sopenharmony_ci#include <sys/types.h>
2562306a36Sopenharmony_ci#include <sys/wait.h>
2662306a36Sopenharmony_ci#include <unistd.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic unsigned int timeout = 30;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void set_cpu(int cpu)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	cpu_set_t cpuset;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (cpu == -1)
3562306a36Sopenharmony_ci		return;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	CPU_ZERO(&cpuset);
3862306a36Sopenharmony_ci	CPU_SET(cpu, &cpuset);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {
4162306a36Sopenharmony_ci		perror("sched_setaffinity");
4262306a36Sopenharmony_ci		exit(1);
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void start_process_on(void *(*fn)(void *), void *arg, int cpu)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int pid;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	pid = fork();
5162306a36Sopenharmony_ci	if (pid == -1) {
5262306a36Sopenharmony_ci		perror("fork");
5362306a36Sopenharmony_ci		exit(1);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (pid)
5762306a36Sopenharmony_ci		return;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	set_cpu(cpu);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	fn(arg);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	exit(0);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int cpu;
6762306a36Sopenharmony_cistatic int do_fork = 0;
6862306a36Sopenharmony_cistatic int do_vfork = 0;
6962306a36Sopenharmony_cistatic int do_exec = 0;
7062306a36Sopenharmony_cistatic char *exec_file;
7162306a36Sopenharmony_cistatic int exec_target = 0;
7262306a36Sopenharmony_cistatic unsigned long iterations;
7362306a36Sopenharmony_cistatic unsigned long iterations_prev;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void run_exec(void)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	char *const argv[] = { "./exec_target", NULL };
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (execve("./exec_target", argv, NULL) == -1) {
8062306a36Sopenharmony_ci		perror("execve");
8162306a36Sopenharmony_ci		exit(1);
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void bench_fork(void)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	while (1) {
8862306a36Sopenharmony_ci		pid_t pid = fork();
8962306a36Sopenharmony_ci		if (pid == -1) {
9062306a36Sopenharmony_ci			perror("fork");
9162306a36Sopenharmony_ci			exit(1);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci		if (pid == 0) {
9462306a36Sopenharmony_ci			if (do_exec)
9562306a36Sopenharmony_ci				run_exec();
9662306a36Sopenharmony_ci			_exit(0);
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci		pid = waitpid(pid, NULL, 0);
9962306a36Sopenharmony_ci		if (pid == -1) {
10062306a36Sopenharmony_ci			perror("waitpid");
10162306a36Sopenharmony_ci			exit(1);
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci		iterations++;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void bench_vfork(void)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	while (1) {
11062306a36Sopenharmony_ci		pid_t pid = vfork();
11162306a36Sopenharmony_ci		if (pid == -1) {
11262306a36Sopenharmony_ci			perror("fork");
11362306a36Sopenharmony_ci			exit(1);
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci		if (pid == 0) {
11662306a36Sopenharmony_ci			if (do_exec)
11762306a36Sopenharmony_ci				run_exec();
11862306a36Sopenharmony_ci			_exit(0);
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci		pid = waitpid(pid, NULL, 0);
12162306a36Sopenharmony_ci		if (pid == -1) {
12262306a36Sopenharmony_ci			perror("waitpid");
12362306a36Sopenharmony_ci			exit(1);
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci		iterations++;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void *null_fn(void *arg)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	pthread_exit(NULL);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void bench_thread(void)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	pthread_t tid;
13762306a36Sopenharmony_ci	cpu_set_t cpuset;
13862306a36Sopenharmony_ci	pthread_attr_t attr;
13962306a36Sopenharmony_ci	int rc;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	rc = pthread_attr_init(&attr);
14262306a36Sopenharmony_ci	if (rc) {
14362306a36Sopenharmony_ci		errno = rc;
14462306a36Sopenharmony_ci		perror("pthread_attr_init");
14562306a36Sopenharmony_ci		exit(1);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (cpu != -1) {
14962306a36Sopenharmony_ci		CPU_ZERO(&cpuset);
15062306a36Sopenharmony_ci		CPU_SET(cpu, &cpuset);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
15362306a36Sopenharmony_ci		if (rc) {
15462306a36Sopenharmony_ci			errno = rc;
15562306a36Sopenharmony_ci			perror("pthread_attr_setaffinity_np");
15662306a36Sopenharmony_ci			exit(1);
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	while (1) {
16162306a36Sopenharmony_ci		rc = pthread_create(&tid, &attr, null_fn, NULL);
16262306a36Sopenharmony_ci		if (rc) {
16362306a36Sopenharmony_ci			errno = rc;
16462306a36Sopenharmony_ci			perror("pthread_create");
16562306a36Sopenharmony_ci			exit(1);
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci		rc = pthread_join(tid, NULL);
16862306a36Sopenharmony_ci		if (rc) {
16962306a36Sopenharmony_ci			errno = rc;
17062306a36Sopenharmony_ci			perror("pthread_join");
17162306a36Sopenharmony_ci			exit(1);
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci		iterations++;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void sigalrm_handler(int junk)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	unsigned long i = iterations;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	printf("%ld\n", i - iterations_prev);
18262306a36Sopenharmony_ci	iterations_prev = i;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (--timeout == 0)
18562306a36Sopenharmony_ci		kill(0, SIGUSR1);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	alarm(1);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void sigusr1_handler(int junk)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	exit(0);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic void *bench_proc(void *arg)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	signal(SIGALRM, sigalrm_handler);
19862306a36Sopenharmony_ci	alarm(1);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (do_fork)
20162306a36Sopenharmony_ci		bench_fork();
20262306a36Sopenharmony_ci	else if (do_vfork)
20362306a36Sopenharmony_ci		bench_vfork();
20462306a36Sopenharmony_ci	else
20562306a36Sopenharmony_ci		bench_thread();
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return NULL;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic struct option options[] = {
21162306a36Sopenharmony_ci	{ "fork", no_argument, &do_fork, 1 },
21262306a36Sopenharmony_ci	{ "vfork", no_argument, &do_vfork, 1 },
21362306a36Sopenharmony_ci	{ "exec", no_argument, &do_exec, 1 },
21462306a36Sopenharmony_ci	{ "timeout", required_argument, 0, 's' },
21562306a36Sopenharmony_ci	{ "exec-target", no_argument, &exec_target, 1 },
21662306a36Sopenharmony_ci	{ NULL },
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void usage(void)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	fprintf(stderr, "Usage: fork <options> CPU\n\n");
22262306a36Sopenharmony_ci	fprintf(stderr, "\t\t--fork\tUse fork() (default threads)\n");
22362306a36Sopenharmony_ci	fprintf(stderr, "\t\t--vfork\tUse vfork() (default threads)\n");
22462306a36Sopenharmony_ci	fprintf(stderr, "\t\t--exec\tAlso exec() (default no exec)\n");
22562306a36Sopenharmony_ci	fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
22662306a36Sopenharmony_ci	fprintf(stderr, "\t\t--exec-target\tInternal option for exec workload\n");
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ciint main(int argc, char *argv[])
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	signed char c;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	while (1) {
23462306a36Sopenharmony_ci		int option_index = 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		c = getopt_long(argc, argv, "", options, &option_index);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		if (c == -1)
23962306a36Sopenharmony_ci			break;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		switch (c) {
24262306a36Sopenharmony_ci		case 0:
24362306a36Sopenharmony_ci			if (options[option_index].flag != 0)
24462306a36Sopenharmony_ci				break;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci			usage();
24762306a36Sopenharmony_ci			exit(1);
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		case 's':
25162306a36Sopenharmony_ci			timeout = atoi(optarg);
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		default:
25562306a36Sopenharmony_ci			usage();
25662306a36Sopenharmony_ci			exit(1);
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (do_fork && do_vfork) {
26162306a36Sopenharmony_ci		usage();
26262306a36Sopenharmony_ci		exit(1);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci	if (do_exec && !do_fork && !do_vfork) {
26562306a36Sopenharmony_ci		usage();
26662306a36Sopenharmony_ci		exit(1);
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (do_exec) {
27062306a36Sopenharmony_ci		char *dirname = strdup(argv[0]);
27162306a36Sopenharmony_ci		int i;
27262306a36Sopenharmony_ci		i = strlen(dirname) - 1;
27362306a36Sopenharmony_ci		while (i) {
27462306a36Sopenharmony_ci			if (dirname[i] == '/') {
27562306a36Sopenharmony_ci				dirname[i] = '\0';
27662306a36Sopenharmony_ci				if (chdir(dirname) == -1) {
27762306a36Sopenharmony_ci					perror("chdir");
27862306a36Sopenharmony_ci					exit(1);
27962306a36Sopenharmony_ci				}
28062306a36Sopenharmony_ci				break;
28162306a36Sopenharmony_ci			}
28262306a36Sopenharmony_ci			i--;
28362306a36Sopenharmony_ci		}
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (exec_target) {
28762306a36Sopenharmony_ci		exit(0);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (((argc - optind) != 1)) {
29162306a36Sopenharmony_ci		cpu = -1;
29262306a36Sopenharmony_ci	} else {
29362306a36Sopenharmony_ci		cpu = atoi(argv[optind++]);
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (do_exec)
29762306a36Sopenharmony_ci		exec_file = argv[0];
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	set_cpu(cpu);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	printf("Using ");
30262306a36Sopenharmony_ci	if (do_fork)
30362306a36Sopenharmony_ci		printf("fork");
30462306a36Sopenharmony_ci	else if (do_vfork)
30562306a36Sopenharmony_ci		printf("vfork");
30662306a36Sopenharmony_ci	else
30762306a36Sopenharmony_ci		printf("clone");
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (do_exec)
31062306a36Sopenharmony_ci		printf(" + exec");
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	printf(" on cpu %d\n", cpu);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Create a new process group so we can signal everyone for exit */
31562306a36Sopenharmony_ci	setpgid(getpid(), getpid());
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	signal(SIGUSR1, sigusr1_handler);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	start_process_on(bench_proc, NULL, cpu);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	while (1)
32262306a36Sopenharmony_ci		sleep(3600);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return 0;
32562306a36Sopenharmony_ci}
326