Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Import Anton's context_switch2 benchmark

This gets referred to a lot in commit messages, so let's pull it into
the selftests.

Almost vanilla from: http://ozlabs.org/~anton/junkcode/context_switch2.c

Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Acked-by: Anton Blanchard <anton@samba.org>

+456 -1
+1
tools/testing/selftests/powerpc/benchmarks/.gitignore
··· 1 1 gettimeofday 2 + context_switch
+3 -1
tools/testing/selftests/powerpc/benchmarks/Makefile
··· 1 - TEST_PROGS := gettimeofday 1 + TEST_PROGS := gettimeofday context_switch 2 2 3 3 CFLAGS += -O2 4 4 5 5 all: $(TEST_PROGS) 6 6 7 7 $(TEST_PROGS): ../harness.c 8 + 9 + context_switch: LDLIBS += -lpthread 8 10 9 11 include ../../lib.mk 10 12
+452
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 + static unsigned int timeout = INT_MAX; 30 + 31 + static int touch_vdso; 32 + struct timeval tv; 33 + 34 + static int touch_fp; 35 + double fp; 36 + 37 + static int touch_vector; 38 + typedef int v4si __attribute__ ((vector_size (16))); 39 + v4si a, b, c; 40 + 41 + #ifdef __powerpc__ 42 + static int touch_altivec; 43 + 44 + static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void) 45 + { 46 + c = a + b; 47 + } 48 + #endif 49 + 50 + static 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 + 69 + static 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 + 91 + static 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 + 118 + static unsigned long iterations; 119 + static unsigned long iterations_prev; 120 + 121 + static 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 + 134 + static void sigusr1_handler(int junk) 135 + { 136 + exit(0); 137 + } 138 + 139 + struct 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 + 148 + static int pipe_fd1[2]; 149 + static int pipe_fd2[2]; 150 + 151 + static void pipe_setup(int cpu1, int cpu2) 152 + { 153 + if (pipe(pipe_fd1) || pipe(pipe_fd2)) 154 + exit(1); 155 + } 156 + 157 + static 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 + 175 + static 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 + 188 + static struct actions pipe_actions = { 189 + .setup = pipe_setup, 190 + .thread1 = pipe_thread1, 191 + .thread2 = pipe_thread2, 192 + }; 193 + 194 + static 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 + 202 + static 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 + 217 + static void *yield_thread2(void *arg) 218 + { 219 + while (1) { 220 + sched_yield(); 221 + touch(); 222 + } 223 + 224 + return NULL; 225 + } 226 + 227 + static struct actions yield_actions = { 228 + .setup = yield_setup, 229 + .thread1 = yield_thread1, 230 + .thread2 = yield_thread2, 231 + }; 232 + 233 + static 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 + 239 + static 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 + 249 + static unsigned long xchg(unsigned long *p, unsigned long val) 250 + { 251 + return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST); 252 + } 253 + 254 + static 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 + 273 + static 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 + 285 + static unsigned long *m1, *m2; 286 + 287 + static 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 + 317 + static 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 + 332 + static void *futex_thread2(void *arg) 333 + { 334 + while (1) { 335 + mutex_unlock(m2); 336 + mutex_lock(m1); 337 + } 338 + 339 + return NULL; 340 + } 341 + 342 + static struct actions futex_actions = { 343 + .setup = futex_setup, 344 + .thread1 = futex_thread1, 345 + .thread2 = futex_thread2, 346 + }; 347 + 348 + static int processes; 349 + 350 + static 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 + 363 + static 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 + 377 + int 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 + }