~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/powerpc/benchmarks/context_switch.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php