cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sched-messaging.c (7190B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *
      4 * sched-messaging.c
      5 *
      6 * messaging: Benchmark for scheduler and IPC mechanisms
      7 *
      8 * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au>
      9 * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
     10 *
     11 */
     12
     13#include <subcmd/parse-options.h>
     14#include "bench.h"
     15
     16/* Test groups of 20 processes spraying to 20 receivers */
     17#include <pthread.h>
     18#include <stdio.h>
     19#include <stdlib.h>
     20#include <string.h>
     21#include <errno.h>
     22#include <unistd.h>
     23#include <sys/types.h>
     24#include <sys/socket.h>
     25#include <sys/wait.h>
     26#include <sys/time.h>
     27#include <poll.h>
     28#include <limits.h>
     29#include <err.h>
     30#include <linux/time64.h>
     31
     32#define DATASIZE 100
     33
     34static bool use_pipes = false;
     35static unsigned int nr_loops = 100;
     36static bool thread_mode = false;
     37static unsigned int num_groups = 10;
     38
     39struct sender_context {
     40	unsigned int num_fds;
     41	int ready_out;
     42	int wakefd;
     43	int out_fds[];
     44};
     45
     46struct receiver_context {
     47	unsigned int num_packets;
     48	int in_fds[2];
     49	int ready_out;
     50	int wakefd;
     51};
     52
     53static void fdpair(int fds[2])
     54{
     55	if (use_pipes) {
     56		if (pipe(fds) == 0)
     57			return;
     58	} else {
     59		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
     60			return;
     61	}
     62
     63	err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()");
     64}
     65
     66/* Block until we're ready to go */
     67static void ready(int ready_out, int wakefd)
     68{
     69	struct pollfd pollfd = { .fd = wakefd, .events = POLLIN };
     70
     71	/* Tell them we're ready. */
     72	if (write(ready_out, "R", 1) != 1)
     73		err(EXIT_FAILURE, "CLIENT: ready write");
     74
     75	/* Wait for "GO" signal */
     76	if (poll(&pollfd, 1, -1) != 1)
     77		err(EXIT_FAILURE, "poll");
     78}
     79
     80/* Sender sprays nr_loops messages down each file descriptor */
     81static void *sender(struct sender_context *ctx)
     82{
     83	char data[DATASIZE];
     84	unsigned int i, j;
     85
     86	ready(ctx->ready_out, ctx->wakefd);
     87	memset(data, 'S', sizeof(data));
     88
     89	/* Now pump to every receiver. */
     90	for (i = 0; i < nr_loops; i++) {
     91		for (j = 0; j < ctx->num_fds; j++) {
     92			int ret, done = 0;
     93
     94again:
     95			ret = write(ctx->out_fds[j], data + done,
     96				    sizeof(data)-done);
     97			if (ret < 0)
     98				err(EXIT_FAILURE, "SENDER: write");
     99			done += ret;
    100			if (done < DATASIZE)
    101				goto again;
    102		}
    103	}
    104
    105	return NULL;
    106}
    107
    108
    109/* One receiver per fd */
    110static void *receiver(struct receiver_context* ctx)
    111{
    112	unsigned int i;
    113
    114	if (!thread_mode)
    115		close(ctx->in_fds[1]);
    116
    117	/* Wait for start... */
    118	ready(ctx->ready_out, ctx->wakefd);
    119
    120	/* Receive them all */
    121	for (i = 0; i < ctx->num_packets; i++) {
    122		char data[DATASIZE];
    123		int ret, done = 0;
    124
    125again:
    126		ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
    127		if (ret < 0)
    128			err(EXIT_FAILURE, "SERVER: read");
    129		done += ret;
    130		if (done < DATASIZE)
    131			goto again;
    132	}
    133
    134	return NULL;
    135}
    136
    137static pthread_t create_worker(void *ctx, void *(*func)(void *))
    138{
    139	pthread_attr_t attr;
    140	pthread_t childid;
    141	int ret;
    142
    143	if (!thread_mode) {
    144		/* process mode */
    145		/* Fork the receiver. */
    146		switch (fork()) {
    147		case -1:
    148			err(EXIT_FAILURE, "fork()");
    149			break;
    150		case 0:
    151			(*func) (ctx);
    152			exit(0);
    153			break;
    154		default:
    155			break;
    156		}
    157
    158		return (pthread_t)0;
    159	}
    160
    161	if (pthread_attr_init(&attr) != 0)
    162		err(EXIT_FAILURE, "pthread_attr_init:");
    163
    164#ifndef __ia64__
    165	if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
    166		err(EXIT_FAILURE, "pthread_attr_setstacksize");
    167#endif
    168
    169	ret = pthread_create(&childid, &attr, func, ctx);
    170	if (ret != 0)
    171		err(EXIT_FAILURE, "pthread_create failed");
    172
    173	return childid;
    174}
    175
    176static void reap_worker(pthread_t id)
    177{
    178	int proc_status;
    179	void *thread_status;
    180
    181	if (!thread_mode) {
    182		/* process mode */
    183		wait(&proc_status);
    184		if (!WIFEXITED(proc_status))
    185			exit(1);
    186	} else {
    187		pthread_join(id, &thread_status);
    188	}
    189}
    190
    191/* One group of senders and receivers */
    192static unsigned int group(pthread_t *pth,
    193		unsigned int num_fds,
    194		int ready_out,
    195		int wakefd)
    196{
    197	unsigned int i;
    198	struct sender_context *snd_ctx = malloc(sizeof(struct sender_context)
    199			+ num_fds * sizeof(int));
    200
    201	if (!snd_ctx)
    202		err(EXIT_FAILURE, "malloc()");
    203
    204	for (i = 0; i < num_fds; i++) {
    205		int fds[2];
    206		struct receiver_context *ctx = malloc(sizeof(*ctx));
    207
    208		if (!ctx)
    209			err(EXIT_FAILURE, "malloc()");
    210
    211
    212		/* Create the pipe between client and server */
    213		fdpair(fds);
    214
    215		ctx->num_packets = num_fds * nr_loops;
    216		ctx->in_fds[0] = fds[0];
    217		ctx->in_fds[1] = fds[1];
    218		ctx->ready_out = ready_out;
    219		ctx->wakefd = wakefd;
    220
    221		pth[i] = create_worker(ctx, (void *)receiver);
    222
    223		snd_ctx->out_fds[i] = fds[1];
    224		if (!thread_mode)
    225			close(fds[0]);
    226	}
    227
    228	/* Now we have all the fds, fork the senders */
    229	for (i = 0; i < num_fds; i++) {
    230		snd_ctx->ready_out = ready_out;
    231		snd_ctx->wakefd = wakefd;
    232		snd_ctx->num_fds = num_fds;
    233
    234		pth[num_fds+i] = create_worker(snd_ctx, (void *)sender);
    235	}
    236
    237	/* Close the fds we have left */
    238	if (!thread_mode)
    239		for (i = 0; i < num_fds; i++)
    240			close(snd_ctx->out_fds[i]);
    241
    242	/* Return number of children to reap */
    243	return num_fds * 2;
    244}
    245
    246static const struct option options[] = {
    247	OPT_BOOLEAN('p', "pipe", &use_pipes,
    248		    "Use pipe() instead of socketpair()"),
    249	OPT_BOOLEAN('t', "thread", &thread_mode,
    250		    "Be multi thread instead of multi process"),
    251	OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"),
    252	OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"),
    253	OPT_END()
    254};
    255
    256static const char * const bench_sched_message_usage[] = {
    257	"perf bench sched messaging <options>",
    258	NULL
    259};
    260
    261int bench_sched_messaging(int argc, const char **argv)
    262{
    263	unsigned int i, total_children;
    264	struct timeval start, stop, diff;
    265	unsigned int num_fds = 20;
    266	int readyfds[2], wakefds[2];
    267	char dummy;
    268	pthread_t *pth_tab;
    269
    270	argc = parse_options(argc, argv, options,
    271			     bench_sched_message_usage, 0);
    272
    273	pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
    274	if (!pth_tab)
    275		err(EXIT_FAILURE, "main:malloc()");
    276
    277	fdpair(readyfds);
    278	fdpair(wakefds);
    279
    280	total_children = 0;
    281	for (i = 0; i < num_groups; i++)
    282		total_children += group(pth_tab+total_children, num_fds,
    283					readyfds[1], wakefds[0]);
    284
    285	/* Wait for everyone to be ready */
    286	for (i = 0; i < total_children; i++)
    287		if (read(readyfds[0], &dummy, 1) != 1)
    288			err(EXIT_FAILURE, "Reading for readyfds");
    289
    290	gettimeofday(&start, NULL);
    291
    292	/* Kick them off */
    293	if (write(wakefds[1], &dummy, 1) != 1)
    294		err(EXIT_FAILURE, "Writing to start them");
    295
    296	/* Reap them all */
    297	for (i = 0; i < total_children; i++)
    298		reap_worker(pth_tab[i]);
    299
    300	gettimeofday(&stop, NULL);
    301
    302	timersub(&stop, &start, &diff);
    303
    304	switch (bench_format) {
    305	case BENCH_FORMAT_DEFAULT:
    306		printf("# %d sender and receiver %s per group\n",
    307		       num_fds, thread_mode ? "threads" : "processes");
    308		printf("# %d groups == %d %s run\n\n",
    309		       num_groups, num_groups * 2 * num_fds,
    310		       thread_mode ? "threads" : "processes");
    311		printf(" %14s: %lu.%03lu [sec]\n", "Total time",
    312		       (unsigned long) diff.tv_sec,
    313		       (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
    314		break;
    315	case BENCH_FORMAT_SIMPLE:
    316		printf("%lu.%03lu\n", (unsigned long) diff.tv_sec,
    317		       (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
    318		break;
    319	default:
    320		/* reaching here is something disaster */
    321		fprintf(stderr, "Unknown format:%d\n", bench_format);
    322		exit(1);
    323		break;
    324	}
    325
    326	free(pth_tab);
    327
    328	return 0;
    329}