162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * sched-messaging.c 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * messaging: Benchmark for scheduler and IPC mechanisms 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au> 962306a36Sopenharmony_ci * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <subcmd/parse-options.h> 1462306a36Sopenharmony_ci#include "bench.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* Test groups of 20 processes spraying to 20 receivers */ 1762306a36Sopenharmony_ci#include <pthread.h> 1862306a36Sopenharmony_ci#include <stdio.h> 1962306a36Sopenharmony_ci#include <stdlib.h> 2062306a36Sopenharmony_ci#include <string.h> 2162306a36Sopenharmony_ci#include <errno.h> 2262306a36Sopenharmony_ci#include <unistd.h> 2362306a36Sopenharmony_ci#include <sys/types.h> 2462306a36Sopenharmony_ci#include <sys/socket.h> 2562306a36Sopenharmony_ci#include <sys/wait.h> 2662306a36Sopenharmony_ci#include <sys/time.h> 2762306a36Sopenharmony_ci#include <poll.h> 2862306a36Sopenharmony_ci#include <limits.h> 2962306a36Sopenharmony_ci#include <err.h> 3062306a36Sopenharmony_ci#include <linux/list.h> 3162306a36Sopenharmony_ci#include <linux/time64.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DATASIZE 100 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic bool use_pipes = false; 3662306a36Sopenharmony_cistatic unsigned int nr_loops = 100; 3762306a36Sopenharmony_cistatic bool thread_mode = false; 3862306a36Sopenharmony_cistatic unsigned int num_groups = 10; 3962306a36Sopenharmony_cistatic struct list_head sender_contexts = LIST_HEAD_INIT(sender_contexts); 4062306a36Sopenharmony_cistatic struct list_head receiver_contexts = LIST_HEAD_INIT(receiver_contexts); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct sender_context { 4362306a36Sopenharmony_ci struct list_head list; 4462306a36Sopenharmony_ci unsigned int num_fds; 4562306a36Sopenharmony_ci int ready_out; 4662306a36Sopenharmony_ci int wakefd; 4762306a36Sopenharmony_ci int out_fds[]; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct receiver_context { 5162306a36Sopenharmony_ci struct list_head list; 5262306a36Sopenharmony_ci unsigned int num_packets; 5362306a36Sopenharmony_ci int in_fds[2]; 5462306a36Sopenharmony_ci int ready_out; 5562306a36Sopenharmony_ci int wakefd; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void fdpair(int fds[2]) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (use_pipes) { 6162306a36Sopenharmony_ci if (pipe(fds) == 0) 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci } else { 6462306a36Sopenharmony_ci if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()"); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Block until we're ready to go */ 7262306a36Sopenharmony_cistatic void ready(int ready_out, int wakefd) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Tell them we're ready. */ 7762306a36Sopenharmony_ci if (write(ready_out, "R", 1) != 1) 7862306a36Sopenharmony_ci err(EXIT_FAILURE, "CLIENT: ready write"); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* Wait for "GO" signal */ 8162306a36Sopenharmony_ci if (poll(&pollfd, 1, -1) != 1) 8262306a36Sopenharmony_ci err(EXIT_FAILURE, "poll"); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Sender sprays nr_loops messages down each file descriptor */ 8662306a36Sopenharmony_cistatic void *sender(struct sender_context *ctx) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci char data[DATASIZE]; 8962306a36Sopenharmony_ci unsigned int i, j; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ready(ctx->ready_out, ctx->wakefd); 9262306a36Sopenharmony_ci memset(data, 'S', sizeof(data)); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Now pump to every receiver. */ 9562306a36Sopenharmony_ci for (i = 0; i < nr_loops; i++) { 9662306a36Sopenharmony_ci for (j = 0; j < ctx->num_fds; j++) { 9762306a36Sopenharmony_ci int ret, done = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciagain: 10062306a36Sopenharmony_ci ret = write(ctx->out_fds[j], data + done, 10162306a36Sopenharmony_ci sizeof(data)-done); 10262306a36Sopenharmony_ci if (ret < 0) 10362306a36Sopenharmony_ci err(EXIT_FAILURE, "SENDER: write"); 10462306a36Sopenharmony_ci done += ret; 10562306a36Sopenharmony_ci if (done < DATASIZE) 10662306a36Sopenharmony_ci goto again; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* One receiver per fd */ 11562306a36Sopenharmony_cistatic void *receiver(struct receiver_context* ctx) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci unsigned int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!thread_mode) 12062306a36Sopenharmony_ci close(ctx->in_fds[1]); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Wait for start... */ 12362306a36Sopenharmony_ci ready(ctx->ready_out, ctx->wakefd); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Receive them all */ 12662306a36Sopenharmony_ci for (i = 0; i < ctx->num_packets; i++) { 12762306a36Sopenharmony_ci char data[DATASIZE]; 12862306a36Sopenharmony_ci int ret, done = 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciagain: 13162306a36Sopenharmony_ci ret = read(ctx->in_fds[0], data + done, DATASIZE - done); 13262306a36Sopenharmony_ci if (ret < 0) 13362306a36Sopenharmony_ci err(EXIT_FAILURE, "SERVER: read"); 13462306a36Sopenharmony_ci done += ret; 13562306a36Sopenharmony_ci if (done < DATASIZE) 13662306a36Sopenharmony_ci goto again; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return NULL; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic pthread_t create_worker(void *ctx, void *(*func)(void *)) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci pthread_attr_t attr; 14562306a36Sopenharmony_ci pthread_t childid; 14662306a36Sopenharmony_ci int ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!thread_mode) { 14962306a36Sopenharmony_ci /* process mode */ 15062306a36Sopenharmony_ci /* Fork the receiver. */ 15162306a36Sopenharmony_ci switch (fork()) { 15262306a36Sopenharmony_ci case -1: 15362306a36Sopenharmony_ci err(EXIT_FAILURE, "fork()"); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case 0: 15662306a36Sopenharmony_ci (*func) (ctx); 15762306a36Sopenharmony_ci exit(0); 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci default: 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return (pthread_t)0; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (pthread_attr_init(&attr) != 0) 16762306a36Sopenharmony_ci err(EXIT_FAILURE, "pthread_attr_init:"); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#ifndef __ia64__ 17062306a36Sopenharmony_ci if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) 17162306a36Sopenharmony_ci err(EXIT_FAILURE, "pthread_attr_setstacksize"); 17262306a36Sopenharmony_ci#endif 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ret = pthread_create(&childid, &attr, func, ctx); 17562306a36Sopenharmony_ci if (ret != 0) 17662306a36Sopenharmony_ci err(EXIT_FAILURE, "pthread_create failed"); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci pthread_attr_destroy(&attr); 17962306a36Sopenharmony_ci return childid; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void reap_worker(pthread_t id) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int proc_status; 18562306a36Sopenharmony_ci void *thread_status; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!thread_mode) { 18862306a36Sopenharmony_ci /* process mode */ 18962306a36Sopenharmony_ci wait(&proc_status); 19062306a36Sopenharmony_ci if (!WIFEXITED(proc_status)) 19162306a36Sopenharmony_ci exit(1); 19262306a36Sopenharmony_ci } else { 19362306a36Sopenharmony_ci pthread_join(id, &thread_status); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* One group of senders and receivers */ 19862306a36Sopenharmony_cistatic unsigned int group(pthread_t *pth, 19962306a36Sopenharmony_ci unsigned int num_fds, 20062306a36Sopenharmony_ci int ready_out, 20162306a36Sopenharmony_ci int wakefd) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci unsigned int i; 20462306a36Sopenharmony_ci struct sender_context *snd_ctx = malloc(sizeof(struct sender_context) 20562306a36Sopenharmony_ci + num_fds * sizeof(int)); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!snd_ctx) 20862306a36Sopenharmony_ci err(EXIT_FAILURE, "malloc()"); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci list_add(&snd_ctx->list, &sender_contexts); 21162306a36Sopenharmony_ci for (i = 0; i < num_fds; i++) { 21262306a36Sopenharmony_ci int fds[2]; 21362306a36Sopenharmony_ci struct receiver_context *ctx = malloc(sizeof(*ctx)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!ctx) 21662306a36Sopenharmony_ci err(EXIT_FAILURE, "malloc()"); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci list_add(&ctx->list, &receiver_contexts); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Create the pipe between client and server */ 22162306a36Sopenharmony_ci fdpair(fds); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ctx->num_packets = num_fds * nr_loops; 22462306a36Sopenharmony_ci ctx->in_fds[0] = fds[0]; 22562306a36Sopenharmony_ci ctx->in_fds[1] = fds[1]; 22662306a36Sopenharmony_ci ctx->ready_out = ready_out; 22762306a36Sopenharmony_ci ctx->wakefd = wakefd; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci pth[i] = create_worker(ctx, (void *)receiver); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci snd_ctx->out_fds[i] = fds[1]; 23262306a36Sopenharmony_ci if (!thread_mode) 23362306a36Sopenharmony_ci close(fds[0]); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Now we have all the fds, fork the senders */ 23762306a36Sopenharmony_ci for (i = 0; i < num_fds; i++) { 23862306a36Sopenharmony_ci snd_ctx->ready_out = ready_out; 23962306a36Sopenharmony_ci snd_ctx->wakefd = wakefd; 24062306a36Sopenharmony_ci snd_ctx->num_fds = num_fds; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pth[num_fds+i] = create_worker(snd_ctx, (void *)sender); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Close the fds we have left */ 24662306a36Sopenharmony_ci if (!thread_mode) 24762306a36Sopenharmony_ci for (i = 0; i < num_fds; i++) 24862306a36Sopenharmony_ci close(snd_ctx->out_fds[i]); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Return number of children to reap */ 25162306a36Sopenharmony_ci return num_fds * 2; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic const struct option options[] = { 25562306a36Sopenharmony_ci OPT_BOOLEAN('p', "pipe", &use_pipes, 25662306a36Sopenharmony_ci "Use pipe() instead of socketpair()"), 25762306a36Sopenharmony_ci OPT_BOOLEAN('t', "thread", &thread_mode, 25862306a36Sopenharmony_ci "Be multi thread instead of multi process"), 25962306a36Sopenharmony_ci OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"), 26062306a36Sopenharmony_ci OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"), 26162306a36Sopenharmony_ci OPT_END() 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic const char * const bench_sched_message_usage[] = { 26562306a36Sopenharmony_ci "perf bench sched messaging <options>", 26662306a36Sopenharmony_ci NULL 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciint bench_sched_messaging(int argc, const char **argv) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci unsigned int i, total_children; 27262306a36Sopenharmony_ci struct timeval start, stop, diff; 27362306a36Sopenharmony_ci unsigned int num_fds = 20; 27462306a36Sopenharmony_ci int readyfds[2], wakefds[2]; 27562306a36Sopenharmony_ci char dummy; 27662306a36Sopenharmony_ci pthread_t *pth_tab; 27762306a36Sopenharmony_ci struct sender_context *pos, *n; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci argc = parse_options(argc, argv, options, 28062306a36Sopenharmony_ci bench_sched_message_usage, 0); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t)); 28362306a36Sopenharmony_ci if (!pth_tab) 28462306a36Sopenharmony_ci err(EXIT_FAILURE, "main:malloc()"); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci fdpair(readyfds); 28762306a36Sopenharmony_ci fdpair(wakefds); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci total_children = 0; 29062306a36Sopenharmony_ci for (i = 0; i < num_groups; i++) 29162306a36Sopenharmony_ci total_children += group(pth_tab+total_children, num_fds, 29262306a36Sopenharmony_ci readyfds[1], wakefds[0]); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Wait for everyone to be ready */ 29562306a36Sopenharmony_ci for (i = 0; i < total_children; i++) 29662306a36Sopenharmony_ci if (read(readyfds[0], &dummy, 1) != 1) 29762306a36Sopenharmony_ci err(EXIT_FAILURE, "Reading for readyfds"); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci gettimeofday(&start, NULL); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Kick them off */ 30262306a36Sopenharmony_ci if (write(wakefds[1], &dummy, 1) != 1) 30362306a36Sopenharmony_ci err(EXIT_FAILURE, "Writing to start them"); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Reap them all */ 30662306a36Sopenharmony_ci for (i = 0; i < total_children; i++) 30762306a36Sopenharmony_ci reap_worker(pth_tab[i]); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci gettimeofday(&stop, NULL); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci timersub(&stop, &start, &diff); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci switch (bench_format) { 31462306a36Sopenharmony_ci case BENCH_FORMAT_DEFAULT: 31562306a36Sopenharmony_ci printf("# %d sender and receiver %s per group\n", 31662306a36Sopenharmony_ci num_fds, thread_mode ? "threads" : "processes"); 31762306a36Sopenharmony_ci printf("# %d groups == %d %s run\n\n", 31862306a36Sopenharmony_ci num_groups, num_groups * 2 * num_fds, 31962306a36Sopenharmony_ci thread_mode ? "threads" : "processes"); 32062306a36Sopenharmony_ci printf(" %14s: %lu.%03lu [sec]\n", "Total time", 32162306a36Sopenharmony_ci (unsigned long) diff.tv_sec, 32262306a36Sopenharmony_ci (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case BENCH_FORMAT_SIMPLE: 32562306a36Sopenharmony_ci printf("%lu.%03lu\n", (unsigned long) diff.tv_sec, 32662306a36Sopenharmony_ci (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci default: 32962306a36Sopenharmony_ci /* reaching here is something disaster */ 33062306a36Sopenharmony_ci fprintf(stderr, "Unknown format:%d\n", bench_format); 33162306a36Sopenharmony_ci exit(1); 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci free(pth_tab); 33662306a36Sopenharmony_ci list_for_each_entry_safe(pos, n, &sender_contexts, list) { 33762306a36Sopenharmony_ci list_del_init(&pos->list); 33862306a36Sopenharmony_ci free(pos); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci list_for_each_entry_safe(pos, n, &receiver_contexts, list) { 34162306a36Sopenharmony_ci list_del_init(&pos->list); 34262306a36Sopenharmony_ci free(pos); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 346