1/******************************************************************************/
2/* Copyright Rusty Russell,                                                   */
3/* Copyright Pierre Peiffer                                                   */
4/* Copyright Zhang, Yanmin,                                                   */
5/* Copyright Ingo Molnar,                                                     */
6/* Copyright Arjan van de Ven,                                                */
7/* Copyright (c) International Business Machines  Corp., 2008                 */
8/*                                                                            */
9/* This program is free software;  you can redistribute it and/or modify      */
10/* it under the terms of the GNU General Public License as published by       */
11/* the Free Software Foundation; either version 2 of the License, or          */
12/* (at your option) any later version.                                        */
13/*                                                                            */
14/* This program is distributed in the hope that it will be useful,            */
15/* but WITHOUT ANY WARRANTY;  without even the implied warranty of            */
16/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See                  */
17/* the GNU General Public License for more details.                           */
18/*                                                                            */
19/* You should have received a copy of the GNU General Public License          */
20/* along with this program;  if not, write to the Free Software               */
21/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
22/*                                                                            */
23/******************************************************************************/
24
25/******************************************************************************/
26/*                                                                            */
27/* File:        hackbench.c                                                   */
28/*                                                                            */
29/* Description: hackbench tests the Linux scheduler. Test groups of 20        */
30/*              processes spraying to 20 receivers                            */
31/*                                                                            */
32/* Total Tests: 1                                                             */
33/*                                                                            */
34/* Test Name:   hackbench01 and hackbench02                                   */
35/*                                                                            */
36/* Test Assertion:                                                            */
37/*                                                                            */
38/* Author(s):   Rusty Russell <rusty@rustcorp.com.au>,                        */
39/*              Pierre Peiffer <pierre.peiffer@bull.net>,                     */
40/*              Ingo Molnar <mingo@elte.hu>,                                  */
41/*              Arjan van de Ven <arjan@infradead.org>,                       */
42/*              "Zhang, Yanmin" <yanmin_zhang@linux.intel.com>,               */
43/*              Nathan Lynch <ntl@pobox.com>                                  */
44/*                                                                            */
45/* History:     Included into LTP                                             */
46/*                  - June 26 2008 - Subrata Modak<subrata@linux.vnet.ibm.com>*/
47/*                                                                            */
48/******************************************************************************/
49#include <pthread.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <errno.h>
54#include <unistd.h>
55#include <sys/types.h>
56#include <sys/socket.h>
57#include <sys/wait.h>
58#include <sys/time.h>
59#include <sys/poll.h>
60#include <limits.h>
61
62#define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
63#define DATASIZE 100
64static struct sender_context **snd_ctx_tab;	/*Table for sender context pointers. */
65static struct receiver_context **rev_ctx_tab;	/*Table for receiver context pointers. */
66static int gr_num = 0;		/*For group calculation */
67static unsigned int loops = 100;
68/*
69 * 0 means thread mode and others mean process (default)
70 */
71static unsigned int process_mode = 1;
72
73static int use_pipes = 0;
74
75struct sender_context {
76	unsigned int num_fds;
77	int ready_out;
78	int wakefd;
79	int out_fds[0];
80};
81
82struct receiver_context {
83	unsigned int num_packets;
84	int in_fds[2];
85	int ready_out;
86	int wakefd;
87};
88
89static void barf(const char *msg)
90{
91	fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
92	exit(1);
93}
94
95static void print_usage_exit()
96{
97	printf
98	    ("Usage: hackbench [-pipe] <num groups> [process|thread] [loops]\n");
99	exit(1);
100}
101
102static void fdpair(int fds[2])
103{
104	if (use_pipes) {
105		if (pipe(fds) == 0)
106			return;
107	} else {
108		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0)
109			return;
110	}
111	barf("Creating fdpair");
112}
113
114/* Block until we're ready to go */
115static void ready(int ready_out, int wakefd)
116{
117	char dummy;
118	struct pollfd pollfd = {.fd = wakefd,.events = POLLIN };
119
120	/* Tell them we're ready. */
121	if (write(ready_out, &dummy, 1) != 1)
122		barf("CLIENT: ready write");
123
124	/* Wait for "GO" signal */
125	if (poll(&pollfd, 1, -1) != 1)
126		barf("poll");
127}
128
129/* Sender sprays loops messages down each file descriptor */
130static void *sender(struct sender_context *ctx)
131{
132	char data[DATASIZE];
133	unsigned int i, j;
134
135	ready(ctx->ready_out, ctx->wakefd);
136
137	/* Now pump to every receiver. */
138	for (i = 0; i < loops; i++) {
139		for (j = 0; j < ctx->num_fds; j++) {
140			int ret, done = 0;
141
142again:
143			ret =
144			    write(ctx->out_fds[j], data + done,
145				  sizeof(data) - done);
146			if (ret < 0)
147				barf("SENDER: write");
148			done += ret;
149			if (done < sizeof(data))
150				goto again;
151		}
152	}
153
154	return NULL;
155}
156
157/* One receiver per fd */
158static void *receiver(struct receiver_context *ctx)
159{
160	unsigned int i;
161
162	if (process_mode)
163		close(ctx->in_fds[1]);
164
165	/* Wait for start... */
166	ready(ctx->ready_out, ctx->wakefd);
167
168	/* Receive them all */
169	for (i = 0; i < ctx->num_packets; i++) {
170		char data[DATASIZE];
171		int ret, done = 0;
172
173again:
174		ret = read(ctx->in_fds[0], data + done, DATASIZE - done);
175		if (ret < 0)
176			barf("SERVER: read");
177		done += ret;
178		if (done < DATASIZE)
179			goto again;
180	}
181
182	return NULL;
183}
184
185pthread_t create_worker(void *ctx, void *(*func) (void *))
186{
187	pthread_attr_t attr;
188	pthread_t childid;
189	int err;
190
191	if (process_mode) {
192		/* process mode */
193		/* Fork the receiver. */
194		switch (fork()) {
195		case -1:
196			barf("fork()");
197		case 0:
198			(*func) (ctx);
199			exit(0);
200		}
201
202		return (pthread_t) 0;
203	}
204
205	if (pthread_attr_init(&attr) != 0)
206		barf("pthread_attr_init:");
207
208#ifndef __ia64__
209	if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0)
210		barf("pthread_attr_setstacksize");
211#endif
212
213	if ((err = pthread_create(&childid, &attr, func, ctx)) != 0) {
214		fprintf(stderr, "pthread_create failed: %s (%d)\n",
215			strerror(err), err);
216		exit(-1);
217	}
218	return (childid);
219}
220
221void reap_worker(pthread_t id)
222{
223	int status;
224
225	if (process_mode) {
226		/* process mode */
227		wait(&status);
228		if (!WIFEXITED(status))
229			exit(1);
230	} else {
231		void *status;
232
233		pthread_join(id, &status);
234	}
235}
236
237/* One group of senders and receivers */
238static unsigned int group(pthread_t * pth,
239			  unsigned int num_fds, int ready_out, int wakefd)
240{
241	unsigned int i;
242	struct sender_context *snd_ctx = malloc(sizeof(struct sender_context) + num_fds * sizeof(int));
243	if (!snd_ctx)
244		barf("malloc()");
245	else
246		snd_ctx_tab[gr_num] = snd_ctx;
247
248	for (i = 0; i < num_fds; i++) {
249		int fds[2];
250		struct receiver_context *ctx = malloc(sizeof(*ctx));
251
252		if (!ctx)
253			barf("malloc()");
254		else
255			rev_ctx_tab[gr_num * num_fds + i] = ctx;
256
257		/* Create the pipe between client and server */
258		fdpair(fds);
259
260		ctx->num_packets = num_fds * loops;
261		ctx->in_fds[0] = fds[0];
262		ctx->in_fds[1] = fds[1];
263		ctx->ready_out = ready_out;
264		ctx->wakefd = wakefd;
265
266		pth[i] = create_worker(ctx, (void *)(void *)receiver);
267
268		snd_ctx->out_fds[i] = fds[1];
269		if (process_mode)
270			close(fds[0]);
271	}
272
273	/* Now we have all the fds, fork the senders */
274	for (i = 0; i < num_fds; i++) {
275		snd_ctx->ready_out = ready_out;
276		snd_ctx->wakefd = wakefd;
277		snd_ctx->num_fds = num_fds;
278
279		pth[num_fds + i] =
280		    create_worker(snd_ctx, (void *)(void *)sender);
281	}
282
283	/* Close the fds we have left */
284	if (process_mode)
285		for (i = 0; i < num_fds; i++)
286			close(snd_ctx->out_fds[i]);
287
288	gr_num++;
289	/* Return number of children to reap */
290	return num_fds * 2;
291}
292
293int main(int argc, char *argv[])
294{
295	unsigned int i, j, num_groups = 10, total_children;
296	struct timeval start, stop, diff;
297	unsigned int num_fds = 20;
298	int readyfds[2], wakefds[2];
299	char dummy;
300	pthread_t *pth_tab;
301
302	if (argv[1] && strcmp(argv[1], "-pipe") == 0) {
303		use_pipes = 1;
304		argc--;
305		argv++;
306	}
307
308	if (argc >= 2 && (num_groups = atoi(argv[1])) == 0)
309		print_usage_exit();
310
311	printf("Running with %d*40 (== %d) tasks.\n",
312	       num_groups, num_groups * 40);
313
314	fflush(NULL);
315
316	if (argc > 2) {
317		if (!strcmp(argv[2], "process"))
318			process_mode = 1;
319		else if (!strcmp(argv[2], "thread"))
320			process_mode = 0;
321		else
322			print_usage_exit();
323	}
324
325	if (argc > 3)
326		loops = atoi(argv[3]);
327
328	pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t));
329	snd_ctx_tab = malloc(num_groups * sizeof(void *));
330	rev_ctx_tab = malloc(num_groups * num_fds * sizeof(void *));
331	if (!pth_tab || !snd_ctx_tab || !rev_ctx_tab)
332		barf("main:malloc()");
333
334	fdpair(readyfds);
335	fdpair(wakefds);
336
337	total_children = 0;
338	for (i = 0; i < num_groups; i++)
339		total_children +=
340		    group(pth_tab + total_children, num_fds, readyfds[1],
341			  wakefds[0]);
342
343	/* Wait for everyone to be ready */
344	for (i = 0; i < total_children; i++)
345		if (read(readyfds[0], &dummy, 1) != 1)
346			barf("Reading for readyfds");
347
348	gettimeofday(&start, NULL);
349
350	/* Kick them off */
351	if (write(wakefds[1], &dummy, 1) != 1)
352		barf("Writing to start them");
353
354	/* Reap them all */
355	for (i = 0; i < total_children; i++)
356		reap_worker(pth_tab[i]);
357
358	gettimeofday(&stop, NULL);
359
360	/* Print time... */
361	timersub(&stop, &start, &diff);
362	printf("Time: %lu.%03lu\n", diff.tv_sec, diff.tv_usec / 1000);
363
364	/* free the memory */
365	for (i = 0; i < num_groups; i++) {
366		for (j = 0; j < num_fds; j++) {
367			SAFE_FREE(rev_ctx_tab[i * num_fds + j])
368		}
369		SAFE_FREE(snd_ctx_tab[i]);
370	}
371	SAFE_FREE(pth_tab);
372	SAFE_FREE(snd_ctx_tab);
373	SAFE_FREE(rev_ctx_tab);
374	exit(0);
375}
376