Linux-libre 4.19.8-gnu
[librecmc/linux-libre.git] / tools / testing / selftests / powerpc / benchmarks / fork.c
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
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
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
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
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
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
129 static void *null_fn(void *arg)
130 {
131         pthread_exit(NULL);
132 }
133
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
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
190 static void sigusr1_handler(int junk)
191 {
192         exit(0);
193 }
194
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
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
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 }