0001
0002
0003
0004
0005
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
0047
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
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 }