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