Hi, I've rewritten tcpbench to use libevent as suggested before. Code got a little smaller but I couldn't notice any performance changes (tested up to 1500 fds). There is still only one O(n) operation each second: the stats accounting. Turned some variables into globals for the sake of clarity, as I needed to pass more state in libevent callbacks. Apart from that I've added function declarations to match style(9).
I intend to send another patch with udp support, but I do have some question regarding flow control. Netperf and Iperf will let you set the bandwith to be used (manual flow control), I've discarded doing that cause it sounds plain stupid, better let the client write as fast it can and have only the server stats meaning something. I'm considering coding a scaling sliding window, sounds pretty simple but I'm not sure if that would work out. Any ideas ? Index: tcpbench.c =================================================================== RCS file: /cvs/src/usr.bin/tcpbench/tcpbench.c,v retrieving revision 1.18 diff -d -u -p -w -r1.18 tcpbench.c --- tcpbench.c 28 Sep 2010 12:00:35 -0000 1.18 +++ tcpbench.c 14 Oct 2010 03:03:21 -0000 @@ -1,5 +1,6 @@ /* * Copyright (c) 2008 Damien Miller <[email protected]> + * Copyright (c) 2010 Christiano Farina Haesbaert <[email protected]> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +20,7 @@ #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/resource.h> +#include <sys/queue.h> #include <net/route.h> @@ -39,6 +41,7 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#include <event.h> #include <netdb.h> #include <signal.h> #include <err.h> @@ -48,31 +51,60 @@ #include <kvm.h> #include <nlist.h> +#define SO_RDOMAIN 1 #define DEFAULT_PORT "12345" -#define DEFAULT_STATS_INTERVAL 1000 /* ms */ -#define DEFAULT_BUF 256 * 1024 +#define DEFAULT_STATS_INTERVAL 1 /* seconds */ +#define DEFAULT_BUF (256 * 1024) #define MAX_FD 1024 -sig_atomic_t done = 0; -sig_atomic_t proc_slice = 0; - -static u_int rtableid; -static char **kflag; -static size_t Bflag; +static u_int rdomain; static int Sflag; static int rflag; static int sflag; static int vflag; +static kvm_t *kvmh; +static char **kvars; +static u_long ktcbtab; +static char *dummybuf; +static size_t dummybuf_len; + /* stats for a single connection */ struct statctx { + TAILQ_ENTRY(statctx) entry; struct timeval t_start, t_last; unsigned long long bytes; u_long tcbaddr; - char **kvars; - kvm_t *kh; + int fd; + char *buf; + size_t buflen; + struct event ev; }; +static void signal_handler(int, short, void *); +static void saddr_ntop(const struct sockaddr *, socklen_t, char *, size_t); +static void drop_gid(void); +static void set_slice_timer(int); +static void print_header(void); +static void kget(u_long, void *, size_t); +static u_long kfind_tcb(int); +static void kupdate_stats(u_long, struct inpcb *, struct tcpcb *, + struct socket *); +static void list_kvars(void); +static void check_kvar(const char *); +static char ** check_prepare_kvars(char *); +static void stats_prepare(struct statctx *); +static void stats_update(struct statctx *, ssize_t); +static void stats_cleanslice(void); +static void stats_display(unsigned long long, long double, float, + struct statctx *, struct inpcb *, struct tcpcb *, struct socket *); +static void process_slice(int, short, void *); +static void server_handle_sc(int, short, void *); +static void server_accept(int, short, void *); +static nfds_t server_init(struct addrinfo *); +static void client_handle_sc(int, short, void *); +static void client_init(struct addrinfo *, int); + /* * We account the mainstats here, that is the stats * for all connections, all variables starting with slice @@ -85,6 +117,7 @@ static struct { struct timeval t_start; /* when we started counting */ long double peak_mbps; /* peak mbps so far */ int nconns; /* connected clients */ + struct event timer; /* process timer */ } mainstats; /* When adding variables, also add to stats_display() */ @@ -118,18 +151,7 @@ static const char *allowed_kvars[] = { NULL }; -static void -exitsighand(int signo) -{ - done = signo; -} - -static void -alarmhandler(int signo) -{ - proc_slice = 1; - signal(signo, alarmhandler); -} +TAILQ_HEAD(, statctx) sc_queue; static void __dead usage(void) @@ -137,13 +159,32 @@ usage(void) fprintf(stderr, "usage: tcpbench -l\n" " tcpbench [-v] [-B buf] [-k kvars] [-n connections] [-p port]\n" - " [-r rate] [-S space] [-V rtable] hostname\n" + " [-r rate] [-S space] [-V rdomain] hostname\n" " tcpbench -s [-v] [-B buf] [-k kvars] [-p port]\n" - " [-r rate] [-S space] [-V rtable]\n"); + " [-r rate] [-S space] [-V rdomain]\n"); exit(1); } static void +signal_handler(int sig, short event, void *bula) +{ + /* + * signal handler rules don't apply, libevent decouples for us + */ + switch (sig) { + case SIGINT: + case SIGTERM: + case SIGHUP: + warnx("Terminated by signal %d", sig); + exit(0); + break; /* NOTREACHED */ + default: + errx(1, "unexpected signal %d", sig); + break; /* NOTREACHED */ + } +} + +static void saddr_ntop(const struct sockaddr *addr, socklen_t alen, char *buf, size_t len) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; @@ -160,22 +201,33 @@ saddr_ntop(const struct sockaddr *addr, } static void -set_timer(int toggle) +drop_gid(void) { - struct itimerval itv; + gid_t gid; + + gid = getgid(); + if (setresgid(gid, gid, gid) == -1) + err(1, "setresgid"); +} + +static void +set_slice_timer(int on) +{ + struct timeval tv; if (rflag <= 0) return; - if (toggle) { - itv.it_interval.tv_sec = rflag / 1000; - itv.it_interval.tv_usec = (rflag % 1000) * 1000; - itv.it_value = itv.it_interval; + if (on) { + if (evtimer_pending(&mainstats.timer, NULL)) + return; + timerclear(&tv); + tv.tv_sec = rflag; + evtimer_add(&mainstats.timer, &tv); + } else { + if (evtimer_pending(&mainstats.timer, NULL)) + evtimer_del(&mainstats.timer); } - else - bzero(&itv, sizeof(itv)); - - setitimer(ITIMER_REAL, &itv, NULL); } static void @@ -186,21 +238,21 @@ print_header(void) printf("%12s %14s %12s %8s ", "elapsed_ms", "bytes", "mbps", "bwidth"); - for (kv = kflag; kflag != NULL && *kv != NULL; kv++) - printf("%s%s", kv != kflag ? "," : "", *kv); + for (kv = kvars; kvars != NULL && *kv != NULL; kv++) + printf("%s%s", kv != kvars ? "," : "", *kv); printf("\n"); } static void -kget(kvm_t *kh, u_long addr, void *buf, size_t size) +kget(u_long addr, void *buf, size_t size) { - if (kvm_read(kh, addr, buf, size) != (ssize_t)size) - errx(1, "kvm_read: %s", kvm_geterr(kh)); + if (kvm_read(kvmh, addr, buf, size) != (ssize_t)size) + errx(1, "kvm_read: %s", kvm_geterr(kvmh)); } static u_long -kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock) +kfind_tcb(int sock) { struct inpcbtable tcbtab; struct inpcb *head, *next, *prev; @@ -234,7 +286,7 @@ kfind_tcb(kvm_t *kh, u_long ktcbtab, int if (vflag >= 2) fprintf(stderr, "Using PCB table at %lu\n", ktcbtab); retry: - kget(kh, ktcbtab, &tcbtab, sizeof(tcbtab)); + kget(ktcbtab, &tcbtab, sizeof(tcbtab)); prev = head = (struct inpcb *)&CIRCLEQ_FIRST( &((struct inpcbtable *)ktcbtab)->inpt_queue); next = CIRCLEQ_FIRST(&tcbtab.inpt_queue); @@ -244,7 +296,7 @@ retry: while (next != head) { if (vflag >= 2) fprintf(stderr, "Checking PCB %p\n", next); - kget(kh, (u_long)next, &inpcb, sizeof(inpcb)); + kget((u_long)next, &inpcb, sizeof(inpcb)); if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) { if (nretry--) { warnx("pcb prev pointer insane"); @@ -307,7 +359,7 @@ retry: in6->sin6_port != inpcb.inp_fport) continue; } - kget(kh, (u_long)inpcb.inp_ppcb, &tcpcb, sizeof(tcpcb)); + kget((u_long)inpcb.inp_ppcb, &tcpcb, sizeof(tcpcb)); if (tcpcb.t_state != TCPS_ESTABLISHED) { if (vflag >= 2) fprintf(stderr, "Not established\n"); @@ -315,19 +367,19 @@ retry: } if (vflag >= 2) fprintf(stderr, "Found PCB at %p\n", prev); - return (u_long)prev; + return ((u_long)prev); } errx(1, "No matching PCB found"); } static void -kupdate_stats(kvm_t *kh, u_long tcbaddr, - struct inpcb *inpcb, struct tcpcb *tcpcb, struct socket *sockb) +kupdate_stats(u_long tcbaddr, struct inpcb *inpcb, + struct tcpcb *tcpcb, struct socket *sockb) { - kget(kh, tcbaddr, inpcb, sizeof(*inpcb)); - kget(kh, (u_long)inpcb->inp_ppcb, tcpcb, sizeof(*tcpcb)); - kget(kh, (u_long)inpcb->inp_socket, sockb, sizeof(*sockb)); + kget(tcbaddr, inpcb, sizeof(*inpcb)); + kget((u_long)inpcb->inp_ppcb, tcpcb, sizeof(*tcpcb)); + kget((u_long)inpcb->inp_socket, sockb, sizeof(*sockb)); } static void @@ -365,22 +417,26 @@ check_prepare_kvars(char *list) errx(1, "strdup"); ret[n] = NULL; } - return ret; + return (ret); } static void -stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab) +stats_prepare(struct statctx *sc) { if (rflag <= 0) return; - sc->kh = kh; - sc->kvars = kflag; - if (kflag) - sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd); - if (gettimeofday(&sc->t_start, NULL) == -1) - err(1, "gettimeofday"); + sc->buf = dummybuf; + sc->buflen = dummybuf_len; + if (kvars) + sc->tcbaddr = kfind_tcb(sc->fd); sc->t_last = sc->t_start; sc->bytes = 0; + event_set(&sc->ev, sc->fd, EV_READ | EV_PERSIST, + server_handle_sc, sc); + event_add(&sc->ev, NULL); + /* TODO: use clock_gettime() */ + if (gettimeofday(&sc->t_start, NULL) == -1) + err(1, "gettimeofday"); } static void @@ -406,14 +462,14 @@ stats_display(unsigned long long total_e printf("%12llu %14llu %12.3Lf %7.2f%% ", total_elapsed, sc->bytes, mbps, bwperc); - if (sc->kvars != NULL) { - kupdate_stats(sc->kh, sc->tcbaddr, inpcb, tcpcb, + if (kvars != NULL) { + kupdate_stats(sc->tcbaddr, inpcb, tcpcb, sockb); - for (j = 0; sc->kvars[j] != NULL; j++) { + for (j = 0; kvars[j] != NULL; j++) { #define S(a) #a #define P(b, v, f) \ - if (strcmp(sc->kvars[j], S(b.v)) == 0) { \ + if (strcmp(kvars[j], S(b.v)) == 0) { \ printf("%s"f, j > 0 ? "," : "", b->v); \ continue; \ } @@ -451,30 +507,24 @@ stats_display(unsigned long long total_e } static void -mainstats_display(long double slice_mbps, long double avg_mbps) -{ - printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n", - mainstats.nconns, slice_mbps, mainstats.peak_mbps, avg_mbps); -} - -static void -process_slice(struct statctx *sc, size_t nsc) +process_slice(int fd, short event, void *bula) { unsigned long long total_elapsed, since_last; long double mbps, slice_mbps = 0; float bwperc; - nfds_t i; + struct statctx *sc; struct timeval t_cur, t_diff; struct inpcb inpcb; struct tcpcb tcpcb; struct socket sockb; - for (i = 0; i < nsc; i++, sc++) { + TAILQ_FOREACH(sc, &sc_queue, entry) { if (gettimeofday(&t_cur, NULL) == -1) err(1, "gettimeofday"); - if (sc->kvars != NULL) /* process kernel stats */ - kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb, + if (kvars != NULL) /* process kernel stats */ + kupdate_stats(sc->tcbaddr, &inpcb, &tcpcb, &sockb); + timersub(&t_cur, &sc->t_start, &t_diff); total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000; timersub(&t_cur, &sc->t_last, &t_diff); @@ -488,59 +538,99 @@ process_slice(struct statctx *sc, size_t sc->t_last = t_cur; sc->bytes = 0; - } /* process stats for this slice */ if (slice_mbps > mainstats.peak_mbps) mainstats.peak_mbps = slice_mbps; - mainstats_display(slice_mbps, slice_mbps / mainstats.nconns); + printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n", + mainstats.nconns, slice_mbps, mainstats.peak_mbps, + slice_mbps / mainstats.nconns); + stats_cleanslice(); + set_slice_timer(mainstats.nconns > 0); } -static int -handle_connection(struct statctx *sc, int fd, char *buf, size_t buflen) +static void +server_handle_sc(int fd, short event, void *v_sc) { + struct statctx *sc = v_sc; ssize_t n; again: - n = read(fd, buf, buflen); + n = read(sc->fd, sc->buf, sc->buflen); if (n == -1) { if (errno == EINTR) goto again; else if (errno == EWOULDBLOCK) - return 0; - warn("fd %d read error", fd); - - return -1; - } - else if (n == 0) { + return; + warn("fd %d read error", sc->fd); + return; + } else if (n == 0) { if (vflag) - fprintf(stderr, "%8d closed by remote end\n", fd); - close(fd); - return -1; + fprintf(stderr, "%8d closed by remote end\n", sc->fd); + close(sc->fd); + TAILQ_REMOVE(&sc_queue, sc, entry); + free(sc); + mainstats.nconns--; + set_slice_timer(mainstats.nconns > 0); + return; } if (vflag >= 3) fprintf(stderr, "read: %zd bytes\n", n); - stats_update(sc, n); - return 0; +} + +static void +server_accept(int fd, short event, void *bula) +{ + int sock, r; + struct statctx *sc; + struct sockaddr_storage ss; + socklen_t sslen; + char tmp[128]; + + sslen = sizeof(ss); +again: + if ((sock = accept(fd, (struct sockaddr *)&ss, + &sslen)) == -1) { + if (errno == EINTR) + goto again; + warn("accept"); + return; + } + saddr_ntop((struct sockaddr *)&ss, sslen, + tmp, sizeof(tmp)); + if ((r = fcntl(sock, F_GETFL, 0)) == -1) + err(1, "fcntl(F_GETFL)"); + r |= O_NONBLOCK; + if (fcntl(sock, F_SETFL, r) == -1) + err(1, "fcntl(F_SETFL, O_NONBLOCK)"); + /* Alloc client structure and register reading callback */ + if ((sc = calloc(1, sizeof(*sc))) == NULL) + err(1, "calloc"); + sc->fd = sock; + stats_prepare(sc); + event_set(&sc->ev, sc->fd, EV_READ | EV_PERSIST, + server_handle_sc, sc); + event_add(&sc->ev, NULL); + TAILQ_INSERT_TAIL(&sc_queue, sc, entry); + mainstats.nconns++; + set_slice_timer(mainstats.nconns > 0); + if (vflag) + warnx("Accepted connection from %s, fd = %d\n", tmp, sc->fd); } static nfds_t -serverbind(struct pollfd *pfd, nfds_t max_nfds, struct addrinfo *aitop) +server_init(struct addrinfo *aitop) { char tmp[128]; int sock, on = 1; struct addrinfo *ai; + struct event *ev; nfds_t lnfds; lnfds = 0; for (ai = aitop; ai != NULL; ai = ai->ai_next) { - if (lnfds == max_nfds) { - fprintf(stderr, - "maximum number of listening fds reached\n"); - break; - } saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, sizeof(tmp)); if (vflag) fprintf(stderr, "Try to listen on %s\n", tmp); @@ -552,12 +642,12 @@ serverbind(struct pollfd *pfd, nfds_t ma warn("socket"); continue; } - if (rtableid && ai->ai_family == AF_INET) { - if (setsockopt(sock, IPPROTO_IP, SO_RTABLE, - &rtableid, sizeof(rtableid)) == -1) - err(1, "setsockopt SO_RTABLE"); - } else if (rtableid) - warnx("rtable only supported on AF_INET"); + if (rdomain && ai->ai_family == AF_INET) { + if (setsockopt(sock, IPPROTO_IP, SO_RDOMAIN, + &rdomain, sizeof(rdomain)) == -1) + err(1, "setsockopt SO_RDOMAIN"); + } else if (rdomain) + warnx("rdomain only supported on AF_INET"); if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) warn("reuse port"); @@ -582,165 +672,47 @@ serverbind(struct pollfd *pfd, nfds_t ma close(sock); continue; } + if ((ev = calloc(1, sizeof(*ev))) == NULL) + err(1, "calloc"); + event_set(ev, sock, EV_READ | EV_PERSIST, server_accept, NULL); + event_add(ev, NULL); if (vflag >= 3) fprintf(stderr, "listening on fd %d\n", sock); lnfds++; - pfd[lnfds - 1].fd = sock; - pfd[lnfds - 1].events = POLLIN; - } freeaddrinfo(aitop); if (lnfds == 0) errx(1, "No working listen addresses found"); - return lnfds; + return (lnfds); } static void -set_listening(struct pollfd *pfd, nfds_t lfds, int toggle) { - int i; - - for (i = 0; i < (int)lfds; i++) { - if (toggle) - pfd[i].events = POLLIN; - else - pfd[i].events = 0; - } - -} -static void __dead -serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop) +client_handle_sc(int fd, short event, void *v_sc) { - socklen_t sslen; - struct pollfd *pfd; - char tmp[128], *buf; - struct statctx *psc; - struct sockaddr_storage ss; - nfds_t i, nfds, lfds; - size_t nalloc; - int r, sock, client_id; - - sslen = sizeof(ss); - nalloc = 128; - if ((pfd = calloc(sizeof(*pfd), nalloc)) == NULL) - err(1, "calloc"); - if ((psc = calloc(sizeof(*psc), nalloc)) == NULL) - err(1, "calloc"); - if ((buf = malloc(Bflag)) == NULL) - err(1, "malloc"); - lfds = nfds = serverbind(pfd, nalloc - 1, aitop); - if (vflag >= 3) - fprintf(stderr, "listening on %d fds\n", lfds); - if (setpgid(0, 0) == -1) - err(1, "setpgid"); - - print_header(); - - client_id = 0; - while (!done) { - if (proc_slice) { - process_slice(psc + lfds, nfds - lfds); - stats_cleanslice(); - proc_slice = 0; - } - if (vflag >= 3) - fprintf(stderr, "mainstats.nconns = %u\n", - mainstats.nconns); - if ((r = poll(pfd, nfds, INFTIM)) == -1) { - if (errno == EINTR) - continue; - warn("poll"); - break; - } - - if (vflag >= 3) - fprintf(stderr, "poll: %d\n", r); - for (i = 0 ; r > 0 && i < nfds; i++) { - if ((pfd[i].revents & POLLIN) == 0) - continue; - if (pfd[i].fd == -1) - errx(1, "pfd insane"); - r--; - if (vflag >= 3) - fprintf(stderr, "fd %d active i = %d\n", - pfd[i].fd, i); - /* new connection */ - if (i < lfds) { - if ((sock = accept(pfd[i].fd, - (struct sockaddr *)&ss, - &sslen)) == -1) { - if (errno == EINTR) - continue; - else if (errno == EMFILE || - errno == ENFILE) - set_listening(pfd, lfds, 0); - warn("accept"); - continue; - } - if ((r = fcntl(sock, F_GETFL, 0)) == -1) - err(1, "fcntl(F_GETFL)"); - r |= O_NONBLOCK; - if (fcntl(sock, F_SETFL, r) == -1) - err(1, "fcntl(F_SETFL, O_NONBLOCK)"); - saddr_ntop((struct sockaddr *)&ss, sslen, - tmp, sizeof(tmp)); - if (vflag) - fprintf(stderr, - "Accepted connection %d from " - "%s, fd = %d\n", client_id++, tmp, - sock); - /* alloc more space if we're full */ - if (nfds == nalloc) { - nalloc *= 2; - if ((pfd = realloc(pfd, - sizeof(*pfd) * nalloc)) == NULL) - err(1, "realloc"); - if ((psc = realloc(psc, - sizeof(*psc) * nalloc)) == NULL) - err(1, "realloc"); + struct statctx *sc = v_sc; + ssize_t n; +again: + if ((n = write(sc->fd, sc->buf, sc->buflen)) == -1) { + if (errno == EINTR || errno == EAGAIN) + goto again; + err(1, "write"); } - pfd[nfds].fd = sock; - pfd[nfds].events = POLLIN; - stats_prepare(&psc[nfds], sock, kvmh, ktcbtab); - nfds++; - if (!mainstats.nconns++) - set_timer(1); - continue; + if (n == 0) { + warnx("Remote end closed connection"); + exit(1); } - /* event in fd */ if (vflag >= 3) - fprintf(stderr, - "fd %d active", pfd[i].fd); - while (handle_connection(&psc[i], pfd[i].fd, - buf, Bflag) == -1) { - pfd[i] = pfd[nfds - 1]; - pfd[nfds - 1].fd = -1; - psc[i] = psc[nfds - 1]; - mainstats.nconns--; - nfds--; - /* stop display if no clients */ - if (!mainstats.nconns) { - proc_slice = 1; - set_timer(0); - } - /* if we were full */ - set_listening(pfd, lfds, 1); - - /* is there an event pending on the last fd? */ - if (pfd[i].fd == -1 || - (pfd[i].revents & POLLIN) == 0) - break; - } - } - } - exit(1); + warnx("write: %zd bytes\n", n); + stats_update(sc, n); } -void -clientconnect(struct addrinfo *aitop, struct pollfd *pfd, int nconn) +static void +client_init(struct addrinfo *aitop, int nconn) { - char tmp[128]; + struct statctx *sc; struct addrinfo *ai; + char tmp[128]; int i, r, sock; for (i = 0; i < nconn; i++) { @@ -757,12 +729,12 @@ clientconnect(struct addrinfo *aitop, st warn("socket"); continue; } - if (rtableid && ai->ai_family == AF_INET) { - if (setsockopt(sock, IPPROTO_IP, SO_RTABLE, - &rtableid, sizeof(rtableid)) == -1) - err(1, "setsockopt SO_RTABLE"); - } else if (rtableid) - warnx("rtable only supported on AF_INET"); + if (rdomain && ai->ai_family == AF_INET) { + if (setsockopt(sock, IPPROTO_IP, SO_RDOMAIN, + &rdomain, sizeof(rdomain)) == -1) + err(1, "setsockopt SO_RDOMAIN"); + } else if (rdomain) + warnx("rdomain only supported on AF_INET"); if (Sflag) { if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &Sflag, sizeof(Sflag)) == -1) @@ -781,15 +753,22 @@ clientconnect(struct addrinfo *aitop, st } if (sock == -1) errx(1, "No host found"); - if ((r = fcntl(sock, F_GETFL, 0)) == -1) err(1, "fcntl(F_GETFL)"); r |= O_NONBLOCK; if (fcntl(sock, F_SETFL, r) == -1) err(1, "fcntl(F_SETFL, O_NONBLOCK)"); - - pfd[i].fd = sock; - pfd[i].events = POLLOUT; + /* Alloc and prepare stats */ + if ((sc = calloc(1, sizeof(*sc))) == NULL) + err(1, "calloc"); + sc->fd = sock; + stats_prepare(sc); + event_set(&sc->ev, sc->fd, EV_WRITE | EV_PERSIST, + client_handle_sc, sc); + event_add(&sc->ev, NULL); + TAILQ_INSERT_TAIL(&sc_queue, sc, entry); + mainstats.nconns++; + set_slice_timer(mainstats.nconns > 0); } freeaddrinfo(aitop); @@ -797,102 +776,26 @@ clientconnect(struct addrinfo *aitop, st fprintf(stderr, "%u connections established\n", nconn); } -static void __dead -clientloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop, int nconn) -{ - struct statctx *psc; - struct pollfd *pfd; - char *buf; - int i; - ssize_t n; - - if ((pfd = calloc(nconn, sizeof(*pfd))) == NULL) - err(1, "clientloop pfd calloc"); - if ((psc = calloc(nconn, sizeof(*psc))) == NULL) - err(1, "clientloop psc calloc"); - - clientconnect(aitop, pfd, nconn); - - for (i = 0; i < nconn; i++) { - stats_prepare(psc + i, pfd[i].fd, kvmh, ktcbtab); - mainstats.nconns++; - } - - if ((buf = malloc(Bflag)) == NULL) - err(1, "malloc"); - arc4random_buf(buf, Bflag); - - print_header(); - set_timer(1); - - while (!done) { - if (proc_slice) { - process_slice(psc, nconn); - stats_cleanslice(); - proc_slice = 0; - } - if (poll(pfd, nconn, INFTIM) == -1) { - if (errno == EINTR) - continue; - err(1, "poll"); - } - for (i = 0; i < nconn; i++) { - if (pfd[i].revents & POLLOUT) { - if ((n = write(pfd[i].fd, buf, Bflag)) == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - err(1, "write"); - } - if (n == 0) { - warnx("Remote end closed connection"); - done = -1; - break; - } - if (vflag >= 3) - fprintf(stderr, "write: %zd bytes\n", - n); - stats_update(psc + i, n); - } - } - } - - if (done > 0) - warnx("Terminated by signal %d", done); - - free(buf); - exit(0); -} - -static void -drop_gid(void) -{ - gid_t gid; - - gid = getgid(); - if (setresgid(gid, gid, gid) == -1) - err(1, "setresgid"); -} - int main(int argc, char **argv) { extern int optind; extern char *optarg; - char kerr[_POSIX2_LINE_MAX], *tmp; struct addrinfo *aitop, hints; const char *errstr; - kvm_t *kvmh = NULL; struct rlimit rl; - int ch, herr; + int ch, herr, nconn; struct nlist nl[] = { { "_tcbtable" }, { "" } }; const char *host = NULL, *port = DEFAULT_PORT; - int nconn = 1; + struct event ev_sigint, ev_sigterm, ev_sighup; - Bflag = DEFAULT_BUF; - Sflag = sflag = vflag = rtableid = 0; - kflag = NULL; + dummybuf_len = DEFAULT_BUF; + Sflag = sflag = vflag = rdomain = 0; + kvmh = NULL; + kvars = NULL; rflag = DEFAULT_STATS_INTERVAL; + nconn = 1; while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:vV:")) != -1) { switch (ch) { @@ -902,7 +805,7 @@ main(int argc, char **argv) case 'k': if ((tmp = strdup(optarg)) == NULL) errx(1, "strdup"); - kflag = check_prepare_kvars(tmp); + kvars = check_prepare_kvars(tmp); free(tmp); break; case 'r': @@ -926,7 +829,7 @@ main(int argc, char **argv) errstr, optarg); break; case 'B': - Bflag = strtonum(optarg, 0, 1024*1024*1024, + dummybuf_len = strtonum(optarg, 0, 1024*1024*1024, &errstr); if (errstr != NULL) errx(1, "read/write buffer size is %s: %s", @@ -936,10 +839,10 @@ main(int argc, char **argv) vflag++; break; case 'V': - rtableid = (unsigned int)strtonum(optarg, 0, + rdomain = (unsigned int)strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); if (errstr) - errx(1, "rtable value is %s: %s", + errx(1, "rdomain value is %s: %s", errstr, optarg); break; case 'n': @@ -972,23 +875,17 @@ main(int argc, char **argv) else errx(1, "getaddrinfo: %s", gai_strerror(herr)); } - - if (kflag) { + if (kvars) { if ((kvmh = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, kerr)) == NULL) errx(1, "kvm_open: %s", kerr); drop_gid(); if (kvm_nlist(kvmh, nl) < 0 || nl[0].n_type == 0) errx(1, "kvm: no namelist"); + ktcbtab = nl[0].n_value; } else drop_gid(); - signal(SIGINT, exitsighand); - signal(SIGTERM, exitsighand); - signal(SIGHUP, exitsighand); - signal(SIGPIPE, SIG_IGN); - signal(SIGALRM, alarmhandler); - if (getrlimit(RLIMIT_NOFILE, &rl) == -1) err(1, "getrlimit"); if (rl.rlim_cur < MAX_FD) @@ -998,10 +895,36 @@ main(int argc, char **argv) if (getrlimit(RLIMIT_NOFILE, &rl) == -1) err(1, "getrlimit"); - if (sflag) - serverloop(kvmh, nl[0].n_value, aitop); - else - clientloop(kvmh, nl[0].n_value, aitop, nconn); + /* Init world */ + TAILQ_INIT(&sc_queue); + if ((dummybuf = malloc(dummybuf_len)) == NULL) + err(1, "malloc"); + arc4random_buf(dummybuf, dummybuf_len); - return 0; + /* Setup libevent and signals */ + event_init(); + signal_set(&ev_sigterm, SIGTERM, signal_handler, NULL); + signal_set(&ev_sighup, SIGHUP, signal_handler, NULL); + signal_set(&ev_sigint, SIGINT, signal_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sighup, NULL); + signal(SIGPIPE, SIG_IGN); + + print_header(); + + /* Slice stats timer */ + evtimer_set(&mainstats.timer, process_slice, NULL); + + if (sflag) { + (void)server_init(aitop); + if (setpgid(0, 0) == -1) + err(1, "setpgid"); + } else + client_init(aitop, nconn); + + /* libevent main loop*/ + event_dispatch(); + + return (0); } Index: Makefile =================================================================== RCS file: /cvs/src/usr.bin/tcpbench/Makefile,v retrieving revision 1.3 diff -d -u -p -w -r1.3 Makefile --- Makefile 26 Jun 2008 07:05:56 -0000 1.3 +++ Makefile 14 Oct 2010 03:03:21 -0000 @@ -15,7 +15,7 @@ CDIAGFLAGS+= -Wshadow PROG=tcpbench -LDADD=-lkvm +LDADD=-lkvm -levent #BINGRP= kmem #BINMODE=2555
