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