2 * Context switch microbenchmark.
4 * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
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.
24 #include <sys/syscall.h>
25 #include <sys/types.h>
27 #include <linux/futex.h>
33 static unsigned int timeout
= 30;
35 static int touch_vdso
;
38 static int touch_fp
= 1;
41 static int touch_vector
= 1;
45 static int touch_altivec
= 1;
48 * Note: LTO (Link Time Optimisation) doesn't play well with this function
49 * attribute. Be very careful enabling LTO for this test.
51 static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
57 static void touch(void)
60 gettimeofday(&tv
, NULL
);
73 asm volatile("# %0 %1 %2": : "r"(&tv
), "r"(&fp
), "r"(&c
));
76 static void start_thread_on(void *(*fn
)(void *), void *arg
, unsigned long cpu
)
83 CPU_SET(cpu
, &cpuset
);
85 pthread_attr_init(&attr
);
87 if (pthread_attr_setaffinity_np(&attr
, sizeof(cpu_set_t
), &cpuset
)) {
88 perror("pthread_attr_setaffinity_np");
92 if (pthread_create(&tid
, &attr
, fn
, arg
)) {
93 perror("pthread_create");
98 static void start_process_on(void *(*fn
)(void *), void *arg
, unsigned long cpu
)
113 CPU_SET(cpu
, &cpuset
);
115 if (sched_setaffinity(0, sizeof(cpuset
), &cpuset
)) {
116 perror("sched_setaffinity");
125 static unsigned long iterations
;
126 static unsigned long iterations_prev
;
128 static void sigalrm_handler(int junk
)
130 unsigned long i
= iterations
;
132 printf("%ld\n", i
- iterations_prev
);
141 static void sigusr1_handler(int junk
)
147 void (*setup
)(int, int);
148 void *(*thread1
)(void *);
149 void *(*thread2
)(void *);
155 static int pipe_fd1
[2];
156 static int pipe_fd2
[2];
158 static void pipe_setup(int cpu1
, int cpu2
)
160 if (pipe(pipe_fd1
) || pipe(pipe_fd2
))
164 static void *pipe_thread1(void *arg
)
166 signal(SIGALRM
, sigalrm_handler
);
170 assert(read(pipe_fd1
[READ
], &c
, 1) == 1);
173 assert(write(pipe_fd2
[WRITE
], &c
, 1) == 1);
182 static void *pipe_thread2(void *arg
)
185 assert(write(pipe_fd1
[WRITE
], &c
, 1) == 1);
188 assert(read(pipe_fd2
[READ
], &c
, 1) == 1);
195 static struct actions pipe_actions
= {
197 .thread1
= pipe_thread1
,
198 .thread2
= pipe_thread2
,
201 static void yield_setup(int cpu1
, int cpu2
)
204 fprintf(stderr
, "Both threads must be on the same CPU for yield test\n");
209 static void *yield_thread1(void *arg
)
211 signal(SIGALRM
, sigalrm_handler
);
224 static void *yield_thread2(void *arg
)
234 static struct actions yield_actions
= {
235 .setup
= yield_setup
,
236 .thread1
= yield_thread1
,
237 .thread2
= yield_thread2
,
240 static long sys_futex(void *addr1
, int op
, int val1
, struct timespec
*timeout
,
241 void *addr2
, int val3
)
243 return syscall(SYS_futex
, addr1
, op
, val1
, timeout
, addr2
, val3
);
246 static unsigned long cmpxchg(unsigned long *p
, unsigned long expected
,
247 unsigned long desired
)
249 unsigned long exp
= expected
;
251 __atomic_compare_exchange_n(p
, &exp
, desired
, 0,
252 __ATOMIC_SEQ_CST
, __ATOMIC_SEQ_CST
);
256 static unsigned long xchg(unsigned long *p
, unsigned long val
)
258 return __atomic_exchange_n(p
, val
, __ATOMIC_SEQ_CST
);
261 static int mutex_lock(unsigned long *m
)
265 c
= cmpxchg(m
, 0, 1);
273 sys_futex(m
, FUTEX_WAIT
, 2, NULL
, NULL
, 0);
280 static int mutex_unlock(unsigned long *m
)
284 else if (xchg(m
, 0) == 1)
287 sys_futex(m
, FUTEX_WAKE
, 1, NULL
, NULL
, 0);
292 static unsigned long *m1
, *m2
;
294 static void futex_setup(int cpu1
, int cpu2
)
299 shmid
= shmget(IPC_PRIVATE
, getpagesize(), SHM_R
| SHM_W
);
305 shmaddr
= shmat(shmid
, NULL
, 0);
306 if (shmaddr
== (char *)-1) {
308 shmctl(shmid
, IPC_RMID
, NULL
);
312 shmctl(shmid
, IPC_RMID
, NULL
);
315 m2
= shmaddr
+ sizeof(*m1
);
324 static void *futex_thread1(void *arg
)
326 signal(SIGALRM
, sigalrm_handler
);
339 static void *futex_thread2(void *arg
)
349 static struct actions futex_actions
= {
350 .setup
= futex_setup
,
351 .thread1
= futex_thread1
,
352 .thread2
= futex_thread2
,
355 static int processes
;
357 static struct option options
[] = {
358 { "test", required_argument
, 0, 't' },
359 { "process", no_argument
, &processes
, 1 },
360 { "timeout", required_argument
, 0, 's' },
361 { "vdso", no_argument
, &touch_vdso
, 1 },
362 { "no-fp", no_argument
, &touch_fp
, 0 },
364 { "no-altivec", no_argument
, &touch_altivec
, 0 },
366 { "no-vector", no_argument
, &touch_vector
, 0 },
370 static void usage(void)
372 fprintf(stderr
, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
373 fprintf(stderr
, "\t\t--test=X\tpipe, futex or yield (default)\n");
374 fprintf(stderr
, "\t\t--process\tUse processes (default threads)\n");
375 fprintf(stderr
, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
376 fprintf(stderr
, "\t\t--vdso\t\ttouch VDSO\n");
377 fprintf(stderr
, "\t\t--no-fp\t\tDon't touch FP\n");
379 fprintf(stderr
, "\t\t--no-altivec\tDon't touch altivec\n");
381 fprintf(stderr
, "\t\t--no-vector\tDon't touch vector\n");
384 int main(int argc
, char *argv
[])
387 struct actions
*actions
= &yield_actions
;
390 static void (*start_fn
)(void *(*fn
)(void *), void *arg
, unsigned long cpu
);
393 int option_index
= 0;
395 c
= getopt_long(argc
, argv
, "", options
, &option_index
);
402 if (options
[option_index
].flag
!= 0)
410 if (!strcmp(optarg
, "pipe")) {
411 actions
= &pipe_actions
;
412 } else if (!strcmp(optarg
, "yield")) {
413 actions
= &yield_actions
;
414 } else if (!strcmp(optarg
, "futex")) {
415 actions
= &futex_actions
;
423 timeout
= atoi(optarg
);
433 start_fn
= start_process_on
;
435 start_fn
= start_thread_on
;
437 if (((argc
- optind
) != 2)) {
438 cpu1
= cpu2
= pick_online_cpu();
440 cpu1
= atoi(argv
[optind
++]);
441 cpu2
= atoi(argv
[optind
++]);
444 printf("Using %s with ", processes
? "processes" : "threads");
446 if (actions
== &pipe_actions
)
448 else if (actions
== &yield_actions
)
453 printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
454 cpu1
, cpu2
, touch_fp
? "yes" : "no", touch_altivec
? "yes" : "no",
455 touch_vector
? "yes" : "no", touch_vdso
? "yes" : "no");
457 /* Create a new process group so we can signal everyone for exit */
458 setpgid(getpid(), getpid());
460 signal(SIGUSR1
, sigusr1_handler
);
462 actions
->setup(cpu1
, cpu2
);
464 start_fn(actions
->thread1
, NULL
, cpu1
);
465 start_fn(actions
->thread2
, NULL
, cpu2
);