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