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