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 168 switch (optx) { 169 case XP_SOCKETPAIR: 170 result = prepare_socketpair(ts); 171 break; 172 case XP_LOCALTCP: 173 result = prepare_localtcp(ts); 174 break; 175 case XP_FIFOS: 176 result = prepare_fifos(ts); 177 break; 178 case XP_PIPES: 179 default: 180 result = prepare_pipes(ts); 181 break; 182 } 183 if (result == -1) { 184 return (1); 185 } 186 187 switch (optm) { 188 case MD_MULTITHREAD: 189 result = pthread_create(&ts->ts_thread, NULL, loopback, tsd); 190 if (result == -1) { 191 return (1); 192 } 193 break; 194 case MD_MULTIPROCESS: 195 pid = fork(); 196 switch (pid) { 197 case 0: 198 (void) loopback(tsd); 199 exit(0); 200 break; 201 case -1: 202 return (-1); 203 default: 204 ts->ts_child = pid; 205 break; 206 } 207 break; 208 case MD_SINGLE: 209 default: 210 break; 211 } 212 213 /* Prime the loopback */ 214 if (write(ts->ts_out, wbuf, opts) != opts) { 215 return (1); 216 } 217 if (readall(ts->ts_in, rbuf, opts) != opts) { 218 return (1); 219 } 220 221 return (0); 222} 223 224int 225benchmark(void *tsd, result_t *res) 226{ 227 tsd_t *ts = (tsd_t *)tsd; 228 int i; 229 int n; 230 231 for (i = 0; i < lm_optB; i++) { 232 if (write(ts->ts_out, wbuf, opts) != opts) { 233 res->re_errors++; 234 continue; 235 } 236 237 n = readall(ts->ts_in, rbuf, opts); 238 if (n == -1) { 239 res->re_errors++; 240 continue; 241 } 242 } 243 res->re_count = i; 244 245 return (0); 246} 247 248int 249benchmark_finibatch(void *tsd) 250{ 251 tsd_t *ts = (tsd_t *)tsd; 252 253 /* Terminate the loopback */ 254 (void) write(ts->ts_out, wbuf, opts); 255 (void) readall(ts->ts_in, rbuf, opts); 256 257 switch (optm) { 258 case MD_MULTITHREAD: 259 (void) close(ts->ts_in2); 260 (void) close(ts->ts_out2); 261 (void) pthread_join(ts->ts_thread, NULL); 262 break; 263 case MD_MULTIPROCESS: 264 (void) close(ts->ts_in2); 265 (void) close(ts->ts_out2); 266 (void) waitpid(ts->ts_child, NULL, 0); 267 break; 268 case MD_SINGLE: 269 default: 270 break; 271 } 272 273 (void) close(ts->ts_in); 274 (void) close(ts->ts_out); 275 276 if (optx == XP_FIFOS) { 277 (void) cleanup_fifos(ts); 278 } 279 280 return (0); 281} 282 283char * 284benchmark_result() 285{ 286 static char result[256]; 287 288 (void) sprintf(result, "%2s %4s", 289 lookupa(optm, modes), lookupa(optx, xports)); 290 291 return (result); 292} 293 294int 295readall(int s, void *buf, size_t len) 296{ 297 size_t n; 298 size_t total = 0; 299 300 for (;;) { 301 n = read(s, (void *)((long)buf + total), len - total); 302 if (n < 1) { 303 return (-1); 304 } 305 total += n; 306 if (total >= len) { 307 return (total); 308 } 309 } 310} 311 312void * 313loopback(void *arg) 314{ 315 tsd_t *ts = (tsd_t *)arg; 316 int i, n, m; 317 318 /* Include priming and termination */ 319 m = lm_optB + 2; 320 321 for (i = 0; i < m; i++) { 322 n = readall(ts->ts_in2, rbuf, opts); 323 if (n == -1) { 324 break; 325 } 326 if (write(ts->ts_out2, wbuf, opts) != opts) { 327 break; 328 } 329 } 330 331 return (NULL); 332} 333 334int 335prepare_localtcp_once(tsd_t *ts) 336{ 337 int j; 338 int opt = 1; 339 struct hostent *host; 340 341 j = FIRSTPORT; 342 343 ts->ts_lsn = socket(AF_INET, SOCK_STREAM, 0); 344 if (ts->ts_lsn == -1) { 345 return (-1); 346 } 347 348 if (setsockopt(ts->ts_lsn, SOL_SOCKET, SO_REUSEADDR, 349 &opt, sizeof (int)) == -1) { 350 return (-1); 351 } 352 353 if ((host = gethostbyname("localhost")) == NULL) { 354 return (-1); 355 } 356 357 for (;;) { 358 (void) memset(&ts->ts_add, 0, 359 sizeof (struct sockaddr_in)); 360 ts->ts_add.sin_family = AF_INET; 361 ts->ts_add.sin_port = htons(j++); 362 (void) memcpy(&ts->ts_add.sin_addr.s_addr, 363 host->h_addr_list[0], sizeof (struct in_addr)); 364 365 if (bind(ts->ts_lsn, 366 (struct sockaddr *)&ts->ts_add, 367 sizeof (struct sockaddr_in)) == 0) { 368 break; 369 } 370 371 if (errno != EADDRINUSE) { 372 return (-1); 373 } 374 } 375 376 if (listen(ts->ts_lsn, 5) == -1) { 377 return (-1); 378 } 379 380 return (0); 381} 382 383int 384prepare_localtcp(tsd_t *ts) 385{ 386 int result; 387 struct sockaddr_in addr; 388 int opt = 1; 389 socklen_t size; 390 391 if (ts->ts_once++ == 0) { 392 if (prepare_localtcp_once(ts) == -1) { 393 return (-1); 394 } 395 } 396 397 ts->ts_out = socket(AF_INET, SOCK_STREAM, 0); 398 if (ts->ts_out == -1) { 399 return (-1); 400 } 401 402 if (fcntl(ts->ts_out, F_SETFL, O_NDELAY) == -1) { 403 return (-1); 404 } 405 406 result = connect(ts->ts_out, (struct sockaddr *)&ts->ts_add, 407 sizeof (struct sockaddr_in)); 408 if ((result == -1) && (errno != EINPROGRESS)) { 409 return (-1); 410 } 411 412 if (fcntl(ts->ts_out, F_SETFL, 0) == -1) { 413 return (-1); 414 } 415 416 size = sizeof (struct sockaddr); 417 result = accept(ts->ts_lsn, (struct sockaddr *)&addr, &size); 418 if (result == -1) { 419 return (-1); 420 } 421 ts->ts_out2 = result; 422 423 if (setsockopt(ts->ts_out, IPPROTO_TCP, TCP_NODELAY, 424 &opt, sizeof (int)) == -1) { 425 return (-1); 426 } 427 428 if (setsockopt(ts->ts_out2, IPPROTO_TCP, TCP_NODELAY, 429 &opt, sizeof (int)) == -1) { 430 return (-1); 431 } 432 433 if (optm == MD_SINGLE) { 434 ts->ts_in = ts->ts_out2; 435 } else { 436 ts->ts_in = ts->ts_out; 437 ts->ts_in2 = ts->ts_out2; 438 } 439 440 return (0); 441} 442 443int 444prepare_socketpair(tsd_t *ts) 445{ 446 int s[2]; 447 448 if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) == -1) { 449 return (-1); 450 } 451 452 if (optm == MD_SINGLE) { 453 ts->ts_in = s[0]; 454 ts->ts_out = s[1]; 455 } else { 456 ts->ts_in = s[0]; 457 ts->ts_out = s[0]; 458 ts->ts_in2 = s[1]; 459 ts->ts_out2 = s[1]; 460 } 461 462 return (0); 463} 464 465int 466prepare_fifos(tsd_t *ts) 467{ 468 char path[64]; 469 470 (void) sprintf(path, "/private/tmp/pipe_%ld.%dA", 471 getpid(), pthread_self()); 472 if (mknod(path, 0600, S_IFIFO) == -1) { 473 return (-1); 474 } 475 476 if (optm == MD_SINGLE) { 477 ts->ts_in = open(path, O_RDONLY); 478 ts->ts_out = open(path, O_WRONLY); 479 } else { 480 ts->ts_in = open(path, O_RDONLY); 481 ts->ts_out2 = open(path, O_WRONLY); 482 483 (void) sprintf(path, "/private/tmp/pipe_%ld.%dB", 484 getpid(), pthread_self()); 485 if (mknod(path, 0600, S_IFIFO) == -1) { 486 return (-1); 487 } 488 489 ts->ts_in2 = open(path, O_RDONLY); 490 ts->ts_out = open(path, O_WRONLY); 491 } 492 493 return (0); 494} 495 496/*ARGSUSED*/ 497int 498cleanup_fifos(tsd_t *ts) 499{ 500 char path[64]; 501 502 (void) sprintf(path, "/private/tmp/pipe_%ld.%dA", getpid(), pthread_self()); 503 (void) unlink(path); 504 (void) sprintf(path, "/private/tmp/pipe_%ld.%dB", getpid(), pthread_self()); 505 (void) unlink(path); 506 507 return (0); 508} 509 510int 511prepare_pipes(tsd_t *ts) 512{ 513 int p[2]; 514 515 if (optm == MD_SINGLE) { 516 if (pipe(p) == -1) { 517 return (-1); 518 } 519 ts->ts_in = p[0]; 520 ts->ts_out = p[1]; 521 522 } else { 523 if (pipe(p) == -1) { 524 return (-1); 525 } 526 ts->ts_in = p[0]; 527 ts->ts_out2 = p[1]; 528 529 if (pipe(p) == -1) { 530 return (-1); 531 } 532 ts->ts_in2 = p[0]; 533 ts->ts_out = p[1]; 534 } 535 536 return (0); 537} 538 539char * 540lookupa(int x, char *names[]) 541{ 542 int i = 0; 543 544 while (names[i] != NULL) { 545 if (x == i) { 546 return (names[i]); 547 } 548 i++; 549 } 550 return (NULL); 551} 552 553int 554lookup(char *x, char *names[]) 555{ 556 int i = 0; 557 558 while (names[i] != NULL) { 559 if (strcmp(names[i], x) == 0) { 560 return (i); 561 } 562 i++; 563 } 564 return (-1); 565} 566