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