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