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