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