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