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