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