Home | History | Annotate | Download | only in libmicro
      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 
     50 typedef 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 
     64 static char			*modes[] = {"st", "mt", "mp", NULL};
     65 #define	MD_SINGLE		0
     66 #define	MD_MULTITHREAD		1
     67 #define	MD_MULTIPROCESS		2
     68 
     69 static 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 
     80 static int			optm = DEFM;
     81 static size_t			opts = DEFS;
     82 static int			optx = DEFX;
     83 static void			*rbuf = NULL;
     84 static void			*wbuf = NULL;
     85 
     86 int readall(int s, void *buf, size_t len);
     87 void *loopback(void *arg);
     88 int prepare_pipes(tsd_t *tsd);
     89 int prepare_fifos(tsd_t *tsd);
     90 int cleanup_fifos(tsd_t *tsd);
     91 int prepare_socketpair(tsd_t *tsd);
     92 int prepare_localtcp(tsd_t *tsd);
     93 int prepare_localtcp_once(tsd_t *tsd);
     94 char *lookupa(int x, char *names[]);
     95 int lookup(char *x, char *names[]);
     96 
     97 int
     98 benchmark_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 
    116 int
    117 benchmark_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 
    143 int
    144 benchmark_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 
    161 int
    162 benchmark_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 
    225 int
    226 benchmark(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 
    249 int
    250 benchmark_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 
    284 char *
    285 benchmark_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 
    295 int
    296 readall(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 
    313 void *
    314 loopback(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 
    335 int
    336 prepare_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 
    384 int
    385 prepare_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 
    444 int
    445 prepare_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 
    466 int
    467 prepare_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*/
    498 int
    499 cleanup_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 
    511 int
    512 prepare_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 
    540 char *
    541 lookupa(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 
    554 int
    555 lookup(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