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 processes;
 262
 263static int mutex_lock(unsigned long *m)
 264{
 265        int c;
 266        int flags = FUTEX_WAIT;
 267        if (!processes)
 268                flags |= FUTEX_PRIVATE_FLAG;
 269
 270        c = cmpxchg(m, 0, 1);
 271        if (!c)
 272                return 0;
 273
 274        if (c == 1)
 275                c = xchg(m, 2);
 276
 277        while (c) {
 278                sys_futex(m, flags, 2, NULL, NULL, 0);
 279                c = xchg(m, 2);
 280        }
 281
 282        return 0;
 283}
 284
 285static int mutex_unlock(unsigned long *m)
 286{
 287        int flags = FUTEX_WAKE;
 288        if (!processes)
 289                flags |= FUTEX_PRIVATE_FLAG;
 290
 291        if (*m == 2)
 292                *m = 0;
 293        else if (xchg(m, 0) == 1)
 294                return 0;
 295
 296        sys_futex(m, flags, 1, NULL, NULL, 0);
 297
 298        return 0;
 299}
 300
 301static unsigned long *m1, *m2;
 302
 303static void futex_setup(int cpu1, int cpu2)
 304{
 305        if (!processes) {
 306                static unsigned long _m1, _m2;
 307                m1 = &_m1;
 308                m2 = &_m2;
 309        } else {
 310                int shmid;
 311                void *shmaddr;
 312
 313                shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W);
 314                if (shmid < 0) {
 315                        perror("shmget");
 316                        exit(1);
 317                }
 318
 319                shmaddr = shmat(shmid, NULL, 0);
 320                if (shmaddr == (char *)-1) {
 321                        perror("shmat");
 322                        shmctl(shmid, IPC_RMID, NULL);
 323                        exit(1);
 324                }
 325
 326                shmctl(shmid, IPC_RMID, NULL);
 327
 328                m1 = shmaddr;
 329                m2 = shmaddr + sizeof(*m1);
 330        }
 331
 332        *m1 = 0;
 333        *m2 = 0;
 334
 335        mutex_lock(m1);
 336        mutex_lock(m2);
 337}
 338
 339static void *futex_thread1(void *arg)
 340{
 341        signal(SIGALRM, sigalrm_handler);
 342        alarm(1);
 343
 344        while (1) {
 345                mutex_lock(m2);
 346                mutex_unlock(m1);
 347
 348                iterations += 2;
 349        }
 350
 351        return NULL;
 352}
 353
 354static void *futex_thread2(void *arg)
 355{
 356        while (1) {
 357                mutex_unlock(m2);
 358                mutex_lock(m1);
 359        }
 360
 361        return NULL;
 362}
 363
 364static struct actions futex_actions = {
 365        .setup = futex_setup,
 366        .thread1 = futex_thread1,
 367        .thread2 = futex_thread2,
 368};
 369
 370static struct option options[] = {
 371        { "test", required_argument, 0, 't' },
 372        { "process", no_argument, &processes, 1 },
 373        { "timeout", required_argument, 0, 's' },
 374        { "vdso", no_argument, &touch_vdso, 1 },
 375        { "no-fp", no_argument, &touch_fp, 0 },
 376#ifdef __powerpc__
 377        { "no-altivec", no_argument, &touch_altivec, 0 },
 378#endif
 379        { "no-vector", no_argument, &touch_vector, 0 },
 380        { 0, },
 381};
 382
 383static void usage(void)
 384{
 385        fprintf(stderr, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
 386        fprintf(stderr, "\t\t--test=X\tpipe, futex or yield (default)\n");
 387        fprintf(stderr, "\t\t--process\tUse processes (default threads)\n");
 388        fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
 389        fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n");
 390        fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n");
 391#ifdef __powerpc__
 392        fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n");
 393#endif
 394        fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n");
 395}
 396
 397int main(int argc, char *argv[])
 398{
 399        signed char c;
 400        struct actions *actions = &yield_actions;
 401        int cpu1;
 402        int cpu2;
 403        static void (*start_fn)(void *(*fn)(void *), void *arg, unsigned long cpu);
 404
 405        while (1) {
 406                int option_index = 0;
 407
 408                c = getopt_long(argc, argv, "", options, &option_index);
 409
 410                if (c == -1)
 411                        break;
 412
 413                switch (c) {
 414                case 0:
 415                        if (options[option_index].flag != 0)
 416                                break;
 417
 418                        usage();
 419                        exit(1);
 420                        break;
 421
 422                case 't':
 423                        if (!strcmp(optarg, "pipe")) {
 424                                actions = &pipe_actions;
 425                        } else if (!strcmp(optarg, "yield")) {
 426                                actions = &yield_actions;
 427                        } else if (!strcmp(optarg, "futex")) {
 428                                actions = &futex_actions;
 429                        } else {
 430                                usage();
 431                                exit(1);
 432                        }
 433                        break;
 434
 435                case 's':
 436                        timeout = atoi(optarg);
 437                        break;
 438
 439                default:
 440                        usage();
 441                        exit(1);
 442                }
 443        }
 444
 445        if (processes)
 446                start_fn = start_process_on;
 447        else
 448                start_fn = start_thread_on;
 449
 450        if (((argc - optind) != 2)) {
 451                cpu1 = cpu2 = pick_online_cpu();
 452        } else {
 453                cpu1 = atoi(argv[optind++]);
 454                cpu2 = atoi(argv[optind++]);
 455        }
 456
 457        printf("Using %s with ", processes ? "processes" : "threads");
 458
 459        if (actions == &pipe_actions)
 460                printf("pipe");
 461        else if (actions == &yield_actions)
 462                printf("yield");
 463        else
 464                printf("futex");
 465
 466        printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
 467               cpu1, cpu2, touch_fp ?  "yes" : "no", touch_altivec ? "yes" : "no",
 468               touch_vector ? "yes" : "no", touch_vdso ? "yes" : "no");
 469
 470        /* Create a new process group so we can signal everyone for exit */
 471        setpgid(getpid(), getpid());
 472
 473        signal(SIGUSR1, sigusr1_handler);
 474
 475        actions->setup(cpu1, cpu2);
 476
 477        start_fn(actions->thread1, NULL, cpu1);
 478        start_fn(actions->thread2, NULL, cpu2);
 479
 480        while (1)
 481                sleep(3600);
 482
 483        return 0;
 484}
 485