diff -ruN tools/tools/netrate/netblast.orig/Makefile tools/tools/netrate/netblast/Makefile --- tools/tools/netrate/netblast.orig/Makefile 2013-06-22 01:44:25.035288939 +0200 +++ tools/tools/netrate/netblast/Makefile 2013-06-22 01:44:08.522290181 +0200 @@ -4,5 +4,8 @@ PROG= netblast NO_MAN= +LDFLAGS += -lpthread + +WARNS?= 3 .include diff -ruN tools/tools/netrate/netblast.orig/netblast.c tools/tools/netrate/netblast/netblast.c --- tools/tools/netrate/netblast.orig/netblast.c 2013-06-22 01:44:25.035288939 +0200 +++ tools/tools/netrate/netblast/netblast.c 2013-06-22 01:02:48.242460668 +0200 @@ -32,23 +32,32 @@ #include #include - -#include +#include /* getaddrinfo */ #include #include #include #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 generator 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 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); - } +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; +}; - send_errors = send_calls = 0; - counter = 0; +static void * +blast(void *data) +{ + 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,57 +99,112 @@ * 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, long payloadsize, int family, 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, + printf("start: %zd.%09lu\n", starttime.tv_sec, starttime.tv_nsec); - printf("finish: %zd.%09lu\n", tmptime.tv_sec, + 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("send success: %lu\n", send_calls - send_errors); + printf("approx send rate: %lu\n", (send_calls - send_errors) / duration); - printf("approx error rate: %d\n", (send_errors / send_calls)); - - return (0); + printf("approx error rate: %lu\n", (send_errors / send_calls)); + printf("approx Ethernet throughput: "); + if (family == AF_INET) + printf("%ld Mib/s\n", ((send_calls - send_errors) / duration ) * + (payloadsize + 8 + 20 + 14 ) * 8 / 1000 / 1000); + else if (family == AF_INET6) + printf("%ld Mib/s\n", ((send_calls - send_errors) / duration ) * + (payloadsize + 8 + 40 + 14 ) * 8 / 1000 / 1000); + else printf("CAN 'T DETERMINE family type %i\n",family); + printf("approx payload throughput: %ld Mib/s\n", ((send_calls - send_errors) / + duration ) * payloadsize * 8 / 1000 / 1000); } int main(int argc, char *argv[]) { - long payloadsize, port, duration; - struct sockaddr_in sin; - char *dummy, *packet; - int s; + long payloadsize, duration; + struct addrinfo hints, *res, *res0; + char *dummy; + u_char *packet; + int family, 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(); - bzero(&sin, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - if (inet_aton(argv[1], &sin.sin_addr) == 0) { - perror(argv[1]); - return (-1); - } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; port = strtoul(argv[2], &dummy, 10); - if (port < 1 || port > 65535 || *dummy != '\0') + if (port < 1 || port > 65535 || *dummy != '\0') { + fprintf(stderr, "Invalid port number: %s\n", argv[2]); usage(); - sin.sin_port = htons(port); + /*NOTREACHED*/ + } payloadsize = strtoul(argv[3], &dummy, 10); if (payloadsize < 0 || *dummy != '\0') @@ -168,29 +212,97 @@ if (payloadsize > 32768) { fprintf(stderr, "payloadsize > 32768\n"); return (-1); + /*NOTREACHED*/ } duration = strtoul(argv[4], &dummy, 10); - if (duration < 0 || *dummy != '\0') + if (duration < 0 || *dummy != '\0') { + fprintf(stderr, "Invalid duration time: %s\n", argv[4]); usage(); + /*NOTREACHED*/ + } + + if (argc > 5) + nthreads = strtoul(argv[5], &dummy, 10); + if (nthreads < 1 || nthreads > 64) + usage(); packet = malloc(payloadsize); if (packet == NULL) { perror("malloc"); return (-1); + /*NOTREACHED*/ } + bzero(packet, payloadsize); + error = getaddrinfo(argv[1],argv[2], &hints, &res0); + if (error) { + perror(gai_strerror(error)); + return (-1); + /*NOTREACHED*/ + } + s = -1; + for (res = res0; res; res = res->ai_next) { + s = socket(res->ai_family, res->ai_socktype, 0); + if (s < 0) { + cause = "socket"; + continue; + } + + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + cause = "connect"; + close(s); + s = -1; + continue; + } - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s == -1) { - perror("socket"); + break; /* okay we got one */ + } + if (s < 0) { + perror(cause); return (-1); + /*NOTREACHED*/ } + family=res->ai_family; + freeaddrinfo(res0); + + 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 (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - perror("connect"); + if (signal(SIGALRM, signal_handler) == SIG_ERR) { + perror("signal"); return (-1); } - return (blast_loop(s, duration, packet, payloadsize)); + 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, payloadsize, family, nthreads); + }