root/tools/testing/selftests/powerpc/benchmarks/context_switch.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. altivec_touch_fn
  2. touch
  3. start_thread_on
  4. start_process_on
  5. sigalrm_handler
  6. sigusr1_handler
  7. pipe_setup
  8. pipe_thread1
  9. pipe_thread2
  10. yield_setup
  11. yield_thread1
  12. yield_thread2
  13. sys_futex
  14. cmpxchg
  15. xchg
  16. mutex_lock
  17. mutex_unlock
  18. futex_setup
  19. futex_thread1
  20. futex_thread2
  21. usage
  22. main

   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 
  30 static unsigned int timeout = 30;
  31 
  32 static int touch_vdso;
  33 struct timeval tv;
  34 
  35 static int touch_fp = 1;
  36 double fp;
  37 
  38 static int touch_vector = 1;
  39 vector int a, b, c;
  40 
  41 #ifdef __powerpc__
  42 static 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  */
  48 static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
  49 {
  50         c = a + b;
  51 }
  52 #endif
  53 
  54 static 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 
  73 static 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 
 105 static 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 
 132 static unsigned long iterations;
 133 static unsigned long iterations_prev;
 134 
 135 static 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 
 148 static void sigusr1_handler(int junk)
 149 {
 150         exit(0);
 151 }
 152 
 153 struct 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 
 162 static int pipe_fd1[2];
 163 static int pipe_fd2[2];
 164 
 165 static void pipe_setup(int cpu1, int cpu2)
 166 {
 167         if (pipe(pipe_fd1) || pipe(pipe_fd2))
 168                 exit(1);
 169 }
 170 
 171 static 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 
 189 static 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 
 202 static struct actions pipe_actions = {
 203         .setup = pipe_setup,
 204         .thread1 = pipe_thread1,
 205         .thread2 = pipe_thread2,
 206 };
 207 
 208 static 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 
 216 static 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 
 231 static void *yield_thread2(void *arg)
 232 {
 233         while (1) {
 234                 sched_yield();
 235                 touch();
 236         }
 237 
 238         return NULL;
 239 }
 240 
 241 static struct actions yield_actions = {
 242         .setup = yield_setup,
 243         .thread1 = yield_thread1,
 244         .thread2 = yield_thread2,
 245 };
 246 
 247 static 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 
 253 static 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 
 263 static unsigned long xchg(unsigned long *p, unsigned long val)
 264 {
 265         return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST);
 266 }
 267 
 268 static int processes;
 269 
 270 static 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 
 292 static 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 
 308 static unsigned long *m1, *m2;
 309 
 310 static 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 
 346 static 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 
 361 static void *futex_thread2(void *arg)
 362 {
 363         while (1) {
 364                 mutex_unlock(m2);
 365                 mutex_lock(m1);
 366         }
 367 
 368         return NULL;
 369 }
 370 
 371 static struct actions futex_actions = {
 372         .setup = futex_setup,
 373         .thread1 = futex_thread1,
 374         .thread2 = futex_thread2,
 375 };
 376 
 377 static 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 
 390 static 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 
 404 int 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 }

/* [<][>][^][v][top][bottom][index][help] */