Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Context switch microbenchmark.
0004  *
0005  * Copyright (C) 2015 Anton Blanchard <anton@au.ibm.com>, IBM
0006  */
0007 
0008 #define _GNU_SOURCE
0009 #include <errno.h>
0010 #include <sched.h>
0011 #include <string.h>
0012 #include <stdio.h>
0013 #include <unistd.h>
0014 #include <stdlib.h>
0015 #include <getopt.h>
0016 #include <signal.h>
0017 #include <assert.h>
0018 #include <pthread.h>
0019 #include <limits.h>
0020 #include <sys/time.h>
0021 #include <sys/syscall.h>
0022 #include <sys/sysinfo.h>
0023 #include <sys/types.h>
0024 #include <sys/shm.h>
0025 #include <linux/futex.h>
0026 #ifdef __powerpc__
0027 #include <altivec.h>
0028 #endif
0029 #include "utils.h"
0030 
0031 static unsigned int timeout = 30;
0032 
0033 static int touch_vdso;
0034 struct timeval tv;
0035 
0036 static int touch_fp = 1;
0037 double fp;
0038 
0039 static int touch_vector = 1;
0040 vector int a, b, c;
0041 
0042 #ifdef __powerpc__
0043 static int touch_altivec = 1;
0044 
0045 /*
0046  * Note: LTO (Link Time Optimisation) doesn't play well with this function
0047  * attribute. Be very careful enabling LTO for this test.
0048  */
0049 static void __attribute__((__target__("no-vsx"))) altivec_touch_fn(void)
0050 {
0051     c = a + b;
0052 }
0053 #endif
0054 
0055 static void touch(void)
0056 {
0057     if (touch_vdso)
0058         gettimeofday(&tv, NULL);
0059 
0060     if (touch_fp)
0061         fp += 0.1;
0062 
0063 #ifdef __powerpc__
0064     if (touch_altivec)
0065         altivec_touch_fn();
0066 #endif
0067 
0068     if (touch_vector)
0069         c = a + b;
0070 
0071     asm volatile("# %0 %1 %2": : "r"(&tv), "r"(&fp), "r"(&c));
0072 }
0073 
0074 static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu)
0075 {
0076     int rc;
0077     pthread_t tid;
0078     cpu_set_t cpuset;
0079     pthread_attr_t attr;
0080 
0081     CPU_ZERO(&cpuset);
0082     CPU_SET(cpu, &cpuset);
0083 
0084     rc = pthread_attr_init(&attr);
0085     if (rc) {
0086         errno = rc;
0087         perror("pthread_attr_init");
0088         exit(1);
0089     }
0090 
0091     rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
0092     if (rc) {
0093         errno = rc;
0094         perror("pthread_attr_setaffinity_np");
0095         exit(1);
0096     }
0097 
0098     rc = pthread_create(&tid, &attr, fn, arg);
0099     if (rc) {
0100         errno = rc;
0101         perror("pthread_create");
0102         exit(1);
0103     }
0104 }
0105 
0106 static void start_process_on(void *(*fn)(void *), void *arg, unsigned long cpu)
0107 {
0108     int pid, ncpus;
0109     cpu_set_t *cpuset;
0110     size_t size;
0111 
0112     pid = fork();
0113     if (pid == -1) {
0114         perror("fork");
0115         exit(1);
0116     }
0117 
0118     if (pid)
0119         return;
0120 
0121     ncpus = get_nprocs();
0122     size = CPU_ALLOC_SIZE(ncpus);
0123     cpuset = CPU_ALLOC(ncpus);
0124     if (!cpuset) {
0125         perror("malloc");
0126         exit(1);
0127     }
0128     CPU_ZERO_S(size, cpuset);
0129     CPU_SET_S(cpu, size, cpuset);
0130 
0131     if (sched_setaffinity(0, size, cpuset)) {
0132         perror("sched_setaffinity");
0133         CPU_FREE(cpuset);
0134         exit(1);
0135     }
0136 
0137     CPU_FREE(cpuset);
0138     fn(arg);
0139 
0140     exit(0);
0141 }
0142 
0143 static unsigned long iterations;
0144 static unsigned long iterations_prev;
0145 
0146 static void sigalrm_handler(int junk)
0147 {
0148     unsigned long i = iterations;
0149 
0150     printf("%ld\n", i - iterations_prev);
0151     iterations_prev = i;
0152 
0153     if (--timeout == 0)
0154         kill(0, SIGUSR1);
0155 
0156     alarm(1);
0157 }
0158 
0159 static void sigusr1_handler(int junk)
0160 {
0161     exit(0);
0162 }
0163 
0164 struct actions {
0165     void (*setup)(int, int);
0166     void *(*thread1)(void *);
0167     void *(*thread2)(void *);
0168 };
0169 
0170 #define READ 0
0171 #define WRITE 1
0172 
0173 static int pipe_fd1[2];
0174 static int pipe_fd2[2];
0175 
0176 static void pipe_setup(int cpu1, int cpu2)
0177 {
0178     if (pipe(pipe_fd1) || pipe(pipe_fd2))
0179         exit(1);
0180 }
0181 
0182 static void *pipe_thread1(void *arg)
0183 {
0184     signal(SIGALRM, sigalrm_handler);
0185     alarm(1);
0186 
0187     while (1) {
0188         assert(read(pipe_fd1[READ], &c, 1) == 1);
0189         touch();
0190 
0191         assert(write(pipe_fd2[WRITE], &c, 1) == 1);
0192         touch();
0193 
0194         iterations += 2;
0195     }
0196 
0197     return NULL;
0198 }
0199 
0200 static void *pipe_thread2(void *arg)
0201 {
0202     while (1) {
0203         assert(write(pipe_fd1[WRITE], &c, 1) == 1);
0204         touch();
0205 
0206         assert(read(pipe_fd2[READ], &c, 1) == 1);
0207         touch();
0208     }
0209 
0210     return NULL;
0211 }
0212 
0213 static struct actions pipe_actions = {
0214     .setup = pipe_setup,
0215     .thread1 = pipe_thread1,
0216     .thread2 = pipe_thread2,
0217 };
0218 
0219 static void yield_setup(int cpu1, int cpu2)
0220 {
0221     if (cpu1 != cpu2) {
0222         fprintf(stderr, "Both threads must be on the same CPU for yield test\n");
0223         exit(1);
0224     }
0225 }
0226 
0227 static void *yield_thread1(void *arg)
0228 {
0229     signal(SIGALRM, sigalrm_handler);
0230     alarm(1);
0231 
0232     while (1) {
0233         sched_yield();
0234         touch();
0235 
0236         iterations += 2;
0237     }
0238 
0239     return NULL;
0240 }
0241 
0242 static void *yield_thread2(void *arg)
0243 {
0244     while (1) {
0245         sched_yield();
0246         touch();
0247     }
0248 
0249     return NULL;
0250 }
0251 
0252 static struct actions yield_actions = {
0253     .setup = yield_setup,
0254     .thread1 = yield_thread1,
0255     .thread2 = yield_thread2,
0256 };
0257 
0258 static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
0259               void *addr2, int val3)
0260 {
0261     return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
0262 }
0263 
0264 static unsigned long cmpxchg(unsigned long *p, unsigned long expected,
0265                  unsigned long desired)
0266 {
0267     unsigned long exp = expected;
0268 
0269     __atomic_compare_exchange_n(p, &exp, desired, 0,
0270                     __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
0271     return exp;
0272 }
0273 
0274 static unsigned long xchg(unsigned long *p, unsigned long val)
0275 {
0276     return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST);
0277 }
0278 
0279 static int processes;
0280 
0281 static int mutex_lock(unsigned long *m)
0282 {
0283     int c;
0284     int flags = FUTEX_WAIT;
0285     if (!processes)
0286         flags |= FUTEX_PRIVATE_FLAG;
0287 
0288     c = cmpxchg(m, 0, 1);
0289     if (!c)
0290         return 0;
0291 
0292     if (c == 1)
0293         c = xchg(m, 2);
0294 
0295     while (c) {
0296         sys_futex(m, flags, 2, NULL, NULL, 0);
0297         c = xchg(m, 2);
0298     }
0299 
0300     return 0;
0301 }
0302 
0303 static int mutex_unlock(unsigned long *m)
0304 {
0305     int flags = FUTEX_WAKE;
0306     if (!processes)
0307         flags |= FUTEX_PRIVATE_FLAG;
0308 
0309     if (*m == 2)
0310         *m = 0;
0311     else if (xchg(m, 0) == 1)
0312         return 0;
0313 
0314     sys_futex(m, flags, 1, NULL, NULL, 0);
0315 
0316     return 0;
0317 }
0318 
0319 static unsigned long *m1, *m2;
0320 
0321 static void futex_setup(int cpu1, int cpu2)
0322 {
0323     if (!processes) {
0324         static unsigned long _m1, _m2;
0325         m1 = &_m1;
0326         m2 = &_m2;
0327     } else {
0328         int shmid;
0329         void *shmaddr;
0330 
0331         shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W);
0332         if (shmid < 0) {
0333             perror("shmget");
0334             exit(1);
0335         }
0336 
0337         shmaddr = shmat(shmid, NULL, 0);
0338         if (shmaddr == (char *)-1) {
0339             perror("shmat");
0340             shmctl(shmid, IPC_RMID, NULL);
0341             exit(1);
0342         }
0343 
0344         shmctl(shmid, IPC_RMID, NULL);
0345 
0346         m1 = shmaddr;
0347         m2 = shmaddr + sizeof(*m1);
0348     }
0349 
0350     *m1 = 0;
0351     *m2 = 0;
0352 
0353     mutex_lock(m1);
0354     mutex_lock(m2);
0355 }
0356 
0357 static void *futex_thread1(void *arg)
0358 {
0359     signal(SIGALRM, sigalrm_handler);
0360     alarm(1);
0361 
0362     while (1) {
0363         mutex_lock(m2);
0364         mutex_unlock(m1);
0365 
0366         iterations += 2;
0367     }
0368 
0369     return NULL;
0370 }
0371 
0372 static void *futex_thread2(void *arg)
0373 {
0374     while (1) {
0375         mutex_unlock(m2);
0376         mutex_lock(m1);
0377     }
0378 
0379     return NULL;
0380 }
0381 
0382 static struct actions futex_actions = {
0383     .setup = futex_setup,
0384     .thread1 = futex_thread1,
0385     .thread2 = futex_thread2,
0386 };
0387 
0388 static struct option options[] = {
0389     { "test", required_argument, 0, 't' },
0390     { "process", no_argument, &processes, 1 },
0391     { "timeout", required_argument, 0, 's' },
0392     { "vdso", no_argument, &touch_vdso, 1 },
0393     { "no-fp", no_argument, &touch_fp, 0 },
0394 #ifdef __powerpc__
0395     { "no-altivec", no_argument, &touch_altivec, 0 },
0396 #endif
0397     { "no-vector", no_argument, &touch_vector, 0 },
0398     { 0, },
0399 };
0400 
0401 static void usage(void)
0402 {
0403     fprintf(stderr, "Usage: context_switch2 <options> CPU1 CPU2\n\n");
0404     fprintf(stderr, "\t\t--test=X\tpipe, futex or yield (default)\n");
0405     fprintf(stderr, "\t\t--process\tUse processes (default threads)\n");
0406     fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
0407     fprintf(stderr, "\t\t--vdso\t\ttouch VDSO\n");
0408     fprintf(stderr, "\t\t--no-fp\t\tDon't touch FP\n");
0409 #ifdef __powerpc__
0410     fprintf(stderr, "\t\t--no-altivec\tDon't touch altivec\n");
0411 #endif
0412     fprintf(stderr, "\t\t--no-vector\tDon't touch vector\n");
0413 }
0414 
0415 int main(int argc, char *argv[])
0416 {
0417     signed char c;
0418     struct actions *actions = &yield_actions;
0419     int cpu1;
0420     int cpu2;
0421     static void (*start_fn)(void *(*fn)(void *), void *arg, unsigned long cpu);
0422 
0423     while (1) {
0424         int option_index = 0;
0425 
0426         c = getopt_long(argc, argv, "", options, &option_index);
0427 
0428         if (c == -1)
0429             break;
0430 
0431         switch (c) {
0432         case 0:
0433             if (options[option_index].flag != 0)
0434                 break;
0435 
0436             usage();
0437             exit(1);
0438             break;
0439 
0440         case 't':
0441             if (!strcmp(optarg, "pipe")) {
0442                 actions = &pipe_actions;
0443             } else if (!strcmp(optarg, "yield")) {
0444                 actions = &yield_actions;
0445             } else if (!strcmp(optarg, "futex")) {
0446                 actions = &futex_actions;
0447             } else {
0448                 usage();
0449                 exit(1);
0450             }
0451             break;
0452 
0453         case 's':
0454             timeout = atoi(optarg);
0455             break;
0456 
0457         default:
0458             usage();
0459             exit(1);
0460         }
0461     }
0462 
0463     if (processes)
0464         start_fn = start_process_on;
0465     else
0466         start_fn = start_thread_on;
0467 
0468     if (((argc - optind) != 2)) {
0469         cpu1 = cpu2 = pick_online_cpu();
0470     } else {
0471         cpu1 = atoi(argv[optind++]);
0472         cpu2 = atoi(argv[optind++]);
0473     }
0474 
0475     printf("Using %s with ", processes ? "processes" : "threads");
0476 
0477     if (actions == &pipe_actions)
0478         printf("pipe");
0479     else if (actions == &yield_actions)
0480         printf("yield");
0481     else
0482         printf("futex");
0483 
0484     if (!have_hwcap(PPC_FEATURE_HAS_ALTIVEC))
0485         touch_altivec = 0;
0486 
0487     if (!have_hwcap(PPC_FEATURE_HAS_VSX))
0488         touch_vector = 0;
0489 
0490     printf(" on cpus %d/%d touching FP:%s altivec:%s vector:%s vdso:%s\n",
0491            cpu1, cpu2, touch_fp ?  "yes" : "no", touch_altivec ? "yes" : "no",
0492            touch_vector ? "yes" : "no", touch_vdso ? "yes" : "no");
0493 
0494     /* Create a new process group so we can signal everyone for exit */
0495     setpgid(getpid(), getpid());
0496 
0497     signal(SIGUSR1, sigusr1_handler);
0498 
0499     actions->setup(cpu1, cpu2);
0500 
0501     start_fn(actions->thread1, NULL, cpu1);
0502     start_fn(actions->thread2, NULL, cpu2);
0503 
0504     while (1)
0505         sleep(3600);
0506 
0507     return 0;
0508 }