162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Context switch microbenchmark. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 2018, Anton Blanchard, IBM Corp. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define _GNU_SOURCE 1062306a36Sopenharmony_ci#include <assert.h> 1162306a36Sopenharmony_ci#include <errno.h> 1262306a36Sopenharmony_ci#include <getopt.h> 1362306a36Sopenharmony_ci#include <limits.h> 1462306a36Sopenharmony_ci#include <linux/futex.h> 1562306a36Sopenharmony_ci#include <pthread.h> 1662306a36Sopenharmony_ci#include <sched.h> 1762306a36Sopenharmony_ci#include <signal.h> 1862306a36Sopenharmony_ci#include <stdio.h> 1962306a36Sopenharmony_ci#include <stdlib.h> 2062306a36Sopenharmony_ci#include <string.h> 2162306a36Sopenharmony_ci#include <sys/shm.h> 2262306a36Sopenharmony_ci#include <sys/syscall.h> 2362306a36Sopenharmony_ci#include <sys/time.h> 2462306a36Sopenharmony_ci#include <sys/types.h> 2562306a36Sopenharmony_ci#include <sys/wait.h> 2662306a36Sopenharmony_ci#include <unistd.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic unsigned int timeout = 30; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void set_cpu(int cpu) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci cpu_set_t cpuset; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (cpu == -1) 3562306a36Sopenharmony_ci return; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci CPU_ZERO(&cpuset); 3862306a36Sopenharmony_ci CPU_SET(cpu, &cpuset); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) { 4162306a36Sopenharmony_ci perror("sched_setaffinity"); 4262306a36Sopenharmony_ci exit(1); 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void start_process_on(void *(*fn)(void *), void *arg, int cpu) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int pid; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci pid = fork(); 5162306a36Sopenharmony_ci if (pid == -1) { 5262306a36Sopenharmony_ci perror("fork"); 5362306a36Sopenharmony_ci exit(1); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (pid) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci set_cpu(cpu); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci fn(arg); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci exit(0); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int cpu; 6762306a36Sopenharmony_cistatic int do_fork = 0; 6862306a36Sopenharmony_cistatic int do_vfork = 0; 6962306a36Sopenharmony_cistatic int do_exec = 0; 7062306a36Sopenharmony_cistatic char *exec_file; 7162306a36Sopenharmony_cistatic int exec_target = 0; 7262306a36Sopenharmony_cistatic unsigned long iterations; 7362306a36Sopenharmony_cistatic unsigned long iterations_prev; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void run_exec(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci char *const argv[] = { "./exec_target", NULL }; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (execve("./exec_target", argv, NULL) == -1) { 8062306a36Sopenharmony_ci perror("execve"); 8162306a36Sopenharmony_ci exit(1); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void bench_fork(void) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci while (1) { 8862306a36Sopenharmony_ci pid_t pid = fork(); 8962306a36Sopenharmony_ci if (pid == -1) { 9062306a36Sopenharmony_ci perror("fork"); 9162306a36Sopenharmony_ci exit(1); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci if (pid == 0) { 9462306a36Sopenharmony_ci if (do_exec) 9562306a36Sopenharmony_ci run_exec(); 9662306a36Sopenharmony_ci _exit(0); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci pid = waitpid(pid, NULL, 0); 9962306a36Sopenharmony_ci if (pid == -1) { 10062306a36Sopenharmony_ci perror("waitpid"); 10162306a36Sopenharmony_ci exit(1); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci iterations++; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void bench_vfork(void) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci while (1) { 11062306a36Sopenharmony_ci pid_t pid = vfork(); 11162306a36Sopenharmony_ci if (pid == -1) { 11262306a36Sopenharmony_ci perror("fork"); 11362306a36Sopenharmony_ci exit(1); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci if (pid == 0) { 11662306a36Sopenharmony_ci if (do_exec) 11762306a36Sopenharmony_ci run_exec(); 11862306a36Sopenharmony_ci _exit(0); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci pid = waitpid(pid, NULL, 0); 12162306a36Sopenharmony_ci if (pid == -1) { 12262306a36Sopenharmony_ci perror("waitpid"); 12362306a36Sopenharmony_ci exit(1); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci iterations++; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void *null_fn(void *arg) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci pthread_exit(NULL); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void bench_thread(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci pthread_t tid; 13762306a36Sopenharmony_ci cpu_set_t cpuset; 13862306a36Sopenharmony_ci pthread_attr_t attr; 13962306a36Sopenharmony_ci int rc; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci rc = pthread_attr_init(&attr); 14262306a36Sopenharmony_ci if (rc) { 14362306a36Sopenharmony_ci errno = rc; 14462306a36Sopenharmony_ci perror("pthread_attr_init"); 14562306a36Sopenharmony_ci exit(1); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (cpu != -1) { 14962306a36Sopenharmony_ci CPU_ZERO(&cpuset); 15062306a36Sopenharmony_ci CPU_SET(cpu, &cpuset); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); 15362306a36Sopenharmony_ci if (rc) { 15462306a36Sopenharmony_ci errno = rc; 15562306a36Sopenharmony_ci perror("pthread_attr_setaffinity_np"); 15662306a36Sopenharmony_ci exit(1); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci while (1) { 16162306a36Sopenharmony_ci rc = pthread_create(&tid, &attr, null_fn, NULL); 16262306a36Sopenharmony_ci if (rc) { 16362306a36Sopenharmony_ci errno = rc; 16462306a36Sopenharmony_ci perror("pthread_create"); 16562306a36Sopenharmony_ci exit(1); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci rc = pthread_join(tid, NULL); 16862306a36Sopenharmony_ci if (rc) { 16962306a36Sopenharmony_ci errno = rc; 17062306a36Sopenharmony_ci perror("pthread_join"); 17162306a36Sopenharmony_ci exit(1); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci iterations++; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void sigalrm_handler(int junk) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci unsigned long i = iterations; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci printf("%ld\n", i - iterations_prev); 18262306a36Sopenharmony_ci iterations_prev = i; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (--timeout == 0) 18562306a36Sopenharmony_ci kill(0, SIGUSR1); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci alarm(1); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void sigusr1_handler(int junk) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci exit(0); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void *bench_proc(void *arg) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci signal(SIGALRM, sigalrm_handler); 19862306a36Sopenharmony_ci alarm(1); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (do_fork) 20162306a36Sopenharmony_ci bench_fork(); 20262306a36Sopenharmony_ci else if (do_vfork) 20362306a36Sopenharmony_ci bench_vfork(); 20462306a36Sopenharmony_ci else 20562306a36Sopenharmony_ci bench_thread(); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return NULL; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct option options[] = { 21162306a36Sopenharmony_ci { "fork", no_argument, &do_fork, 1 }, 21262306a36Sopenharmony_ci { "vfork", no_argument, &do_vfork, 1 }, 21362306a36Sopenharmony_ci { "exec", no_argument, &do_exec, 1 }, 21462306a36Sopenharmony_ci { "timeout", required_argument, 0, 's' }, 21562306a36Sopenharmony_ci { "exec-target", no_argument, &exec_target, 1 }, 21662306a36Sopenharmony_ci { NULL }, 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void usage(void) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci fprintf(stderr, "Usage: fork <options> CPU\n\n"); 22262306a36Sopenharmony_ci fprintf(stderr, "\t\t--fork\tUse fork() (default threads)\n"); 22362306a36Sopenharmony_ci fprintf(stderr, "\t\t--vfork\tUse vfork() (default threads)\n"); 22462306a36Sopenharmony_ci fprintf(stderr, "\t\t--exec\tAlso exec() (default no exec)\n"); 22562306a36Sopenharmony_ci fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n"); 22662306a36Sopenharmony_ci fprintf(stderr, "\t\t--exec-target\tInternal option for exec workload\n"); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciint main(int argc, char *argv[]) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci signed char c; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci while (1) { 23462306a36Sopenharmony_ci int option_index = 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci c = getopt_long(argc, argv, "", options, &option_index); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (c == -1) 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci switch (c) { 24262306a36Sopenharmony_ci case 0: 24362306a36Sopenharmony_ci if (options[option_index].flag != 0) 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci usage(); 24762306a36Sopenharmony_ci exit(1); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci case 's': 25162306a36Sopenharmony_ci timeout = atoi(optarg); 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci default: 25562306a36Sopenharmony_ci usage(); 25662306a36Sopenharmony_ci exit(1); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (do_fork && do_vfork) { 26162306a36Sopenharmony_ci usage(); 26262306a36Sopenharmony_ci exit(1); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci if (do_exec && !do_fork && !do_vfork) { 26562306a36Sopenharmony_ci usage(); 26662306a36Sopenharmony_ci exit(1); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (do_exec) { 27062306a36Sopenharmony_ci char *dirname = strdup(argv[0]); 27162306a36Sopenharmony_ci int i; 27262306a36Sopenharmony_ci i = strlen(dirname) - 1; 27362306a36Sopenharmony_ci while (i) { 27462306a36Sopenharmony_ci if (dirname[i] == '/') { 27562306a36Sopenharmony_ci dirname[i] = '\0'; 27662306a36Sopenharmony_ci if (chdir(dirname) == -1) { 27762306a36Sopenharmony_ci perror("chdir"); 27862306a36Sopenharmony_ci exit(1); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci i--; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (exec_target) { 28762306a36Sopenharmony_ci exit(0); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (((argc - optind) != 1)) { 29162306a36Sopenharmony_ci cpu = -1; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci cpu = atoi(argv[optind++]); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (do_exec) 29762306a36Sopenharmony_ci exec_file = argv[0]; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci set_cpu(cpu); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci printf("Using "); 30262306a36Sopenharmony_ci if (do_fork) 30362306a36Sopenharmony_ci printf("fork"); 30462306a36Sopenharmony_ci else if (do_vfork) 30562306a36Sopenharmony_ci printf("vfork"); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci printf("clone"); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (do_exec) 31062306a36Sopenharmony_ci printf(" + exec"); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci printf(" on cpu %d\n", cpu); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Create a new process group so we can signal everyone for exit */ 31562306a36Sopenharmony_ci setpgid(getpid(), getpid()); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci signal(SIGUSR1, sigusr1_handler); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci start_process_on(bench_proc, NULL, cpu); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci while (1) 32262306a36Sopenharmony_ci sleep(3600); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci} 326