1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms 5 * of the Common Development and Distribution License 6 * (the "License"). You may not use this file except 7 * in compliance with the License. 8 * 9 * You can obtain a copy of the license at 10 * src/OPENSOLARIS.LICENSE 11 * or http://www.opensolaris.org/os/licensing. 12 * See the License for the specific language governing 13 * permissions and limitations under the License. 14 * 15 * When distributing Covered Code, include this CDDL 16 * HEADER in each file and include the License file at 17 * usr/src/OPENSOLARIS.LICENSE. If applicable, 18 * add the following below this CDDL HEADER, with the 19 * fields enclosed by brackets "[]" replaced with your 20 * own identifying information: Portions Copyright [yyyy] 21 * [name of copyright owner] 22 * 23 * CDDL HEADER END 24 */ 25 26/* 27 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31#include <sys/types.h> 32#include <sys/stat.h> 33#include <sys/wait.h> 34#include <sys/socket.h> 35#include <netinet/in.h> 36#include <netinet/tcp.h> 37#include <arpa/inet.h> 38#include <netdb.h> 39#include <pthread.h> 40#include <signal.h> 41#include <string.h> 42#include <unistd.h> 43#include <stdlib.h> 44#include <stdio.h> 45#include <fcntl.h> 46#include <errno.h> 47 48#include "libmicro.h" 49 50typedef struct { 51 int ts_once; 52 pid_t ts_child; 53 pthread_t ts_thread; 54 int ts_in; 55 int ts_out; 56 int ts_in2; 57 int ts_out2; 58 int ts_lsn; 59 struct sockaddr_in ts_add; 60} tsd_t; 61 62#define FIRSTPORT 12345 63 64static char *modes[] = {"st", "mt", "mp", NULL}; 65#define MD_SINGLE 0 66#define MD_MULTITHREAD 1 67#define MD_MULTIPROCESS 2 68 69static char *xports[] = {"pipe", "fifo", "sock", "tcp", 70 NULL}; 71#define XP_PIPES 0 72#define XP_FIFOS 1 73#define XP_SOCKETPAIR 2 74#define XP_LOCALTCP 3 75 76#define DEFM MD_SINGLE 77#define DEFS 1024 78#define DEFX XP_PIPES 79 80static int optm = DEFM; 81static size_t opts = DEFS; 82static int optx = DEFX; 83static void *rbuf = NULL; 84static void *wbuf = NULL; 85 86int readall(int s, void *buf, size_t len); 87void *loopback(void *arg); 88int prepare_pipes(tsd_t *tsd); 89int prepare_fifos(tsd_t *tsd); 90int cleanup_fifos(tsd_t *tsd); 91int prepare_socketpair(tsd_t *tsd); 92int prepare_localtcp(tsd_t *tsd); 93int prepare_localtcp_once(tsd_t *tsd); 94char *lookupa(int x, char *names[]); 95int lookup(char *x, char *names[]); 96 97int 98benchmark_init() 99{ 100 lm_tsdsize = sizeof (tsd_t); 101 102 (void) sprintf(lm_optstr, "m:s:x:"); 103 104 (void) sprintf(lm_usage, 105 " [-m mode (st|mt|mp, default %s)]\n" 106 " [-s buffer-size (default %d)]\n" 107 " [-x transport (pipe|fifo|sock|tcp, default %s)]\n" 108 "notes: measures write()/read() across various transports\n", 109 lookupa(DEFM, modes), DEFS, lookupa(DEFX, xports)); 110 111 (void) sprintf(lm_header, "%2s %4s", "md", "xprt"); 112 113 return (0); 114} 115 116int 117benchmark_optswitch(int opt, char *optarg) 118{ 119 int x; 120 121 switch (opt) { 122 case 'm': 123 x = lookup(optarg, modes); 124 if (x == -1) 125 return (-1); 126 optm = x; 127 break; 128 case 's': 129 opts = sizetoll(optarg); 130 break; 131 case 'x': 132 x = lookup(optarg, xports); 133 if (x == -1) 134 return (-1); 135 optx = x; 136 break; 137 default: 138 return (-1); 139 } 140 return (0); 141} 142 143int 144benchmark_initrun() 145{ 146 if (optx == XP_FIFOS) { 147 if (geteuid() != 0) { 148 (void) printf("sorry, must be root to create fifos\n"); 149 exit(1); 150 } 151 } 152 153 (void) setfdlimit(4 * lm_optT + 10); 154 155 rbuf = malloc(opts); 156 wbuf = malloc(opts); 157 158 return (0); 159} 160 161int 162benchmark_initbatch(void *tsd) 163{ 164 tsd_t *ts = (tsd_t *)tsd; 165 int result; 166 pid_t pid; 167 int i; 168 169 switch (optx) { 170 case XP_SOCKETPAIR: 171 result = prepare_socketpair(ts); 172 break; 173 case XP_LOCALTCP: 174 result = prepare_localtcp(ts); 175 break; 176 case XP_FIFOS: 177 result = prepare_fifos(ts); 178 break; 179 case XP_PIPES: 180 default: 181 result = prepare_pipes(ts); 182 break; 183 } 184 if (result == -1) { 185 return (1); 186 } 187 188 switch (optm) { 189 case MD_MULTITHREAD: 190 result = pthread_create(&ts->ts_thread, NULL, loopback, tsd); 191 if (result == -1) { 192 return (1); 193 } 194 break; 195 case MD_MULTIPROCESS: 196 pid = fork(); 197 switch (pid) { 198 case 0: 199 (void) loopback(tsd); 200 exit(0); 201 break; 202 case -1: 203 return (-1); 204 default: 205 ts->ts_child = pid; 206 break; 207 } 208 break; 209 case MD_SINGLE: 210 default: 211 break; 212 } 213 214 /* Prime the loopback */ 215 if (write(ts->ts_out, wbuf, opts) != opts) { 216 return (1); 217 } 218 if (readall(ts->ts_in, rbuf, opts) != opts) { 219 return (1); 220 } 221 222 return (0); 223} 224 225int 226benchmark(void *tsd, result_t *res) 227{ 228 tsd_t *ts = (tsd_t *)tsd; 229 int i; 230 int n; 231 232 for (i = 0; i < lm_optB; i++) { 233 if (write(ts->ts_out, wbuf, opts) != opts) { 234 res->re_errors++; 235 continue; 236 } 237 238 n = readall(ts->ts_in, rbuf, opts); 239 if (n == -1) { 240 res->re_errors++; 241 continue; 242 } 243 } 244 res->re_count = i; 245 246 return (0); 247} 248 249int 250benchmark_finibatch(void *tsd) 251{ 252 tsd_t *ts = (tsd_t *)tsd; 253 254 /* Terminate the loopback */ 255 (void) write(ts->ts_out, wbuf, opts); 256 (void) readall(ts->ts_in, rbuf, opts); 257 258 switch (optm) { 259 case MD_MULTITHREAD: 260 (void) close(ts->ts_in2); 261 (void) close(ts->ts_out2); 262 (void) pthread_join(ts->ts_thread, NULL); 263 break; 264 case MD_MULTIPROCESS: 265 (void) close(ts->ts_in2); 266 (void) close(ts->ts_out2); 267 (void) waitpid(ts->ts_child, NULL, 0); 268 break; 269 case MD_SINGLE: 270 default: 271 break; 272 } 273 274 (void) close(ts->ts_in); 275 (void) close(ts->ts_out); 276 277 if (optx == XP_FIFOS) { 278 (void) cleanup_fifos(ts); 279 } 280 281 return (0); 282} 283 284char * 285benchmark_result() 286{ 287 static char result[256]; 288 289 (void) sprintf(result, "%2s %4s", 290 lookupa(optm, modes), lookupa(optx, xports)); 291 292 return (result); 293} 294 295int 296readall(int s, void *buf, size_t len) 297{ 298 size_t n; 299 size_t total = 0; 300 301 for (;;) { 302 n = read(s, (void *)((long)buf + total), len - total); 303 if (n < 1) { 304 return (-1); 305 } 306 total += n; 307 if (total >= len) { 308 return (total); 309 } 310 } 311} 312 313void * 314loopback(void *arg) 315{ 316 tsd_t *ts = (tsd_t *)arg; 317 int i, n, m; 318 319 /* Include priming and termination */ 320 m = lm_optB + 2; 321 322 for (i = 0; i < m; i++) { 323 n = readall(ts->ts_in2, rbuf, opts); 324 if (n == -1) { 325 break; 326 } 327 if (write(ts->ts_out2, wbuf, opts) != opts) { 328 break; 329 } 330 } 331 332 return (NULL); 333} 334 335int 336prepare_localtcp_once(tsd_t *ts) 337{ 338 int j; 339 int opt = 1; 340 struct hostent *host; 341 342 j = FIRSTPORT; 343 344 ts->ts_lsn = socket(AF_INET, SOCK_STREAM, 0); 345 if (ts->ts_lsn == -1) { 346 return (-1); 347 } 348 349 if (setsockopt(ts->ts_lsn, SOL_SOCKET, SO_REUSEADDR, 350 &opt, sizeof (int)) == -1) { 351 return (-1); 352 } 353 354 if ((host = gethostbyname("localhost")) == NULL) { 355 return (-1); 356 } 357 358 for (;;) { 359 (void) memset(&ts->ts_add, 0, 360 sizeof (struct sockaddr_in)); 361 ts->ts_add.sin_family = AF_INET; 362 ts->ts_add.sin_port = htons(j++); 363 (void) memcpy(&ts->ts_add.sin_addr.s_addr, 364 host->h_addr_list[0], sizeof (struct in_addr)); 365 366 if (bind(ts->ts_lsn, 367 (struct sockaddr *)&ts->ts_add, 368 sizeof (struct sockaddr_in)) == 0) { 369 break; 370 } 371 372 if (errno != EADDRINUSE) { 373 return (-1); 374 } 375 } 376 377 if (listen(ts->ts_lsn, 5) == -1) { 378 return (-1); 379 } 380 381 return (0); 382} 383 384int 385prepare_localtcp(tsd_t *ts) 386{ 387 int result; 388 struct sockaddr_in addr; 389 int opt = 1; 390 socklen_t size; 391 392 if (ts->ts_once++ == 0) { 393 if (prepare_localtcp_once(ts) == -1) { 394 return (-1); 395 } 396 } 397 398 ts->ts_out = socket(AF_INET, SOCK_STREAM, 0); 399 if (ts->ts_out == -1) { 400 return (-1); 401 } 402 403 if (fcntl(ts->ts_out, F_SETFL, O_NDELAY) == -1) { 404 return (-1); 405 } 406 407 result = connect(ts->ts_out, (struct sockaddr *)&ts->ts_add, 408 sizeof (struct sockaddr_in)); 409 if ((result == -1) && (errno != EINPROGRESS)) { 410 return (-1); 411 } 412 413 if (fcntl(ts->ts_out, F_SETFL, 0) == -1) { 414 return (-1); 415 } 416 417 size = sizeof (struct sockaddr); 418 result = accept(ts->ts_lsn, (struct sockaddr *)&addr, &size); 419 if (result == -1) { 420 return (-1); 421 } 422 ts->ts_out2 = result; 423 424 if (setsockopt(ts->ts_out, IPPROTO_TCP, TCP_NODELAY, 425 &opt, sizeof (int)) == -1) { 426 return (-1); 427 } 428 429 if (setsockopt(ts->ts_out2, IPPROTO_TCP, TCP_NODELAY, 430 &opt, sizeof (int)) == -1) { 431 return (-1); 432 } 433 434 if (optm == MD_SINGLE) { 435 ts->ts_in = ts->ts_out2; 436 } else { 437 ts->ts_in = ts->ts_out; 438 ts->ts_in2 = ts->ts_out2; 439 } 440 441 return (0); 442} 443 444int 445prepare_socketpair(tsd_t *ts) 446{ 447 int s[2]; 448 449 if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) == -1) { 450 return (-1); 451 } 452 453 if (optm == MD_SINGLE) { 454 ts->ts_in = s[0]; 455 ts->ts_out = s[1]; 456 } else { 457 ts->ts_in = s[0]; 458 ts->ts_out = s[0]; 459 ts->ts_in2 = s[1]; 460 ts->ts_out2 = s[1]; 461 } 462 463 return (0); 464} 465 466int 467prepare_fifos(tsd_t *ts) 468{ 469 char path[64]; 470 471 (void) sprintf(path, "/tmp/pipe_%ld.%dA", 472 getpid(), pthread_self()); 473 if (mknod(path, 0600, S_IFIFO) == -1) { 474 return (-1); 475 } 476 477 if (optm == MD_SINGLE) { 478 ts->ts_in = open(path, O_RDONLY); 479 ts->ts_out = open(path, O_WRONLY); 480 } else { 481 ts->ts_in = open(path, O_RDONLY); 482 ts->ts_out2 = open(path, O_WRONLY); 483 484 (void) sprintf(path, "/tmp/pipe_%ld.%dB", 485 getpid(), pthread_self()); 486 if (mknod(path, 0600, S_IFIFO) == -1) { 487 return (-1); 488 } 489 490 ts->ts_in2 = open(path, O_RDONLY); 491 ts->ts_out = open(path, O_WRONLY); 492 } 493 494 return (0); 495} 496 497/*ARGSUSED*/ 498int 499cleanup_fifos(tsd_t *ts) 500{ 501 char path[64]; 502 503 (void) sprintf(path, "/tmp/pipe_%ld.%dA", getpid(), pthread_self()); 504 (void) unlink(path); 505 (void) sprintf(path, "/tmp/pipe_%ld.%dB", getpid(), pthread_self()); 506 (void) unlink(path); 507 508 return (0); 509} 510 511int 512prepare_pipes(tsd_t *ts) 513{ 514 int p[2]; 515 516 if (optm == MD_SINGLE) { 517 if (pipe(p) == -1) { 518 return (-1); 519 } 520 ts->ts_in = p[0]; 521 ts->ts_out = p[1]; 522 523 } else { 524 if (pipe(p) == -1) { 525 return (-1); 526 } 527 ts->ts_in = p[0]; 528 ts->ts_out2 = p[1]; 529 530 if (pipe(p) == -1) { 531 return (-1); 532 } 533 ts->ts_in2 = p[0]; 534 ts->ts_out = p[1]; 535 } 536 537 return (0); 538} 539 540char * 541lookupa(int x, char *names[]) 542{ 543 int i = 0; 544 545 while (names[i] != NULL) { 546 if (x == i) { 547 return (names[i]); 548 } 549 i++; 550 } 551 return (NULL); 552} 553 554int 555lookup(char *x, char *names[]) 556{ 557 int i = 0; 558 559 while (names[i] != NULL) { 560 if (strcmp(names[i], x) == 0) { 561 return (i); 562 } 563 i++; 564 } 565 return (-1); 566} 567