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