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