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