18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * sched-messaging.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * messaging: Benchmark for scheduler and IPC mechanisms 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au> 98c2ecf20Sopenharmony_ci * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <subcmd/parse-options.h> 148c2ecf20Sopenharmony_ci#include "bench.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Test groups of 20 processes spraying to 20 receivers */ 178c2ecf20Sopenharmony_ci#include <pthread.h> 188c2ecf20Sopenharmony_ci#include <stdio.h> 198c2ecf20Sopenharmony_ci#include <stdlib.h> 208c2ecf20Sopenharmony_ci#include <string.h> 218c2ecf20Sopenharmony_ci#include <errno.h> 228c2ecf20Sopenharmony_ci#include <unistd.h> 238c2ecf20Sopenharmony_ci#include <sys/types.h> 248c2ecf20Sopenharmony_ci#include <sys/socket.h> 258c2ecf20Sopenharmony_ci#include <sys/wait.h> 268c2ecf20Sopenharmony_ci#include <sys/time.h> 278c2ecf20Sopenharmony_ci#include <poll.h> 288c2ecf20Sopenharmony_ci#include <limits.h> 298c2ecf20Sopenharmony_ci#include <err.h> 308c2ecf20Sopenharmony_ci#include <linux/time64.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define DATASIZE 100 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic bool use_pipes = false; 358c2ecf20Sopenharmony_cistatic unsigned int nr_loops = 100; 368c2ecf20Sopenharmony_cistatic bool thread_mode = false; 378c2ecf20Sopenharmony_cistatic unsigned int num_groups = 10; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct sender_context { 408c2ecf20Sopenharmony_ci unsigned int num_fds; 418c2ecf20Sopenharmony_ci int ready_out; 428c2ecf20Sopenharmony_ci int wakefd; 438c2ecf20Sopenharmony_ci int out_fds[]; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct receiver_context { 478c2ecf20Sopenharmony_ci unsigned int num_packets; 488c2ecf20Sopenharmony_ci int in_fds[2]; 498c2ecf20Sopenharmony_ci int ready_out; 508c2ecf20Sopenharmony_ci int wakefd; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void fdpair(int fds[2]) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci if (use_pipes) { 568c2ecf20Sopenharmony_ci if (pipe(fds) == 0) 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci } else { 598c2ecf20Sopenharmony_ci if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()"); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Block until we're ready to go */ 678c2ecf20Sopenharmony_cistatic void ready(int ready_out, int wakefd) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* Tell them we're ready. */ 728c2ecf20Sopenharmony_ci if (write(ready_out, "R", 1) != 1) 738c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "CLIENT: ready write"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Wait for "GO" signal */ 768c2ecf20Sopenharmony_ci if (poll(&pollfd, 1, -1) != 1) 778c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "poll"); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Sender sprays nr_loops messages down each file descriptor */ 818c2ecf20Sopenharmony_cistatic void *sender(struct sender_context *ctx) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci char data[DATASIZE]; 848c2ecf20Sopenharmony_ci unsigned int i, j; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ready(ctx->ready_out, ctx->wakefd); 878c2ecf20Sopenharmony_ci memset(data, 'S', sizeof(data)); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Now pump to every receiver. */ 908c2ecf20Sopenharmony_ci for (i = 0; i < nr_loops; i++) { 918c2ecf20Sopenharmony_ci for (j = 0; j < ctx->num_fds; j++) { 928c2ecf20Sopenharmony_ci int ret, done = 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciagain: 958c2ecf20Sopenharmony_ci ret = write(ctx->out_fds[j], data + done, 968c2ecf20Sopenharmony_ci sizeof(data)-done); 978c2ecf20Sopenharmony_ci if (ret < 0) 988c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "SENDER: write"); 998c2ecf20Sopenharmony_ci done += ret; 1008c2ecf20Sopenharmony_ci if (done < DATASIZE) 1018c2ecf20Sopenharmony_ci goto again; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return NULL; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* One receiver per fd */ 1108c2ecf20Sopenharmony_cistatic void *receiver(struct receiver_context* ctx) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci unsigned int i; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!thread_mode) 1158c2ecf20Sopenharmony_ci close(ctx->in_fds[1]); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Wait for start... */ 1188c2ecf20Sopenharmony_ci ready(ctx->ready_out, ctx->wakefd); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Receive them all */ 1218c2ecf20Sopenharmony_ci for (i = 0; i < ctx->num_packets; i++) { 1228c2ecf20Sopenharmony_ci char data[DATASIZE]; 1238c2ecf20Sopenharmony_ci int ret, done = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciagain: 1268c2ecf20Sopenharmony_ci ret = read(ctx->in_fds[0], data + done, DATASIZE - done); 1278c2ecf20Sopenharmony_ci if (ret < 0) 1288c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "SERVER: read"); 1298c2ecf20Sopenharmony_ci done += ret; 1308c2ecf20Sopenharmony_ci if (done < DATASIZE) 1318c2ecf20Sopenharmony_ci goto again; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return NULL; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic pthread_t create_worker(void *ctx, void *(*func)(void *)) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci pthread_attr_t attr; 1408c2ecf20Sopenharmony_ci pthread_t childid; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!thread_mode) { 1448c2ecf20Sopenharmony_ci /* process mode */ 1458c2ecf20Sopenharmony_ci /* Fork the receiver. */ 1468c2ecf20Sopenharmony_ci switch (fork()) { 1478c2ecf20Sopenharmony_ci case -1: 1488c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "fork()"); 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case 0: 1518c2ecf20Sopenharmony_ci (*func) (ctx); 1528c2ecf20Sopenharmony_ci exit(0); 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return (pthread_t)0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (pthread_attr_init(&attr) != 0) 1628c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "pthread_attr_init:"); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#ifndef __ia64__ 1658c2ecf20Sopenharmony_ci if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) 1668c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "pthread_attr_setstacksize"); 1678c2ecf20Sopenharmony_ci#endif 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ret = pthread_create(&childid, &attr, func, ctx); 1708c2ecf20Sopenharmony_ci if (ret != 0) 1718c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "pthread_create failed"); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return childid; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void reap_worker(pthread_t id) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci int proc_status; 1798c2ecf20Sopenharmony_ci void *thread_status; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (!thread_mode) { 1828c2ecf20Sopenharmony_ci /* process mode */ 1838c2ecf20Sopenharmony_ci wait(&proc_status); 1848c2ecf20Sopenharmony_ci if (!WIFEXITED(proc_status)) 1858c2ecf20Sopenharmony_ci exit(1); 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci pthread_join(id, &thread_status); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* One group of senders and receivers */ 1928c2ecf20Sopenharmony_cistatic unsigned int group(pthread_t *pth, 1938c2ecf20Sopenharmony_ci unsigned int num_fds, 1948c2ecf20Sopenharmony_ci int ready_out, 1958c2ecf20Sopenharmony_ci int wakefd) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci unsigned int i; 1988c2ecf20Sopenharmony_ci struct sender_context *snd_ctx = malloc(sizeof(struct sender_context) 1998c2ecf20Sopenharmony_ci + num_fds * sizeof(int)); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!snd_ctx) 2028c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "malloc()"); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (i = 0; i < num_fds; i++) { 2058c2ecf20Sopenharmony_ci int fds[2]; 2068c2ecf20Sopenharmony_ci struct receiver_context *ctx = malloc(sizeof(*ctx)); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (!ctx) 2098c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "malloc()"); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Create the pipe between client and server */ 2138c2ecf20Sopenharmony_ci fdpair(fds); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ctx->num_packets = num_fds * nr_loops; 2168c2ecf20Sopenharmony_ci ctx->in_fds[0] = fds[0]; 2178c2ecf20Sopenharmony_ci ctx->in_fds[1] = fds[1]; 2188c2ecf20Sopenharmony_ci ctx->ready_out = ready_out; 2198c2ecf20Sopenharmony_ci ctx->wakefd = wakefd; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci pth[i] = create_worker(ctx, (void *)receiver); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci snd_ctx->out_fds[i] = fds[1]; 2248c2ecf20Sopenharmony_ci if (!thread_mode) 2258c2ecf20Sopenharmony_ci close(fds[0]); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Now we have all the fds, fork the senders */ 2298c2ecf20Sopenharmony_ci for (i = 0; i < num_fds; i++) { 2308c2ecf20Sopenharmony_ci snd_ctx->ready_out = ready_out; 2318c2ecf20Sopenharmony_ci snd_ctx->wakefd = wakefd; 2328c2ecf20Sopenharmony_ci snd_ctx->num_fds = num_fds; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pth[num_fds+i] = create_worker(snd_ctx, (void *)sender); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Close the fds we have left */ 2388c2ecf20Sopenharmony_ci if (!thread_mode) 2398c2ecf20Sopenharmony_ci for (i = 0; i < num_fds; i++) 2408c2ecf20Sopenharmony_ci close(snd_ctx->out_fds[i]); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Return number of children to reap */ 2438c2ecf20Sopenharmony_ci return num_fds * 2; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic const struct option options[] = { 2478c2ecf20Sopenharmony_ci OPT_BOOLEAN('p', "pipe", &use_pipes, 2488c2ecf20Sopenharmony_ci "Use pipe() instead of socketpair()"), 2498c2ecf20Sopenharmony_ci OPT_BOOLEAN('t', "thread", &thread_mode, 2508c2ecf20Sopenharmony_ci "Be multi thread instead of multi process"), 2518c2ecf20Sopenharmony_ci OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"), 2528c2ecf20Sopenharmony_ci OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"), 2538c2ecf20Sopenharmony_ci OPT_END() 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic const char * const bench_sched_message_usage[] = { 2578c2ecf20Sopenharmony_ci "perf bench sched messaging <options>", 2588c2ecf20Sopenharmony_ci NULL 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ciint bench_sched_messaging(int argc, const char **argv) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci unsigned int i, total_children; 2648c2ecf20Sopenharmony_ci struct timeval start, stop, diff; 2658c2ecf20Sopenharmony_ci unsigned int num_fds = 20; 2668c2ecf20Sopenharmony_ci int readyfds[2], wakefds[2]; 2678c2ecf20Sopenharmony_ci char dummy; 2688c2ecf20Sopenharmony_ci pthread_t *pth_tab; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci argc = parse_options(argc, argv, options, 2718c2ecf20Sopenharmony_ci bench_sched_message_usage, 0); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t)); 2748c2ecf20Sopenharmony_ci if (!pth_tab) 2758c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "main:malloc()"); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci fdpair(readyfds); 2788c2ecf20Sopenharmony_ci fdpair(wakefds); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci total_children = 0; 2818c2ecf20Sopenharmony_ci for (i = 0; i < num_groups; i++) 2828c2ecf20Sopenharmony_ci total_children += group(pth_tab+total_children, num_fds, 2838c2ecf20Sopenharmony_ci readyfds[1], wakefds[0]); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Wait for everyone to be ready */ 2868c2ecf20Sopenharmony_ci for (i = 0; i < total_children; i++) 2878c2ecf20Sopenharmony_ci if (read(readyfds[0], &dummy, 1) != 1) 2888c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "Reading for readyfds"); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci gettimeofday(&start, NULL); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Kick them off */ 2938c2ecf20Sopenharmony_ci if (write(wakefds[1], &dummy, 1) != 1) 2948c2ecf20Sopenharmony_ci err(EXIT_FAILURE, "Writing to start them"); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Reap them all */ 2978c2ecf20Sopenharmony_ci for (i = 0; i < total_children; i++) 2988c2ecf20Sopenharmony_ci reap_worker(pth_tab[i]); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci gettimeofday(&stop, NULL); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci timersub(&stop, &start, &diff); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci switch (bench_format) { 3058c2ecf20Sopenharmony_ci case BENCH_FORMAT_DEFAULT: 3068c2ecf20Sopenharmony_ci printf("# %d sender and receiver %s per group\n", 3078c2ecf20Sopenharmony_ci num_fds, thread_mode ? "threads" : "processes"); 3088c2ecf20Sopenharmony_ci printf("# %d groups == %d %s run\n\n", 3098c2ecf20Sopenharmony_ci num_groups, num_groups * 2 * num_fds, 3108c2ecf20Sopenharmony_ci thread_mode ? "threads" : "processes"); 3118c2ecf20Sopenharmony_ci printf(" %14s: %lu.%03lu [sec]\n", "Total time", 3128c2ecf20Sopenharmony_ci diff.tv_sec, 3138c2ecf20Sopenharmony_ci (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci case BENCH_FORMAT_SIMPLE: 3168c2ecf20Sopenharmony_ci printf("%lu.%03lu\n", diff.tv_sec, 3178c2ecf20Sopenharmony_ci (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci default: 3208c2ecf20Sopenharmony_ci /* reaching here is something disaster */ 3218c2ecf20Sopenharmony_ci fprintf(stderr, "Unknown format:%d\n", bench_format); 3228c2ecf20Sopenharmony_ci exit(1); 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci free(pth_tab); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 330