xref: /linux/tools/testing/selftests/powerpc/benchmarks/fork.c (revision c1144d29f405ce1f4e6ede6482beb3d0d09750c6)
1*dd40c5b4SNicholas Piggin // SPDX-License-Identifier: GPL-2.0+
2*dd40c5b4SNicholas Piggin 
3*dd40c5b4SNicholas Piggin /*
4*dd40c5b4SNicholas Piggin  * Context switch microbenchmark.
5*dd40c5b4SNicholas Piggin  *
6*dd40c5b4SNicholas Piggin  * Copyright 2018, Anton Blanchard, IBM Corp.
7*dd40c5b4SNicholas Piggin  */
8*dd40c5b4SNicholas Piggin 
9*dd40c5b4SNicholas Piggin #define _GNU_SOURCE
10*dd40c5b4SNicholas Piggin #include <assert.h>
11*dd40c5b4SNicholas Piggin #include <errno.h>
12*dd40c5b4SNicholas Piggin #include <getopt.h>
13*dd40c5b4SNicholas Piggin #include <limits.h>
14*dd40c5b4SNicholas Piggin #include <linux/futex.h>
15*dd40c5b4SNicholas Piggin #include <pthread.h>
16*dd40c5b4SNicholas Piggin #include <sched.h>
17*dd40c5b4SNicholas Piggin #include <signal.h>
18*dd40c5b4SNicholas Piggin #include <stdio.h>
19*dd40c5b4SNicholas Piggin #include <stdlib.h>
20*dd40c5b4SNicholas Piggin #include <string.h>
21*dd40c5b4SNicholas Piggin #include <sys/shm.h>
22*dd40c5b4SNicholas Piggin #include <sys/syscall.h>
23*dd40c5b4SNicholas Piggin #include <sys/time.h>
24*dd40c5b4SNicholas Piggin #include <sys/types.h>
25*dd40c5b4SNicholas Piggin #include <sys/wait.h>
26*dd40c5b4SNicholas Piggin #include <unistd.h>
27*dd40c5b4SNicholas Piggin 
28*dd40c5b4SNicholas Piggin static unsigned int timeout = 30;
29*dd40c5b4SNicholas Piggin 
set_cpu(int cpu)30*dd40c5b4SNicholas Piggin static void set_cpu(int cpu)
31*dd40c5b4SNicholas Piggin {
32*dd40c5b4SNicholas Piggin 	cpu_set_t cpuset;
33*dd40c5b4SNicholas Piggin 
34*dd40c5b4SNicholas Piggin 	if (cpu == -1)
35*dd40c5b4SNicholas Piggin 		return;
36*dd40c5b4SNicholas Piggin 
37*dd40c5b4SNicholas Piggin 	CPU_ZERO(&cpuset);
38*dd40c5b4SNicholas Piggin 	CPU_SET(cpu, &cpuset);
39*dd40c5b4SNicholas Piggin 
40*dd40c5b4SNicholas Piggin 	if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {
41*dd40c5b4SNicholas Piggin 		perror("sched_setaffinity");
42*dd40c5b4SNicholas Piggin 		exit(1);
43*dd40c5b4SNicholas Piggin 	}
44*dd40c5b4SNicholas Piggin }
45*dd40c5b4SNicholas Piggin 
start_process_on(void * (* fn)(void *),void * arg,int cpu)46*dd40c5b4SNicholas Piggin static void start_process_on(void *(*fn)(void *), void *arg, int cpu)
47*dd40c5b4SNicholas Piggin {
48*dd40c5b4SNicholas Piggin 	int pid;
49*dd40c5b4SNicholas Piggin 
50*dd40c5b4SNicholas Piggin 	pid = fork();
51*dd40c5b4SNicholas Piggin 	if (pid == -1) {
52*dd40c5b4SNicholas Piggin 		perror("fork");
53*dd40c5b4SNicholas Piggin 		exit(1);
54*dd40c5b4SNicholas Piggin 	}
55*dd40c5b4SNicholas Piggin 
56*dd40c5b4SNicholas Piggin 	if (pid)
57*dd40c5b4SNicholas Piggin 		return;
58*dd40c5b4SNicholas Piggin 
59*dd40c5b4SNicholas Piggin 	set_cpu(cpu);
60*dd40c5b4SNicholas Piggin 
61*dd40c5b4SNicholas Piggin 	fn(arg);
62*dd40c5b4SNicholas Piggin 
63*dd40c5b4SNicholas Piggin 	exit(0);
64*dd40c5b4SNicholas Piggin }
65*dd40c5b4SNicholas Piggin 
66*dd40c5b4SNicholas Piggin static int cpu;
67*dd40c5b4SNicholas Piggin static int do_fork = 0;
68*dd40c5b4SNicholas Piggin static int do_vfork = 0;
69*dd40c5b4SNicholas Piggin static int do_exec = 0;
70*dd40c5b4SNicholas Piggin static char *exec_file;
71*dd40c5b4SNicholas Piggin static int exec_target = 0;
72*dd40c5b4SNicholas Piggin static unsigned long iterations;
73*dd40c5b4SNicholas Piggin static unsigned long iterations_prev;
74*dd40c5b4SNicholas Piggin 
run_exec(void)75*dd40c5b4SNicholas Piggin static void run_exec(void)
76*dd40c5b4SNicholas Piggin {
77*dd40c5b4SNicholas Piggin 	char *const argv[] = { "./exec_target", NULL };
78*dd40c5b4SNicholas Piggin 
79*dd40c5b4SNicholas Piggin 	if (execve("./exec_target", argv, NULL) == -1) {
80*dd40c5b4SNicholas Piggin 		perror("execve");
81*dd40c5b4SNicholas Piggin 		exit(1);
82*dd40c5b4SNicholas Piggin 	}
83*dd40c5b4SNicholas Piggin }
84*dd40c5b4SNicholas Piggin 
bench_fork(void)85*dd40c5b4SNicholas Piggin static void bench_fork(void)
86*dd40c5b4SNicholas Piggin {
87*dd40c5b4SNicholas Piggin 	while (1) {
88*dd40c5b4SNicholas Piggin 		pid_t pid = fork();
89*dd40c5b4SNicholas Piggin 		if (pid == -1) {
90*dd40c5b4SNicholas Piggin 			perror("fork");
91*dd40c5b4SNicholas Piggin 			exit(1);
92*dd40c5b4SNicholas Piggin 		}
93*dd40c5b4SNicholas Piggin 		if (pid == 0) {
94*dd40c5b4SNicholas Piggin 			if (do_exec)
95*dd40c5b4SNicholas Piggin 				run_exec();
96*dd40c5b4SNicholas Piggin 			_exit(0);
97*dd40c5b4SNicholas Piggin 		}
98*dd40c5b4SNicholas Piggin 		pid = waitpid(pid, NULL, 0);
99*dd40c5b4SNicholas Piggin 		if (pid == -1) {
100*dd40c5b4SNicholas Piggin 			perror("waitpid");
101*dd40c5b4SNicholas Piggin 			exit(1);
102*dd40c5b4SNicholas Piggin 		}
103*dd40c5b4SNicholas Piggin 		iterations++;
104*dd40c5b4SNicholas Piggin 	}
105*dd40c5b4SNicholas Piggin }
106*dd40c5b4SNicholas Piggin 
bench_vfork(void)107*dd40c5b4SNicholas Piggin static void bench_vfork(void)
108*dd40c5b4SNicholas Piggin {
109*dd40c5b4SNicholas Piggin 	while (1) {
110*dd40c5b4SNicholas Piggin 		pid_t pid = vfork();
111*dd40c5b4SNicholas Piggin 		if (pid == -1) {
112*dd40c5b4SNicholas Piggin 			perror("fork");
113*dd40c5b4SNicholas Piggin 			exit(1);
114*dd40c5b4SNicholas Piggin 		}
115*dd40c5b4SNicholas Piggin 		if (pid == 0) {
116*dd40c5b4SNicholas Piggin 			if (do_exec)
117*dd40c5b4SNicholas Piggin 				run_exec();
118*dd40c5b4SNicholas Piggin 			_exit(0);
119*dd40c5b4SNicholas Piggin 		}
120*dd40c5b4SNicholas Piggin 		pid = waitpid(pid, NULL, 0);
121*dd40c5b4SNicholas Piggin 		if (pid == -1) {
122*dd40c5b4SNicholas Piggin 			perror("waitpid");
123*dd40c5b4SNicholas Piggin 			exit(1);
124*dd40c5b4SNicholas Piggin 		}
125*dd40c5b4SNicholas Piggin 		iterations++;
126*dd40c5b4SNicholas Piggin 	}
127*dd40c5b4SNicholas Piggin }
128*dd40c5b4SNicholas Piggin 
null_fn(void * arg)129*dd40c5b4SNicholas Piggin static void *null_fn(void *arg)
130*dd40c5b4SNicholas Piggin {
131*dd40c5b4SNicholas Piggin 	pthread_exit(NULL);
132*dd40c5b4SNicholas Piggin }
133*dd40c5b4SNicholas Piggin 
bench_thread(void)134*dd40c5b4SNicholas Piggin static void bench_thread(void)
135*dd40c5b4SNicholas Piggin {
136*dd40c5b4SNicholas Piggin 	pthread_t tid;
137*dd40c5b4SNicholas Piggin 	cpu_set_t cpuset;
138*dd40c5b4SNicholas Piggin 	pthread_attr_t attr;
139*dd40c5b4SNicholas Piggin 	int rc;
140*dd40c5b4SNicholas Piggin 
141*dd40c5b4SNicholas Piggin 	rc = pthread_attr_init(&attr);
142*dd40c5b4SNicholas Piggin 	if (rc) {
143*dd40c5b4SNicholas Piggin 		errno = rc;
144*dd40c5b4SNicholas Piggin 		perror("pthread_attr_init");
145*dd40c5b4SNicholas Piggin 		exit(1);
146*dd40c5b4SNicholas Piggin 	}
147*dd40c5b4SNicholas Piggin 
148*dd40c5b4SNicholas Piggin 	if (cpu != -1) {
149*dd40c5b4SNicholas Piggin 		CPU_ZERO(&cpuset);
150*dd40c5b4SNicholas Piggin 		CPU_SET(cpu, &cpuset);
151*dd40c5b4SNicholas Piggin 
152*dd40c5b4SNicholas Piggin 		rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
153*dd40c5b4SNicholas Piggin 		if (rc) {
154*dd40c5b4SNicholas Piggin 			errno = rc;
155*dd40c5b4SNicholas Piggin 			perror("pthread_attr_setaffinity_np");
156*dd40c5b4SNicholas Piggin 			exit(1);
157*dd40c5b4SNicholas Piggin 		}
158*dd40c5b4SNicholas Piggin 	}
159*dd40c5b4SNicholas Piggin 
160*dd40c5b4SNicholas Piggin 	while (1) {
161*dd40c5b4SNicholas Piggin 		rc = pthread_create(&tid, &attr, null_fn, NULL);
162*dd40c5b4SNicholas Piggin 		if (rc) {
163*dd40c5b4SNicholas Piggin 			errno = rc;
164*dd40c5b4SNicholas Piggin 			perror("pthread_create");
165*dd40c5b4SNicholas Piggin 			exit(1);
166*dd40c5b4SNicholas Piggin 		}
167*dd40c5b4SNicholas Piggin 		rc = pthread_join(tid, NULL);
168*dd40c5b4SNicholas Piggin 		if (rc) {
169*dd40c5b4SNicholas Piggin 			errno = rc;
170*dd40c5b4SNicholas Piggin 			perror("pthread_join");
171*dd40c5b4SNicholas Piggin 			exit(1);
172*dd40c5b4SNicholas Piggin 		}
173*dd40c5b4SNicholas Piggin 		iterations++;
174*dd40c5b4SNicholas Piggin 	}
175*dd40c5b4SNicholas Piggin }
176*dd40c5b4SNicholas Piggin 
sigalrm_handler(int junk)177*dd40c5b4SNicholas Piggin static void sigalrm_handler(int junk)
178*dd40c5b4SNicholas Piggin {
179*dd40c5b4SNicholas Piggin 	unsigned long i = iterations;
180*dd40c5b4SNicholas Piggin 
181*dd40c5b4SNicholas Piggin 	printf("%ld\n", i - iterations_prev);
182*dd40c5b4SNicholas Piggin 	iterations_prev = i;
183*dd40c5b4SNicholas Piggin 
184*dd40c5b4SNicholas Piggin 	if (--timeout == 0)
185*dd40c5b4SNicholas Piggin 		kill(0, SIGUSR1);
186*dd40c5b4SNicholas Piggin 
187*dd40c5b4SNicholas Piggin 	alarm(1);
188*dd40c5b4SNicholas Piggin }
189*dd40c5b4SNicholas Piggin 
sigusr1_handler(int junk)190*dd40c5b4SNicholas Piggin static void sigusr1_handler(int junk)
191*dd40c5b4SNicholas Piggin {
192*dd40c5b4SNicholas Piggin 	exit(0);
193*dd40c5b4SNicholas Piggin }
194*dd40c5b4SNicholas Piggin 
bench_proc(void * arg)195*dd40c5b4SNicholas Piggin static void *bench_proc(void *arg)
196*dd40c5b4SNicholas Piggin {
197*dd40c5b4SNicholas Piggin 	signal(SIGALRM, sigalrm_handler);
198*dd40c5b4SNicholas Piggin 	alarm(1);
199*dd40c5b4SNicholas Piggin 
200*dd40c5b4SNicholas Piggin 	if (do_fork)
201*dd40c5b4SNicholas Piggin 		bench_fork();
202*dd40c5b4SNicholas Piggin 	else if (do_vfork)
203*dd40c5b4SNicholas Piggin 		bench_vfork();
204*dd40c5b4SNicholas Piggin 	else
205*dd40c5b4SNicholas Piggin 		bench_thread();
206*dd40c5b4SNicholas Piggin 
207*dd40c5b4SNicholas Piggin 	return NULL;
208*dd40c5b4SNicholas Piggin }
209*dd40c5b4SNicholas Piggin 
210*dd40c5b4SNicholas Piggin static struct option options[] = {
211*dd40c5b4SNicholas Piggin 	{ "fork", no_argument, &do_fork, 1 },
212*dd40c5b4SNicholas Piggin 	{ "vfork", no_argument, &do_vfork, 1 },
213*dd40c5b4SNicholas Piggin 	{ "exec", no_argument, &do_exec, 1 },
214*dd40c5b4SNicholas Piggin 	{ "timeout", required_argument, 0, 's' },
215*dd40c5b4SNicholas Piggin 	{ "exec-target", no_argument, &exec_target, 1 },
216*dd40c5b4SNicholas Piggin 	{ NULL },
217*dd40c5b4SNicholas Piggin };
218*dd40c5b4SNicholas Piggin 
usage(void)219*dd40c5b4SNicholas Piggin static void usage(void)
220*dd40c5b4SNicholas Piggin {
221*dd40c5b4SNicholas Piggin 	fprintf(stderr, "Usage: fork <options> CPU\n\n");
222*dd40c5b4SNicholas Piggin 	fprintf(stderr, "\t\t--fork\tUse fork() (default threads)\n");
223*dd40c5b4SNicholas Piggin 	fprintf(stderr, "\t\t--vfork\tUse vfork() (default threads)\n");
224*dd40c5b4SNicholas Piggin 	fprintf(stderr, "\t\t--exec\tAlso exec() (default no exec)\n");
225*dd40c5b4SNicholas Piggin 	fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
226*dd40c5b4SNicholas Piggin 	fprintf(stderr, "\t\t--exec-target\tInternal option for exec workload\n");
227*dd40c5b4SNicholas Piggin }
228*dd40c5b4SNicholas Piggin 
main(int argc,char * argv[])229*dd40c5b4SNicholas Piggin int main(int argc, char *argv[])
230*dd40c5b4SNicholas Piggin {
231*dd40c5b4SNicholas Piggin 	signed char c;
232*dd40c5b4SNicholas Piggin 
233*dd40c5b4SNicholas Piggin 	while (1) {
234*dd40c5b4SNicholas Piggin 		int option_index = 0;
235*dd40c5b4SNicholas Piggin 
236*dd40c5b4SNicholas Piggin 		c = getopt_long(argc, argv, "", options, &option_index);
237*dd40c5b4SNicholas Piggin 
238*dd40c5b4SNicholas Piggin 		if (c == -1)
239*dd40c5b4SNicholas Piggin 			break;
240*dd40c5b4SNicholas Piggin 
241*dd40c5b4SNicholas Piggin 		switch (c) {
242*dd40c5b4SNicholas Piggin 		case 0:
243*dd40c5b4SNicholas Piggin 			if (options[option_index].flag != 0)
244*dd40c5b4SNicholas Piggin 				break;
245*dd40c5b4SNicholas Piggin 
246*dd40c5b4SNicholas Piggin 			usage();
247*dd40c5b4SNicholas Piggin 			exit(1);
248*dd40c5b4SNicholas Piggin 			break;
249*dd40c5b4SNicholas Piggin 
250*dd40c5b4SNicholas Piggin 		case 's':
251*dd40c5b4SNicholas Piggin 			timeout = atoi(optarg);
252*dd40c5b4SNicholas Piggin 			break;
253*dd40c5b4SNicholas Piggin 
254*dd40c5b4SNicholas Piggin 		default:
255*dd40c5b4SNicholas Piggin 			usage();
256*dd40c5b4SNicholas Piggin 			exit(1);
257*dd40c5b4SNicholas Piggin 		}
258*dd40c5b4SNicholas Piggin 	}
259*dd40c5b4SNicholas Piggin 
260*dd40c5b4SNicholas Piggin 	if (do_fork && do_vfork) {
261*dd40c5b4SNicholas Piggin 		usage();
262*dd40c5b4SNicholas Piggin 		exit(1);
263*dd40c5b4SNicholas Piggin 	}
264*dd40c5b4SNicholas Piggin 	if (do_exec && !do_fork && !do_vfork) {
265*dd40c5b4SNicholas Piggin 		usage();
266*dd40c5b4SNicholas Piggin 		exit(1);
267*dd40c5b4SNicholas Piggin 	}
268*dd40c5b4SNicholas Piggin 
269*dd40c5b4SNicholas Piggin 	if (do_exec) {
270*dd40c5b4SNicholas Piggin 		char *dirname = strdup(argv[0]);
271*dd40c5b4SNicholas Piggin 		int i;
272*dd40c5b4SNicholas Piggin 		i = strlen(dirname) - 1;
273*dd40c5b4SNicholas Piggin 		while (i) {
274*dd40c5b4SNicholas Piggin 			if (dirname[i] == '/') {
275*dd40c5b4SNicholas Piggin 				dirname[i] = '\0';
276*dd40c5b4SNicholas Piggin 				if (chdir(dirname) == -1) {
277*dd40c5b4SNicholas Piggin 					perror("chdir");
278*dd40c5b4SNicholas Piggin 					exit(1);
279*dd40c5b4SNicholas Piggin 				}
280*dd40c5b4SNicholas Piggin 				break;
281*dd40c5b4SNicholas Piggin 			}
282*dd40c5b4SNicholas Piggin 			i--;
283*dd40c5b4SNicholas Piggin 		}
284*dd40c5b4SNicholas Piggin 	}
285*dd40c5b4SNicholas Piggin 
286*dd40c5b4SNicholas Piggin 	if (exec_target) {
287*dd40c5b4SNicholas Piggin 		exit(0);
288*dd40c5b4SNicholas Piggin 	}
289*dd40c5b4SNicholas Piggin 
290*dd40c5b4SNicholas Piggin 	if (((argc - optind) != 1)) {
291*dd40c5b4SNicholas Piggin 		cpu = -1;
292*dd40c5b4SNicholas Piggin 	} else {
293*dd40c5b4SNicholas Piggin 		cpu = atoi(argv[optind++]);
294*dd40c5b4SNicholas Piggin 	}
295*dd40c5b4SNicholas Piggin 
296*dd40c5b4SNicholas Piggin 	if (do_exec)
297*dd40c5b4SNicholas Piggin 		exec_file = argv[0];
298*dd40c5b4SNicholas Piggin 
299*dd40c5b4SNicholas Piggin 	set_cpu(cpu);
300*dd40c5b4SNicholas Piggin 
301*dd40c5b4SNicholas Piggin 	printf("Using ");
302*dd40c5b4SNicholas Piggin 	if (do_fork)
303*dd40c5b4SNicholas Piggin 		printf("fork");
304*dd40c5b4SNicholas Piggin 	else if (do_vfork)
305*dd40c5b4SNicholas Piggin 		printf("vfork");
306*dd40c5b4SNicholas Piggin 	else
307*dd40c5b4SNicholas Piggin 		printf("clone");
308*dd40c5b4SNicholas Piggin 
309*dd40c5b4SNicholas Piggin 	if (do_exec)
310*dd40c5b4SNicholas Piggin 		printf(" + exec");
311*dd40c5b4SNicholas Piggin 
312*dd40c5b4SNicholas Piggin 	printf(" on cpu %d\n", cpu);
313*dd40c5b4SNicholas Piggin 
314*dd40c5b4SNicholas Piggin 	/* Create a new process group so we can signal everyone for exit */
315*dd40c5b4SNicholas Piggin 	setpgid(getpid(), getpid());
316*dd40c5b4SNicholas Piggin 
317*dd40c5b4SNicholas Piggin 	signal(SIGUSR1, sigusr1_handler);
318*dd40c5b4SNicholas Piggin 
319*dd40c5b4SNicholas Piggin 	start_process_on(bench_proc, NULL, cpu);
320*dd40c5b4SNicholas Piggin 
321*dd40c5b4SNicholas Piggin 	while (1)
322*dd40c5b4SNicholas Piggin 		sleep(3600);
323*dd40c5b4SNicholas Piggin 
324*dd40c5b4SNicholas Piggin 	return 0;
325*dd40c5b4SNicholas Piggin }
326