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
64 static struct sender_context **snd_ctx_tab; /*Table for sender context pointers. */
65 static struct receiver_context **rev_ctx_tab; /*Table for receiver context pointers. */
66 static int gr_num = 0; /*For group calculation */
67 static unsigned int loops = 100;
68 /*
69 * 0 means thread mode and others mean process (default)
70 */
71 static unsigned int process_mode = 1;
72
73 static int use_pipes = 0;
74
75 struct sender_context {
76 unsigned int num_fds;
77 int ready_out;
78 int wakefd;
79 int out_fds[0];
80 };
81
82 struct receiver_context {
83 unsigned int num_packets;
84 int in_fds[2];
85 int ready_out;
86 int wakefd;
87 };
88
barf(const char * msg)89 static void barf(const char *msg)
90 {
91 fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno));
92 exit(1);
93 }
94
print_usage_exit(void)95 static void print_usage_exit(void)
96 {
97 printf
98 ("Usage: hackbench [-pipe] <num groups> [process|thread] [loops]\n");
99 exit(1);
100 }
101
fdpair(int fds[2])102 static 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 */
ready(int ready_out,int wakefd)115 static 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 */
sender(struct sender_context * ctx)130 static 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
142 again:
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 */
receiver(struct receiver_context * ctx)158 static 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
173 again:
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
create_worker(void * ctx,void * (* func)(void *))185 pthread_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
reap_worker(pthread_t id)221 void 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 */
group(pthread_t * pth,unsigned int num_fds,int ready_out,int wakefd)238 static 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
main(int argc,char * argv[])293 int 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