/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the "License"). You may not use this file except
* in compliance with the License.
*
* You can obtain a copy of the license at
* src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* HEADER in each file and include the License file at
* usr/src/OPENSOLARIS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your
* own identifying information: Portions Copyright [yyyy]
* [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "libmicro.h"
typedef struct {
int ts_once;
pid_t ts_child;
pthread_t ts_thread;
int ts_in;
int ts_out;
int ts_in2;
int ts_out2;
int ts_lsn;
struct sockaddr_in ts_add;
} tsd_t;
#define FIRSTPORT 12345
static char *modes[] = {"st", "mt", "mp", NULL};
#define MD_SINGLE 0
#define MD_MULTITHREAD 1
#define MD_MULTIPROCESS 2
static char *xports[] = {"pipe", "fifo", "sock", "tcp",
NULL};
#define XP_PIPES 0
#define XP_FIFOS 1
#define XP_SOCKETPAIR 2
#define XP_LOCALTCP 3
#define DEFM MD_SINGLE
#define DEFS 1024
#define DEFX XP_PIPES
static int optm = DEFM;
static size_t opts = DEFS;
static int optx = DEFX;
static void *rbuf = NULL;
static void *wbuf = NULL;
int readall(int s, void *buf, size_t len);
void *loopback(void *arg);
int prepare_pipes(tsd_t *tsd);
int prepare_fifos(tsd_t *tsd);
int cleanup_fifos(tsd_t *tsd);
int prepare_socketpair(tsd_t *tsd);
int prepare_localtcp(tsd_t *tsd);
int prepare_localtcp_once(tsd_t *tsd);
char *lookupa(int x, char *names[]);
int lookup(char *x, char *names[]);
int
benchmark_init()
{
lm_tsdsize = sizeof (tsd_t);
(void) sprintf(lm_optstr, "m:s:x:");
(void) sprintf(lm_usage,
" [-m mode (st|mt|mp, default %s)]\n"
" [-s buffer-size (default %d)]\n"
" [-x transport (pipe|fifo|sock|tcp, default %s)]\n"
"notes: measures write()/read() across various transports\n",
lookupa(DEFM, modes), DEFS, lookupa(DEFX, xports));
(void) sprintf(lm_header, "%2s %4s", "md", "xprt");
return (0);
}
int
benchmark_optswitch(int opt, char *optarg)
{
int x;
switch (opt) {
case 'm':
x = lookup(optarg, modes);
if (x == -1)
return (-1);
optm = x;
break;
case 's':
opts = sizetoll(optarg);
break;
case 'x':
x = lookup(optarg, xports);
if (x == -1)
return (-1);
optx = x;
break;
default:
return (-1);
}
return (0);
}
int
benchmark_initrun()
{
if (optx == XP_FIFOS) {
if (geteuid() != 0) {
(void) printf("sorry, must be root to create fifos\n");
exit(1);
}
}
(void) setfdlimit(4 * lm_optT + 10);
rbuf = malloc(opts);
wbuf = malloc(opts);
return (0);
}
int
benchmark_initbatch(void *tsd)
{
tsd_t *ts = (tsd_t *)tsd;
int result;
pid_t pid;
int i;
switch (optx) {
case XP_SOCKETPAIR:
result = prepare_socketpair(ts);
break;
case XP_LOCALTCP:
result = prepare_localtcp(ts);
break;
case XP_FIFOS:
result = prepare_fifos(ts);
break;
case XP_PIPES:
default:
result = prepare_pipes(ts);
break;
}
if (result == -1) {
return (1);
}
switch (optm) {
case MD_MULTITHREAD:
result = pthread_create(&ts->ts_thread, NULL, loopback, tsd);
if (result == -1) {
return (1);
}
break;
case MD_MULTIPROCESS:
pid = fork();
switch (pid) {
case 0:
(void) loopback(tsd);
exit(0);
break;
case -1:
return (-1);
default:
ts->ts_child = pid;
break;
}
break;
case MD_SINGLE:
default:
break;
}
/* Prime the loopback */
if (write(ts->ts_out, wbuf, opts) != opts) {
return (1);
}
if (readall(ts->ts_in, rbuf, opts) != opts) {
return (1);
}
return (0);
}
int
benchmark(void *tsd, result_t *res)
{
tsd_t *ts = (tsd_t *)tsd;
int i;
int n;
for (i = 0; i < lm_optB; i++) {
if (write(ts->ts_out, wbuf, opts) != opts) {
res->re_errors++;
continue;
}
n = readall(ts->ts_in, rbuf, opts);
if (n == -1) {
res->re_errors++;
continue;
}
}
res->re_count = i;
return (0);
}
int
benchmark_finibatch(void *tsd)
{
tsd_t *ts = (tsd_t *)tsd;
/* Terminate the loopback */
(void) write(ts->ts_out, wbuf, opts);
(void) readall(ts->ts_in, rbuf, opts);
switch (optm) {
case MD_MULTITHREAD:
(void) close(ts->ts_in2);
(void) close(ts->ts_out2);
(void) pthread_join(ts->ts_thread, NULL);
break;
case MD_MULTIPROCESS:
(void) close(ts->ts_in2);
(void) close(ts->ts_out2);
(void) waitpid(ts->ts_child, NULL, 0);
break;
case MD_SINGLE:
default:
break;
}
(void) close(ts->ts_in);
(void) close(ts->ts_out);
if (optx == XP_FIFOS) {
(void) cleanup_fifos(ts);
}
return (0);
}
char *
benchmark_result()
{
static char result[256];
(void) sprintf(result, "%2s %4s",
lookupa(optm, modes), lookupa(optx, xports));
return (result);
}
int
readall(int s, void *buf, size_t len)
{
size_t n;
size_t total = 0;
for (;;) {
n = read(s, (void *)((long)buf + total), len - total);
if (n < 1) {
return (-1);
}
total += n;
if (total >= len) {
return (total);
}
}
}
void *
loopback(void *arg)
{
tsd_t *ts = (tsd_t *)arg;
int i, n, m;
/* Include priming and termination */
m = lm_optB + 2;
for (i = 0; i < m; i++) {
n = readall(ts->ts_in2, rbuf, opts);
if (n == -1) {
break;
}
if (write(ts->ts_out2, wbuf, opts) != opts) {
break;
}
}
return (NULL);
}
int
prepare_localtcp_once(tsd_t *ts)
{
int j;
int opt = 1;
struct hostent *host;
j = FIRSTPORT;
ts->ts_lsn = socket(AF_INET, SOCK_STREAM, 0);
if (ts->ts_lsn == -1) {
return (-1);
}
if (setsockopt(ts->ts_lsn, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof (int)) == -1) {
return (-1);
}
if ((host = gethostbyname("localhost")) == NULL) {
return (-1);
}
for (;;) {
(void) memset(&ts->ts_add, 0,
sizeof (struct sockaddr_in));
ts->ts_add.sin_family = AF_INET;
ts->ts_add.sin_port = htons(j++);
(void) memcpy(&ts->ts_add.sin_addr.s_addr,
host->h_addr_list[0], sizeof (struct in_addr));
if (bind(ts->ts_lsn,
(struct sockaddr *)&ts->ts_add,
sizeof (struct sockaddr_in)) == 0) {
break;
}
if (errno != EADDRINUSE) {
return (-1);
}
}
if (listen(ts->ts_lsn, 5) == -1) {
return (-1);
}
return (0);
}
int
prepare_localtcp(tsd_t *ts)
{
int result;
struct sockaddr_in addr;
int opt = 1;
socklen_t size;
if (ts->ts_once++ == 0) {
if (prepare_localtcp_once(ts) == -1) {
return (-1);
}
}
ts->ts_out = socket(AF_INET, SOCK_STREAM, 0);
if (ts->ts_out == -1) {
return (-1);
}
if (fcntl(ts->ts_out, F_SETFL, O_NDELAY) == -1) {
return (-1);
}
result = connect(ts->ts_out, (struct sockaddr *)&ts->ts_add,
sizeof (struct sockaddr_in));
if ((result == -1) && (errno != EINPROGRESS)) {
return (-1);
}
if (fcntl(ts->ts_out, F_SETFL, 0) == -1) {
return (-1);
}
size = sizeof (struct sockaddr);
result = accept(ts->ts_lsn, (struct sockaddr *)&addr, &size);
if (result == -1) {
return (-1);
}
ts->ts_out2 = result;
if (setsockopt(ts->ts_out, IPPROTO_TCP, TCP_NODELAY,
&opt, sizeof (int)) == -1) {
return (-1);
}
if (setsockopt(ts->ts_out2, IPPROTO_TCP, TCP_NODELAY,
&opt, sizeof (int)) == -1) {
return (-1);
}
if (optm == MD_SINGLE) {
ts->ts_in = ts->ts_out2;
} else {
ts->ts_in = ts->ts_out;
ts->ts_in2 = ts->ts_out2;
}
return (0);
}
int
prepare_socketpair(tsd_t *ts)
{
int s[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, s) == -1) {
return (-1);
}
if (optm == MD_SINGLE) {
ts->ts_in = s[0];
ts->ts_out = s[1];
} else {
ts->ts_in = s[0];
ts->ts_out = s[0];
ts->ts_in2 = s[1];
ts->ts_out2 = s[1];
}
return (0);
}
int
prepare_fifos(tsd_t *ts)
{
char path[64];
(void) sprintf(path, "/tmp/pipe_%ld.%dA",
getpid(), pthread_self());
if (mknod(path, 0600, S_IFIFO) == -1) {
return (-1);
}
if (optm == MD_SINGLE) {
ts->ts_in = open(path, O_RDONLY);
ts->ts_out = open(path, O_WRONLY);
} else {
ts->ts_in = open(path, O_RDONLY);
ts->ts_out2 = open(path, O_WRONLY);
(void) sprintf(path, "/tmp/pipe_%ld.%dB",
getpid(), pthread_self());
if (mknod(path, 0600, S_IFIFO) == -1) {
return (-1);
}
ts->ts_in2 = open(path, O_RDONLY);
ts->ts_out = open(path, O_WRONLY);
}
return (0);
}
/*ARGSUSED*/
int
cleanup_fifos(tsd_t *ts)
{
char path[64];
(void) sprintf(path, "/tmp/pipe_%ld.%dA", getpid(), pthread_self());
(void) unlink(path);
(void) sprintf(path, "/tmp/pipe_%ld.%dB", getpid(), pthread_self());
(void) unlink(path);
return (0);
}
int
prepare_pipes(tsd_t *ts)
{
int p[2];
if (optm == MD_SINGLE) {
if (pipe(p) == -1) {
return (-1);
}
ts->ts_in = p[0];
ts->ts_out = p[1];
} else {
if (pipe(p) == -1) {
return (-1);
}
ts->ts_in = p[0];
ts->ts_out2 = p[1];
if (pipe(p) == -1) {
return (-1);
}
ts->ts_in2 = p[0];
ts->ts_out = p[1];
}
return (0);
}
char *
lookupa(int x, char *names[])
{
int i = 0;
while (names[i] != NULL) {
if (x == i) {
return (names[i]);
}
i++;
}
return (NULL);
}
int
lookup(char *x, char *names[])
{
int i = 0;
while (names[i] != NULL) {
if (strcmp(names[i], x) == 0) {
return (i);
}
i++;
}
return (-1);
}