selftests/powerpc: Fix usage message in context_switch
[deliverable/linux.git] / tools / testing / selftests / powerpc / benchmarks / context_switch.c
1 /*
2 * Context switch microbenchmark.
3 *
4 * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12 #define _GNU_SOURCE
13 #include <sched.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include <getopt.h>
19 #include <signal.h>
20 #include <assert.h>
21 #include <pthread.h>
22 #include <limits.h>
23 #include <sys/time.h>
24 #include <sys/syscall.h>
25 #include <sys/types.h>
26 #include <sys/shm.h>
27 #include <linux/futex.h>
28
29 #include "../utils.h"
30
31 static unsigned int timeout = 30;
32
33 static int touch_vdso;
34 struct timeval tv;
35
36 static int touch_fp = 1;
37 double fp;
38
39 static int touch_vector = 1;
40 typedef int v4si __attribute__ ((vector_size (16)));
41 v4si a, b, c;
42
43 #ifdef __powerpc__
44 static int touch_altivec = 1;
45
46 static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
47 {
48 c = a + b;
49 }
50 #endif
51
52 static void touch(void)
53 {
54 if (touch_vdso)
55 gettimeofday(&tv, NULL);
56
57 if (touch_fp)
58 fp += 0.1;
59
60 #ifdef __powerpc__
61 if (touch_altivec)
62 altivec_touch_fn();
63 #endif
64
65 if (touch_vector)
66 c = a + b;
67
68 asm volatile("# %0 %1 %2": : "r"(&tv), "r"(&fp), "r"(&c));
69 }
70
71 static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu)
72 {
73 pthread_t tid;
74 cpu_set_t cpuset;
75 pthread_attr_t attr;
76
77 CPU_ZERO(&cpuset);
78 CPU_SET(cpu, &cpuset);
79
80 pthread_attr_init(&attr);
81
82 if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset)) {
83 perror("pthread_attr_setaffinity_np");
84 exit(1);
85 }
86
87 if (pthread_create(&tid, &attr, fn, arg)) {
88 perror("pthread_create");
89 exit(1);
90 }
91 }
92
93 static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu)
94 {
95 int pid;
96 cpu_set_t cpuset;
97
98 pid = fork();
99 if (pid == -1) {
100 perror("fork");
101 exit(1);
102 }
103
104 if (pid)
105 return;
106
107 CPU_ZERO(&cpuset);
108 CPU_SET(cpu, &cpuset);
109
110 if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {
111 perror("sched_setaffinity");
112 exit(1);
113 }
114
115 fn(arg);
116
117 exit(0);
118 }
119
120 static unsigned long iterations;
121 static unsigned long iterations_prev;
122
123 static void sigalrm_handler(int junk)
124 {
125 unsigned long i = iterations;
126
127 printf("%ld\n", i - iterations_prev);
128 iterations_prev = i;
129
130 if (--timeout == 0)
131 kill(0, SIGUSR1);
132
133 alarm(1);
134 }
135
136 static void sigusr1_handler(int junk)
137 {
138 exit(0);
139 }
140
141 struct actions {
142 void (*setup)(int, int);
143 void *(*thread1)(void *);
144 void *(*thread2)(void *);
145 };
146
147 #define READ 0
148 #define WRITE 1
149
150 static int pipe_fd1[2];
151 static int pipe_fd2[2];
152
153 static void pipe_setup(int cpu1, int cpu2)
154 {
155 if (pipe(pipe_fd1) || pipe(pipe_fd2))
156 exit(1);
157 }
158
159 static void *pipe_thread1(void *arg)
160 {
161 signal(SIGALRM, sigalrm_handler);
162 alarm(1);
163
164 while (1) {
165 assert(read(pipe_fd1[READ], &c, 1) == 1);
166 touch();
167
168 assert(write(pipe_fd2[WRITE], &c, 1) == 1);
169 touch();
170
171 iterations += 2;
172 }
173
174 return NULL;
175 }
176
177 static void *pipe_thread2(void *arg)
178 {
179 while (1) {
180 assert(write(pipe_fd1[WRITE], &c, 1) == 1);
181 touch();
182
183 assert(read(pipe_fd2[READ], &c, 1) == 1);
184 touch();
185 }
186
187 return NULL;
188 }
189
190 static struct actions pipe_actions = {
191 .setup = pipe_setup,
192 .thread1 = pipe_thread1,
193 .thread2 = pipe_thread2,
194 };
195
196 static void yield_setup(int cpu1, int cpu2)
197 {
198 if (cpu1 != cpu2) {
199 fprintf(stderr, "Both threads must be on the same CPU for yield test\n");
200 exit(1);
201 }
202 }
203
204 static void *yield_thread1(void *arg)
205 {
206 signal(SIGALRM, sigalrm_handler);
207 alarm(1);
208
209 while (1) {
210 sched_yield();
211 touch();
212
213 iterations += 2;
214 }
215
216 return NULL;
217 }
218
219 static void *yield_thread2(void *arg)
220 {
221 while (1) {
222 sched_yield();
223 touch();
224 }
225
226 return NULL;
227 }
228
229 static struct actions yield_actions = {
230 .setup = yield_setup,
231 .thread1 = yield_thread1,
232 .thread2 = yield_thread2,
233 };
234
235 static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
236 void *addr2, int val3)
237 {
238 return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
239 }
240
241 static unsigned long cmpxchg(unsigned long *p, unsigned long expected,
242 unsigned long desired)
243 {
244 unsigned long exp = expected;
245
246 __atomic_compare_exchange_n(p, &exp, desired, 0,
247 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
248 return exp;
249 }
250
251 static unsigned long xchg(unsigned long *p, unsigned long val)
252 {
253 return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST);
254 }
255
256 static int mutex_lock(unsigned long *m)
257 {
258 int c;
259
260 c = cmpxchg(m, 0, 1);
261 if (!c)
262 return 0;
263
264 if (c == 1)
265 c = xchg(m, 2);
266
267 while (c) {
268 sys_futex(m, FUTEX_WAIT, 2, NULL, NULL, 0);
269 c = xchg(m, 2);
270 }
271
272 return 0;
273 }
274
275 static int mutex_unlock(unsigned long *m)
276 {
277 if (*m == 2)
278 *m = 0;
279 else if (xchg(m, 0) == 1)
280 return 0;
281
282 sys_futex(m, FUTEX_WAKE, 1, NULL, NULL, 0);
283
284 return 0;
285 }
286
287 static unsigned long *m1, *m2;
288
289 static void futex_setup(int cpu1, int cpu2)
290 {
291 int shmid;
292 void *shmaddr;
293
294 shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W);
295 if (shmid < 0) {
296 perror("shmget");
297 exit(1);
298 }
299
300 shmaddr = shmat(shmid, NULL, 0);
301 if (shmaddr == (char *)-1) {
302 perror("shmat");
303 shmctl(shmid, IPC_RMID, NULL);
304 exit(1);
305 }
306
307 shmctl(shmid, IPC_RMID, NULL);
308
309 m1 = shmaddr;
310 m2 = shmaddr + sizeof(*m1);
311
312 *m1 = 0;
313 *m2 = 0;
314
315 mutex_lock(m1);
316 mutex_lock(m2);
317 }
318
319 static void *futex_thread1(void *arg)
320 {
321 signal(SIGALRM, sigalrm_handler);
322 alarm(1);
323
324 while (1) {
325 mutex_lock(m2);
326 mutex_unlock(m1);
327
328 iterations += 2;
329 }
330
331 return NULL;
332 }
333
334 static void *futex_thread2(void *arg)
335 {
336 while (1) {
337 mutex_unlock(m2);
338 mutex_lock(m1);
339 }
340
341 return NULL;
342 }
343
344 static struct actions futex_actions = {
345 .setup = futex_setup,
346 .thread1 = futex_thread1,
347 .thread2 = futex_thread2,
348 };
349
350 static int processes;
351
352 static struct option options[] = {
353 { "test", required_argument, 0, 't' },
354 { "process", no_argument, &processes, 1 },
355 { "timeout", required_argument, 0, 's' },
356 { "vdso", no_argument, &touch_vdso, 1 },
357 { "no-fp", no_argument, &touch_fp, 0 },
358 #ifdef __powerpc__
359 { "no-altivec", no_argument, &touch_altivec, 0 },
360 #endif
361 { "no-vector", no_argument, &touch_vector, 0 },
362 { 0, },
363 };
364
365 static void usage(void)
366 {
367 fprintf(stderr, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
368 fprintf(stderr, "\t\t--test=X\tpipe, futex or yield (default)\n");
369 fprintf(stderr, "\t\t--process\tUse processes (default threads)\n");
370 fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
371 fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n");
372 fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n");
373 #ifdef __powerpc__
374 fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n");
375 #endif
376 fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n");
377 }
378
379 int main(int argc, char *argv[])
380 {
381 signed char c;
382 struct actions *actions = &yield_actions;
383 int cpu1;
384 int cpu2;
385 static void (*start_fn)(void *(*fn)(void *), void *arg, unsigned long cpu);
386
387 while (1) {
388 int option_index = 0;
389
390 c = getopt_long(argc, argv, "", options, &option_index);
391
392 if (c == -1)
393 break;
394
395 switch (c) {
396 case 0:
397 if (options[option_index].flag != 0)
398 break;
399
400 usage();
401 exit(1);
402 break;
403
404 case 't':
405 if (!strcmp(optarg, "pipe")) {
406 actions = &pipe_actions;
407 } else if (!strcmp(optarg, "yield")) {
408 actions = &yield_actions;
409 } else if (!strcmp(optarg, "futex")) {
410 actions = &futex_actions;
411 } else {
412 usage();
413 exit(1);
414 }
415 break;
416
417 case 's':
418 timeout = atoi(optarg);
419 break;
420
421 default:
422 usage();
423 exit(1);
424 }
425 }
426
427 if (processes)
428 start_fn = start_process_on;
429 else
430 start_fn = start_thread_on;
431
432 if (((argc - optind) != 2)) {
433 cpu1 = cpu2 = pick_online_cpu();
434 } else {
435 cpu1 = atoi(argv[optind++]);
436 cpu2 = atoi(argv[optind++]);
437 }
438
439 printf("Using %s with ", processes ? "processes" : "threads");
440
441 if (actions == &pipe_actions)
442 printf("pipe");
443 else if (actions == &yield_actions)
444 printf("yield");
445 else
446 printf("futex");
447
448 printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
449 cpu1, cpu2, touch_fp ? "yes" : "no", touch_altivec ? "yes" : "no",
450 touch_vector ? "yes" : "no", touch_vdso ? "yes" : "no");
451
452 /* Create a new process group so we can signal everyone for exit */
453 setpgid(getpid(), getpid());
454
455 signal(SIGUSR1, sigusr1_handler);
456
457 actions->setup(cpu1, cpu2);
458
459 start_fn(actions->thread1, NULL, cpu1);
460 start_fn(actions->thread2, NULL, cpu2);
461
462 while (1)
463 sleep(3600);
464
465 return 0;
466 }
This page took 0.067117 seconds and 5 git commands to generate.