From owner-freebsd-bugs@FreeBSD.ORG Wed May 29 20:20:00 2013 Return-Path: Delivered-To: freebsd-bugs@smarthost.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id B78E74C3 for ; Wed, 29 May 2013 20:20:00 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) by mx1.freebsd.org (Postfix) with ESMTP id 9E1166DB for ; Wed, 29 May 2013 20:20:00 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.7/8.14.7) with ESMTP id r4TKK0aq023281 for ; Wed, 29 May 2013 20:20:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.7/8.14.7/Submit) id r4TKK0Od023280; Wed, 29 May 2013 20:20:00 GMT (envelope-from gnats) Resent-Date: Wed, 29 May 2013 20:20:00 GMT Resent-Message-Id: <201305292020.r4TKK0Od023280@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Olivier Cochard-Labbe Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id C4F88402 for ; Wed, 29 May 2013 20:17:08 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from oldred.FreeBSD.org (oldred.freebsd.org [8.8.178.121]) by mx1.freebsd.org (Postfix) with ESMTP id B6C3F6B9 for ; Wed, 29 May 2013 20:17:08 +0000 (UTC) Received: from oldred.FreeBSD.org ([127.0.1.6]) by oldred.FreeBSD.org (8.14.5/8.14.5) with ESMTP id r4TKH7wC003794 for ; Wed, 29 May 2013 20:17:07 GMT (envelope-from nobody@oldred.FreeBSD.org) Received: (from nobody@localhost) by oldred.FreeBSD.org (8.14.5/8.14.5/Submit) id r4TKH7G0003793; Wed, 29 May 2013 20:17:07 GMT (envelope-from nobody) Message-Id: <201305292017.r4TKH7G0003793@oldred.FreeBSD.org> Date: Wed, 29 May 2013 20:17:07 GMT From: Olivier Cochard-Labbe To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: bin/179085: pthread patch for netblast X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 29 May 2013 20:20:00 -0000 >Number: 179085 >Category: bin >Synopsis: pthread patch for netblast >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed May 29 20:20:00 UTC 2013 >Closed-Date: >Last-Modified: >Originator: Olivier Cochard-Labbe >Release: current >Organization: BSD Router Project >Environment: FreeBSD laptop.bsdrp.net 10.0-CURRENT FreeBSD 10.0-CURRENT #2 r250925: Thu May 23 21:49:14 CEST 2013 root@laptop.bsdrp.net:/usr/obj/usr/local/BSDRP/BSDRPcur/FreeBSD/src/sys/LAPTOP amd64 >Description: Here is a patch that add multithread support to tools/tools/netrate/netblast (I've used the netreceive code). WARNING: It need full review because it's my first try with pthread. >How-To-Repeat: >Fix: Patch attached with submission follows: Index: tools/tools/netrate/netblast/Makefile =================================================================== --- tools/tools/netrate/netblast/Makefile (revision 250926) +++ tools/tools/netrate/netblast/Makefile (working copy) @@ -4,5 +4,8 @@ PROG= netblast NO_MAN= +LDFLAGS += -lpthread +WARNS?= 3 + .include Index: tools/tools/netrate/netblast/netblast.c =================================================================== --- tools/tools/netrate/netblast/netblast.c (revision 250926) +++ tools/tools/netrate/netblast/netblast.c (working copy) @@ -40,15 +40,24 @@ #include #include /* close */ +#include +#include +#include /* clock_getres() */ + +static int round_to(int n, int l) +{ + return ((n + l - 1)/l)*l; +} + static void usage(void) { - fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration]\n"); + fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration] [nthreads]\n"); exit(-1); } -static int global_stop_flag; +static int global_stop_flag=0; static void signal_handler(int signum __unused) @@ -57,48 +66,28 @@ global_stop_flag = 1; } + /* - * Loop that blasts packets: begin by recording time information, resetting - * stats. Set the interval timer for when we want to wake up. Then go. - * SIGALRM will set a flag indicating it's time to stop. Note that there's - * some overhead to the signal and timer setup, so the smaller the duration, - * the higher the relative overhead. + * Each socket uses multiple threads so the receiver is + * more efficient. A collector thread runs the stats. */ -static int -blast_loop(int s, long duration, u_char *packet, u_int packet_len) +struct td_desc { + pthread_t td_id; + uint64_t counter; /* tx counter */ + uint64_t send_errors; /* tx send errors */ + uint64_t send_calls; /* tx send calls */ + int s; + u_char *packet; + u_int packet_len; +}; + +static void * +blast(void *data) { - struct timespec starttime, tmptime; - struct itimerval it; - u_int32_t counter; - int send_errors, send_calls; - - if (signal(SIGALRM, signal_handler) == SIG_ERR) { - perror("signal"); - return (-1); - } - - if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { - perror("clock_getres"); - return (-1); - } - - if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { - perror("clock_gettime"); - return (-1); - } - - it.it_interval.tv_sec = 0; - it.it_interval.tv_usec = 0; - it.it_value.tv_sec = duration; - it.it_value.tv_usec = 0; - - if (setitimer(ITIMER_REAL, &it, NULL) < 0) { - perror("setitimer"); - return (-1); - } - - send_errors = send_calls = 0; - counter = 0; + struct td_desc *t = data; + t->counter=0; + t->send_errors=0; + t->send_calls=0; while (global_stop_flag == 0) { /* * We maintain and, if there's room, send a counter. Note @@ -110,32 +99,74 @@ * operation, causing the current sequence number also to be * skipped. */ - if (packet_len >= 4) { - be32enc(packet, counter); - counter++; + if (t->packet_len >= 4) { + be32enc(t->packet, t->counter); + t->counter++; } - if (send(s, packet, packet_len, 0) < 0) - send_errors++; - send_calls++; + if (send(t->s, t->packet, t->packet_len, 0) < 0) + t->send_errors++; + t->send_calls++; } + return NULL; +} +static struct td_desc ** +make_threads(int s, u_char *packet, u_int packet_len, int nthreads) +{ + int i; + int lb = round_to(nthreads * sizeof (struct td_desc *), 64); + int td_len = round_to(sizeof(struct td_desc), 64); // cache align + char *m = calloc(1, lb + td_len * nthreads); + struct td_desc **tp; + + /* pointers plus the structs */ + if (m == NULL) { + perror("no room for pointers!"); + exit(1); + } + tp = (struct td_desc **)m; + m += lb; /* skip the pointers */ + for (i = 0; i < nthreads; i++, m += td_len) { + tp[i] = (struct td_desc *)m; + tp[i]->s = s; + tp[i]->packet = packet; + tp[i]->packet_len = packet_len; + if (pthread_create(&tp[i]->td_id, NULL, blast, tp[i])) { + perror("unable to create thread"); + exit(1); + } + } + return tp; +} + + +static void +main_thread(struct td_desc **tp, long duration, struct timespec starttime, struct timespec tmptime, int nthreads) +{ + uint64_t send_errors=0, send_calls=0; + int i; if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { perror("clock_gettime"); - return (-1); } + for (i = 0; i < nthreads; i++) { + /* Wait for thread end */ + pthread_join( tp[i]->td_id, NULL); + send_calls+=tp[i]->send_calls; + send_errors+=tp[i]->send_errors; + } + printf("\n"); printf("start: %zd.%09lu\n", starttime.tv_sec, starttime.tv_nsec); printf("finish: %zd.%09lu\n", tmptime.tv_sec, tmptime.tv_nsec); - printf("send calls: %d\n", send_calls); - printf("send errors: %d\n", send_errors); - printf("approx send rate: %ld\n", (send_calls - send_errors) / + printf("send calls: %lu\n", send_calls); + printf("send errors: %lu\n", send_errors); + printf("approx send rate: %lu\n", (send_calls - send_errors) / duration); - printf("approx error rate: %d\n", (send_errors / send_calls)); + printf("approx error rate: %lu\n", (send_errors / send_calls)); - return (0); } int @@ -143,11 +174,15 @@ { long payloadsize, duration; struct addrinfo hints, *res, *res0; - char *dummy, *packet; - int port, s, error; + char *dummy; + u_char *packet; + int port, s, error, nthreads = 1; + struct td_desc **tp; const char *cause = NULL; + struct timespec starttime, tmptime; + struct itimerval it; - if (argc != 5) + if (argc < 5) usage(); memset(&hints, 0, sizeof(hints)); @@ -177,6 +212,11 @@ /*NOTREACHED*/ } + if (argc > 5) + nthreads = strtoul(argv[5], &dummy, 10); + if (nthreads < 1 || nthreads > 64) + usage(); + packet = malloc(payloadsize); if (packet == NULL) { perror("malloc"); @@ -216,6 +256,43 @@ freeaddrinfo(res0); - return (blast_loop(s, duration, packet, payloadsize)); + printf("netblast %d threads sending on UDP port %d\n", + nthreads, (u_short)port); + /* + * Begin by recording time information stats. + * Set the interval timer for when we want to wake up. + * SIGALRM will set a flag indicating it's time to stop. Note that there's + * some overhead to the signal and timer setup, so the smaller the duration, + * the higher the relative overhead. + */ + + if (signal(SIGALRM, signal_handler) == SIG_ERR) { + perror("signal"); + return (-1); + } + + if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { + perror("clock_getres"); + return (-1); + } + + if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { + perror("clock_gettime"); + return (-1); + } + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = duration; + it.it_value.tv_usec = 0; + + if (setitimer(ITIMER_REAL, &it, NULL) < 0) { + perror("setitimer"); + return (-1); + } + + tp = make_threads(s, packet, payloadsize, nthreads); + main_thread(tp, duration, starttime, tmptime, nthreads); + } >Release-Note: >Audit-Trail: >Unformatted: