/* * bw_tcp.c - simple TCP bandwidth test * * Three programs in one - * server usage: bw_tcp -s * client usage: bw_tcp [-m ] [-M ] [-P ] [-W ] [-N ] hostname * shutdown: bw_tcp -hostname * * Copyright (c) 2000 Carl Staelin. * Copyright (c) 1994 Larry McVoy. Distributed under the FSF GPL with * additional restriction that results may published only if * (1) the benchmark is unmodified, and * (2) the version in the sccsid below is included in the report. * Support for this development by Sun Microsystems is gratefully acknowledged. */ char *id = "$Id$\n"; #include "bench.h" typedef struct _state { int sock; uint64 move; size_t msize; char *server; int fd; char *buf; } state_t; void server_main(); void client_main(int parallel, state_t *state); void source(int data); void initialize(iter_t iterations, void* cookie); void loop_transfer(iter_t iterations, void *cookie); void cleanup(iter_t iterations, void* cookie); int main(int ac, char **av) { int parallel = 1; int warmup = LONGER; int repetitions = -1; int shutdown = 0; state_t state; char *usage = "-s\n OR [-m ] [-M ] [-P ] [-W ] [-N ] server\n OR -S serverhost\n"; int c; state.msize = 0; state.move = 0; /* Rest is client argument processing */ while (( c = getopt(ac, av, "sS:m:M:P:W:N:")) != EOF) { switch(c) { case 's': /* Server */ if (fork() == 0) { server_main(); } exit(0); break; case 'S': /* shutdown serverhost */ { int conn; conn = tcp_connect(optarg, TCP_DATA, SOCKOPT_NONE); write(conn, "0", 1); exit(0); } case 'm': state.msize = bytes(optarg); break; case 'M': state.move = bytes(optarg); break; case 'P': parallel = atoi(optarg); if (parallel <= 0) lmbench_usage(ac, av, usage); break; case 'W': warmup = atoi(optarg); break; case 'N': repetitions = atoi(optarg); break; default: lmbench_usage(ac, av, usage); break; } } if (optind < ac - 2 || optind >= ac) { lmbench_usage(ac, av, usage); } state.server = av[optind++]; if (state.msize == 0 && state.move == 0) { state.msize = state.move = XFERSIZE; } else if (state.msize == 0) { state.msize = state.move; } else if (state.move == 0) { state.move = state.msize; } /* make the number of bytes to move a multiple of the message size */ if (state.move % state.msize) { state.move += state.msize - state.move % state.msize; } /* * Default is to warmup the connection for seven seconds, * then measure performance over each timing interval. * This minimizes the effect of opening and initializing TCP * connections. */ benchmp(initialize, loop_transfer, cleanup, 0, parallel, warmup, repetitions, &state); if (gettime() > 0) { fprintf(stderr, "%.6f ", state.msize / (1000. * 1000.)); mb(state.move * get_n() * parallel); } return(0); } void initialize(iter_t iterations, void *cookie) { char buf[100]; state_t *state = (state_t *) cookie; if (iterations) return; state->buf = valloc(state->msize); if (!state->buf) { perror("valloc"); exit(1); } touch(state->buf, state->msize); state->sock = tcp_connect(state->server, TCP_DATA, SOCKOPT_READ|SOCKOPT_WRITE|SOCKOPT_REUSE); if (state->sock < 0) { perror("socket connection"); exit(1); } sprintf(buf, "%lu", (unsigned long)state->msize); if (write(state->sock, buf, strlen(buf) + 1) != strlen(buf) + 1) { perror("control write"); exit(1); } } void loop_transfer(iter_t iterations, void *cookie) { int c; uint64 todo; state_t *state = (state_t *) cookie; while (iterations-- > 0) { for (todo = state->move; todo > 0; todo -= c) { if ((c = read(state->sock, state->buf, state->msize)) <= 0) { exit(1); } if (c > todo) c = todo; } } } void cleanup(iter_t iterations, void* cookie) { state_t *state = (state_t *) cookie; if (iterations) return; /* close connection */ (void)close(state->sock); } void server_main() { int data, newdata; GO_AWAY; data = tcp_server(TCP_DATA, SOCKOPT_WRITE|SOCKOPT_REUSE); if (data < 0) { perror("server socket creation"); exit(1); } signal(SIGCHLD, sigchld_wait_handler); for ( ;; ) { newdata = tcp_accept(data, SOCKOPT_WRITE); switch (fork()) { case -1: perror("fork"); break; case 0: source(newdata); exit(0); default: close(newdata); break; } } } /* * Read the message size. Keep transferring * data in message-size sized packets until * the socket goes away. */ void source(int data) { size_t m; unsigned long nbytes; char *buf, scratch[100]; /* * read the message size */ bzero(scratch, 100); if (read(data, scratch, 100) <= 0) { perror("control nbytes"); exit(7); } sscanf(scratch, "%lu", &nbytes); m = nbytes; /* * A hack to allow turning off the absorb daemon. */ if (m == 0) { tcp_done(TCP_DATA); kill(getppid(), SIGTERM); exit(0); } buf = valloc(m); if (!buf) { perror("valloc"); exit(1); } bzero(buf, m); /* * Keep sending messages until the connection is closed */ while (write(data, buf, m) == m) { #ifdef TOUCH touch(buf, m); #endif } free(buf); }