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