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