1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Context switch microbenchmark.
5 * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
21 #include <sys/syscall.h>
22 #include <sys/sysinfo.h>
23 #include <sys/types.h>
25 #include <linux/futex.h>
31 static unsigned int timeout
= 30;
33 static int touch_vdso
;
36 static int touch_fp
= 1;
39 static int touch_vector
= 1;
43 static int touch_altivec
= 1;
46 * Note: LTO (Link Time Optimisation) doesn't play well with this function
47 * attribute. Be very careful enabling LTO for this test.
49 static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
55 static void touch(void)
58 gettimeofday(&tv
, NULL
);
71 asm volatile("# %0 %1 %2": : "r"(&tv
), "r"(&fp
), "r"(&c
));
74 static void start_thread_on(void *(*fn
)(void *), void *arg
, unsigned long cpu
)
82 CPU_SET(cpu
, &cpuset
);
84 rc
= pthread_attr_init(&attr
);
87 perror("pthread_attr_init");
91 rc
= pthread_attr_setaffinity_np(&attr
, sizeof(cpu_set_t
), &cpuset
);
94 perror("pthread_attr_setaffinity_np");
98 rc
= pthread_create(&tid
, &attr
, fn
, arg
);
101 perror("pthread_create");
106 static void start_process_on(void *(*fn
)(void *), void *arg
, unsigned long cpu
)
121 ncpus
= get_nprocs();
122 size
= CPU_ALLOC_SIZE(ncpus
);
123 cpuset
= CPU_ALLOC(ncpus
);
128 CPU_ZERO_S(size
, cpuset
);
129 CPU_SET_S(cpu
, size
, cpuset
);
131 if (sched_setaffinity(0, size
, cpuset
)) {
132 perror("sched_setaffinity");
143 static unsigned long iterations
;
144 static unsigned long iterations_prev
;
146 static void sigalrm_handler(int junk
)
148 unsigned long i
= iterations
;
150 printf("%ld\n", i
- iterations_prev
);
159 static void sigusr1_handler(int junk
)
165 void (*setup
)(int, int);
166 void *(*thread1
)(void *);
167 void *(*thread2
)(void *);
173 static int pipe_fd1
[2];
174 static int pipe_fd2
[2];
176 static void pipe_setup(int cpu1
, int cpu2
)
178 if (pipe(pipe_fd1
) || pipe(pipe_fd2
))
182 static void *pipe_thread1(void *arg
)
184 signal(SIGALRM
, sigalrm_handler
);
188 assert(read(pipe_fd1
[READ
], &c
, 1) == 1);
191 assert(write(pipe_fd2
[WRITE
], &c
, 1) == 1);
200 static void *pipe_thread2(void *arg
)
203 assert(write(pipe_fd1
[WRITE
], &c
, 1) == 1);
206 assert(read(pipe_fd2
[READ
], &c
, 1) == 1);
213 static struct actions pipe_actions
= {
215 .thread1
= pipe_thread1
,
216 .thread2
= pipe_thread2
,
219 static void yield_setup(int cpu1
, int cpu2
)
222 fprintf(stderr
, "Both threads must be on the same CPU for yield test\n");
227 static void *yield_thread1(void *arg
)
229 signal(SIGALRM
, sigalrm_handler
);
242 static void *yield_thread2(void *arg
)
252 static struct actions yield_actions
= {
253 .setup
= yield_setup
,
254 .thread1
= yield_thread1
,
255 .thread2
= yield_thread2
,
258 static long sys_futex(void *addr1
, int op
, int val1
, struct timespec
*timeout
,
259 void *addr2
, int val3
)
261 return syscall(SYS_futex
, addr1
, op
, val1
, timeout
, addr2
, val3
);
264 static unsigned long cmpxchg(unsigned long *p
, unsigned long expected
,
265 unsigned long desired
)
267 unsigned long exp
= expected
;
269 __atomic_compare_exchange_n(p
, &exp
, desired
, 0,
270 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
);
274 static unsigned long xchg(unsigned long *p
, unsigned long val
)
276 return __atomic_exchange_n(p
, val
, __ATOMIC_SEQ_CST
);
279 static int processes
;
281 static int mutex_lock(unsigned long *m
)
284 int flags
= FUTEX_WAIT
;
286 flags
|= FUTEX_PRIVATE_FLAG
;
288 c
= cmpxchg(m
, 0, 1);
296 sys_futex(m
, flags
, 2, NULL
, NULL
, 0);
303 static int mutex_unlock(unsigned long *m
)
305 int flags
= FUTEX_WAKE
;
307 flags
|= FUTEX_PRIVATE_FLAG
;
311 else if (xchg(m
, 0) == 1)
314 sys_futex(m
, flags
, 1, NULL
, NULL
, 0);
319 static unsigned long *m1
, *m2
;
321 static void futex_setup(int cpu1
, int cpu2
)
324 static unsigned long _m1
, _m2
;
331 shmid
= shmget(IPC_PRIVATE
, getpagesize(), SHM_R
| SHM_W
);
337 shmaddr
= shmat(shmid
, NULL
, 0);
338 if (shmaddr
== (char *)-1) {
340 shmctl(shmid
, IPC_RMID
, NULL
);
344 shmctl(shmid
, IPC_RMID
, NULL
);
347 m2
= shmaddr
+ sizeof(*m1
);
357 static void *futex_thread1(void *arg
)
359 signal(SIGALRM
, sigalrm_handler
);
372 static void *futex_thread2(void *arg
)
382 static struct actions futex_actions
= {
383 .setup
= futex_setup
,
384 .thread1
= futex_thread1
,
385 .thread2
= futex_thread2
,
388 static struct option options
[] = {
389 { "test", required_argument
, 0, 't' },
390 { "process", no_argument
, &processes
, 1 },
391 { "timeout", required_argument
, 0, 's' },
392 { "vdso", no_argument
, &touch_vdso
, 1 },
393 { "no-fp", no_argument
, &touch_fp
, 0 },
395 { "no-altivec", no_argument
, &touch_altivec
, 0 },
397 { "no-vector", no_argument
, &touch_vector
, 0 },
401 static void usage(void)
403 fprintf(stderr
, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
404 fprintf(stderr
, "\t\t--test=X\tpipe, futex or yield (default)\n");
405 fprintf(stderr
, "\t\t--process\tUse processes (default threads)\n");
406 fprintf(stderr
, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
407 fprintf(stderr
, "\t\t--vdso\t\ttouch VDSO\n");
408 fprintf(stderr
, "\t\t--no-fp\t\tDon't touch FP\n");
410 fprintf(stderr
, "\t\t--no-altivec\tDon't touch altivec\n");
412 fprintf(stderr
, "\t\t--no-vector\tDon't touch vector\n");
415 int main(int argc
, char *argv
[])
418 struct actions
*actions
= &yield_actions
;
421 static void (*start_fn
)(void *(*fn
)(void *), void *arg
, unsigned long cpu
);
424 int option_index
= 0;
426 c
= getopt_long(argc
, argv
, "", options
, &option_index
);
433 if (options
[option_index
].flag
!= 0)
441 if (!strcmp(optarg
, "pipe")) {
442 actions
= &pipe_actions
;
443 } else if (!strcmp(optarg
, "yield")) {
444 actions
= &yield_actions
;
445 } else if (!strcmp(optarg
, "futex")) {
446 actions
= &futex_actions
;
454 timeout
= atoi(optarg
);
464 start_fn
= start_process_on
;
466 start_fn
= start_thread_on
;
468 if (((argc
- optind
) != 2)) {
469 cpu1
= cpu2
= pick_online_cpu();
471 cpu1
= atoi(argv
[optind
++]);
472 cpu2
= atoi(argv
[optind
++]);
475 printf("Using %s with ", processes
? "processes" : "threads");
477 if (actions
== &pipe_actions
)
479 else if (actions
== &yield_actions
)
484 if (!have_hwcap(PPC_FEATURE_HAS_ALTIVEC
))
487 if (!have_hwcap(PPC_FEATURE_HAS_VSX
))
490 printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
491 cpu1
, cpu2
, touch_fp
? "yes" : "no", touch_altivec
? "yes" : "no",
492 touch_vector
? "yes" : "no", touch_vdso
? "yes" : "no");
494 /* Create a new process group so we can signal everyone for exit */
495 setpgid(getpid(), getpid());
497 signal(SIGUSR1
, sigusr1_handler
);
499 actions
->setup(cpu1
, cpu2
);
501 start_fn(actions
->thread1
, NULL
, cpu1
);
502 start_fn(actions
->thread2
, NULL
, cpu2
);