2 * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * $Id: pkt-gen.c 10967 2012-05-03 11:29:23Z luigi $
30 * Example program to show how to build a multithreaded packet
31 * source/sink using the netmap device.
33 * In this example we create a programmable number of threads
34 * to take care of all the queues of the interface used to
35 * send or receive traffic.
39 const char *default_payload="netmap pkt-gen Luigi Rizzo and Matteo Landi\n"
40 "http://info.iet.unipi.it/~luigi/netmap/ ";
43 #include <pthread.h> /* pthread_* */
44 #include <pthread_np.h> /* pthread w/ affinity */
45 #include <signal.h> /* signal */
48 #include <inttypes.h> /* PRI* macros */
49 #include <string.h> /* strcmp */
50 #include <fcntl.h> /* open */
51 #include <unistd.h> /* close */
52 #include <ifaddrs.h> /* getifaddrs */
54 #include <sys/mman.h> /* PROT_* */
55 #include <sys/ioctl.h> /* ioctl */
57 #include <sys/socket.h> /* sockaddr.. */
58 #include <arpa/inet.h> /* ntohs */
59 #include <sys/param.h>
60 #include <sys/cpuset.h> /* cpu_set */
61 #include <sys/sysctl.h> /* sysctl */
62 #include <sys/time.h> /* timersub */
64 #include <net/ethernet.h>
65 #include <net/if.h> /* ifreq */
66 #include <net/if_dl.h> /* LLADDR */
68 #include <netinet/in.h>
69 #include <netinet/ip.h>
70 #include <netinet/udp.h>
72 #include <net/netmap.h>
73 #include <net/netmap_user.h>
74 #include <pcap/pcap.h>
77 static inline int min(int a, int b) { return a < b ? a : b; }
80 #define D(format, ...) \
81 fprintf(stderr, "%s [%d] " format "\n", \
82 __FUNCTION__, __LINE__, ##__VA_ARGS__)
85 #define EXPERIMENTAL 0
89 #define MAX_QUEUES 64 /* no need to limit */
91 #define SKIP_PAYLOAD 1 /* do not check payload. */
93 inline void prefetch (const void *x)
95 __asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x));
98 // XXX only for multiples of 32 bytes, non overlapped.
100 pkt_copy(void *_src, void *_dst, int l)
102 uint64_t *src = _src;
103 uint64_t *dst = _dst;
104 #define likely(x) __builtin_expect(!!(x), 1)
105 #define unlikely(x) __builtin_expect(!!(x), 0)
106 if (unlikely(l >= 1024)) {
110 for (; l > 0; l-=64) {
124 /* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */
125 #define netmap_rdtsc(t) \
129 do_cpuid(0, __regs); \
134 do_cpuid(u_int ax, u_int *p)
136 __asm __volatile("cpuid"
137 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
141 static __inline uint64_t
146 __asm __volatile("rdtsc" : "=A" (rv));
149 #define MAX_SAMPLES 100000
150 #endif /* EXPERIMENTAL */
154 struct ether_header eh;
157 uint8_t body[2048]; // XXX hardwired
158 } __attribute__((__packed__));
161 * global arguments for all threads
170 int npackets; /* total packets to send */
173 int options; /* testing */
174 #define OPT_PREFETCH 1
183 uint64_t containers[8];
187 * Arguments for a new thread. The same structure is used by
188 * the source and the sink
196 struct netmap_if *nifp;
197 uint16_t qfirst, qlast; /* range of queues to scan */
199 struct timeval tic, toc;
217 static struct targ *targs;
218 static int global_nthreads;
220 /* control-C handler */
222 sigint_h(__unused int sig)
224 for (int i = 0; i < global_nthreads; i++) {
225 /* cancel active threads. */
226 if (targs[i].used == 0)
229 D("Cancelling thread #%d\n", i);
230 pthread_cancel(targs[i].thread);
234 signal(SIGINT, SIG_DFL);
238 /* sysctl wrapper to return the number of active CPUs */
248 sysctl(mib, 2, &ncpus, &len, NULL, 0);
254 * locate the src mac address for our interface, put it
255 * into the user-supplied buffer. return 0 if ok, -1 on error.
258 source_hwaddr(const char *ifname, char *buf)
260 struct ifaddrs *ifaphead, *ifap;
261 int l = sizeof(ifap->ifa_name);
263 if (getifaddrs(&ifaphead) != 0) {
264 D("getifaddrs %s failed", ifname);
268 for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
269 struct sockaddr_dl *sdl =
270 (struct sockaddr_dl *)ifap->ifa_addr;
273 if (!sdl || sdl->sdl_family != AF_LINK)
275 if (strncmp(ifap->ifa_name, ifname, l) != 0)
277 mac = (uint8_t *)LLADDR(sdl);
278 sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
279 mac[0], mac[1], mac[2],
280 mac[3], mac[4], mac[5]);
282 D("source hwaddr %s", buf);
285 freeifaddrs(ifaphead);
290 /* set the thread affinity. */
292 setaffinity(pthread_t me, int i)
299 /* Set thread affinity affinity.*/
301 CPU_SET(i, &cpumask);
303 if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
304 D("Unable to set affinity");
310 /* Compute the checksum of the given ip header. */
312 checksum(const void *data, uint16_t len)
314 const uint8_t *addr = data;
318 sum += addr[0] * 256 + addr[1];
326 sum = (sum >> 16) + (sum & 0xffff);
335 * Fill a packet with some payload.
338 initialize_packet(struct targ *targ)
340 struct pkt *pkt = &targ->pkt;
341 struct ether_header *eh;
344 uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(*ip);
345 int i, l, l0 = strlen(default_payload);
348 for (i = 0; i < paylen;) {
349 l = min(l0, paylen - i);
350 bcopy(default_payload, pkt->body + i, l);
353 pkt->body[i-1] = '\0';
356 udp->uh_sport = htons(1234);
357 udp->uh_dport = htons(4321);
358 udp->uh_ulen = htons(paylen);
359 udp->uh_sum = 0; // checksum(udp, sizeof(*udp));
362 ip->ip_v = IPVERSION;
365 ip->ip_tos = IPTOS_LOWDELAY;
366 ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
368 ip->ip_off = htons(IP_DF); /* Don't fragment */
369 ip->ip_ttl = IPDEFTTL;
370 ip->ip_p = IPPROTO_UDP;
371 inet_aton(targ->g->src_ip, (struct in_addr *)&ip->ip_src);
372 inet_aton(targ->g->dst_ip, (struct in_addr *)&ip->ip_dst);
373 targ->dst_ip = ip->ip_dst.s_addr;
374 targ->src_ip = ip->ip_src.s_addr;
375 p = index(targ->g->src_ip, '-');
377 targ->dst_ip_range = atoi(p+1);
378 D("dst-ip sweep %d addresses", targ->dst_ip_range);
380 ip->ip_sum = checksum(ip, sizeof(*ip));
383 bcopy(ether_aton(targ->g->src_mac), targ->src_mac, 6);
384 bcopy(targ->src_mac, eh->ether_shost, 6);
385 p = index(targ->g->src_mac, '-');
387 targ->src_mac_range = atoi(p+1);
389 bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
390 bcopy(targ->dst_mac, eh->ether_dhost, 6);
391 p = index(targ->g->dst_mac, '-');
393 targ->dst_mac_range = atoi(p+1);
394 eh->ether_type = htons(ETHERTYPE_IP);
397 /* Check the payload of the packet for errors (use it for debug).
398 * Look for consecutive ascii representations of the size of the packet.
401 check_payload(char *p, int psize)
404 int n_read, size, sizelen;
406 /* get the length in ASCII of the length of the packet. */
407 sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace
410 p += 14; /* skip packet header. */
412 while (psize - n_read >= sizelen) {
413 sscanf(p, "%d", &size);
415 D("Read %d instead of %d", size, psize);
426 * create and enqueue a batch of packets on a ring.
427 * On the last one set NS_REPORT to tell the driver to generate
428 * an interrupt when done.
431 send_packets(struct netmap_ring *ring, struct pkt *pkt,
432 int size, u_int count, int options)
434 u_int sent, cur = ring->cur;
436 if (ring->avail < count)
440 if (options & (OPT_COPY | OPT_PREFETCH) ) {
441 for (sent = 0; sent < count; sent++) {
442 struct netmap_slot *slot = &ring->slot[cur];
443 char *p = NETMAP_BUF(ring, slot->buf_idx);
446 cur = NETMAP_RING_NEXT(ring, cur);
451 for (sent = 0; sent < count; sent++) {
452 struct netmap_slot *slot = &ring->slot[cur];
453 char *p = NETMAP_BUF(ring, slot->buf_idx);
455 if (options & OPT_COPY)
456 pkt_copy(pkt, p, size);
457 else if (options & OPT_MEMCPY)
458 memcpy(p, pkt, size);
459 else if (options & OPT_PREFETCH)
463 if (sent == count - 1)
464 slot->flags |= NS_REPORT;
465 cur = NETMAP_RING_NEXT(ring, cur);
474 sender_body(void *data)
476 struct targ *targ = (struct targ *) data;
478 struct pollfd fds[1];
479 struct netmap_if *nifp = targ->nifp;
480 struct netmap_ring *txring;
481 int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
482 int options = targ->g->options | OPT_COPY;
484 if (setaffinity(targ->thread, targ->affinity))
486 /* setup poll(2) mechanism. */
487 memset(fds, 0, sizeof(fds));
488 fds[0].fd = targ->fd;
489 fds[0].events = (POLLOUT);
492 gettimeofday(&targ->tic, NULL);
493 if (targ->g->use_pcap) {
494 int size = targ->g->pkt_size;
495 void *pkt = &targ->pkt;
496 pcap_t *p = targ->g->p;
498 for (i = 0; sent < n; i++) {
499 if (pcap_inject(p, pkt, size) != -1)
510 * wait for available room in the send queue(s)
512 if (poll(fds, 1, 2000) <= 0) {
513 D("poll error/timeout on queue %d\n", targ->me);
517 * scan our queues and send on those with room
519 if (sent > 100000 && !(targ->g->options & OPT_COPY) )
520 options &= ~OPT_COPY;
521 for (i = targ->qfirst; i < targ->qlast; i++) {
522 int m, limit = MIN(n - sent, targ->g->burst);
524 txring = NETMAP_TXRING(nifp, i);
525 if (txring->avail == 0)
527 m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
533 /* flush any remaining packets */
534 ioctl(fds[0].fd, NIOCTXSYNC, NULL);
536 /* final part: wait all the TX queues to be empty. */
537 for (i = targ->qfirst; i < targ->qlast; i++) {
538 txring = NETMAP_TXRING(nifp, i);
539 while (!NETMAP_TX_RING_EMPTY(txring)) {
540 ioctl(fds[0].fd, NIOCTXSYNC, NULL);
541 usleep(1); /* wait 1 tick */
546 gettimeofday(&targ->toc, NULL);
551 /* reset the ``used`` flag. */
559 receive_pcap(u_char *user, __unused const struct pcap_pkthdr * h,
560 __unused const u_char * bytes)
562 int *count = (int *)user;
567 receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload)
572 if (ring->avail < limit)
574 for (rx = 0; rx < limit; rx++) {
575 struct netmap_slot *slot = &ring->slot[cur];
576 char *p = NETMAP_BUF(ring, slot->buf_idx);
579 check_payload(p, slot->len);
581 cur = NETMAP_RING_NEXT(ring, cur);
590 receiver_body(void *data)
592 struct targ *targ = (struct targ *) data;
593 struct pollfd fds[1];
594 struct netmap_if *nifp = targ->nifp;
595 struct netmap_ring *rxring;
598 if (setaffinity(targ->thread, targ->affinity))
601 /* setup poll(2) mechanism. */
602 memset(fds, 0, sizeof(fds));
603 fds[0].fd = targ->fd;
604 fds[0].events = (POLLIN);
606 /* unbounded wait for the first packet. */
608 i = poll(fds, 1, 1000);
609 if (i > 0 && !(fds[0].revents & POLLERR))
611 D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
614 /* main loop, exit after 1s silence */
615 gettimeofday(&targ->tic, NULL);
616 if (targ->g->use_pcap) {
618 pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
622 /* Once we started to receive packets, wait at most 1 seconds
624 if (poll(fds, 1, 1 * 1000) <= 0) {
625 gettimeofday(&targ->toc, NULL);
626 targ->toc.tv_sec -= 1; /* Subtract timeout time. */
630 for (i = targ->qfirst; i < targ->qlast; i++) {
633 rxring = NETMAP_RXRING(nifp, i);
634 if (rxring->avail == 0)
637 m = receive_packets(rxring, targ->g->burst,
640 targ->count = received;
643 // tell the card we have read the data
644 //ioctl(fds[0].fd, NIOCRXSYNC, NULL);
649 targ->count = received;
652 /* reset the ``used`` flag. */
659 tx_output(uint64_t sent, int size, double delta)
661 double amount = 8.0 * (1.0 * size * sent) / delta;
662 double pps = sent / delta;
663 char units[4] = { '\0', 'K', 'M', 'G' };
664 int aunit = 0, punit = 0;
666 while (amount >= 1000) {
670 while (pps >= 1000) {
675 printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n",
677 printf("Speed: %.2f%cpps. Bandwidth: %.2f%cbps.\n",
678 pps, units[punit], amount, units[aunit]);
683 rx_output(uint64_t received, double delta)
686 double pps = received / delta;
687 char units[4] = { '\0', 'K', 'M', 'G' };
690 while (pps >= 1000) {
695 printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
696 printf("Speed: %.2f%cpps.\n", pps, units[punit]);
702 const char *cmd = "pkt-gen";
706 "\t-i interface interface name\n"
707 "\t-t pkts_to_send also forces send mode\n"
708 "\t-r pkts_to_receive also forces receive mode\n"
709 "\t-l pkts_size in bytes excluding CRC\n"
710 "\t-d dst-ip end with %%n to sweep n addresses\n"
711 "\t-s src-ip end with %%n to sweep n addresses\n"
712 "\t-D dst-mac end with %%n to sweep n addresses\n"
713 "\t-S src-mac end with %%n to sweep n addresses\n"
714 "\t-b burst size testing, mostly\n"
715 "\t-c cores cores to use\n"
716 "\t-p threads processes/threads to use\n"
717 "\t-T report_ms milliseconds between reports\n"
718 "\t-w wait_for_link_time in seconds\n"
727 main(int arc, char **argv)
730 char pcap_errbuf[PCAP_ERRBUF_SIZE];
735 void *mmap_addr; /* the mmap address */
736 void *(*td_body)(void *) = receiver_body;
738 int report_interval = 1000; /* report interval */
741 int devqueues = 1; /* how many device queues */
743 bzero(&g, sizeof(g));
745 g.src_ip = "10.0.0.1";
746 g.dst_ip = "10.1.0.1";
747 g.dst_mac = "ff:ff:ff:ff:ff:ff";
750 g.burst = 512; // default
754 while ( (ch = getopt(arc, argv,
755 "i:t:r:l:d:s:D:S:b:c:o:p:PT:w:v")) != -1) {
758 D("bad option %c %s", ch, optarg);
762 g.options = atoi(optarg);
764 case 'i': /* interface */
768 td_body = sender_body;
769 g.npackets = atoi(optarg);
771 case 'r': /* receive */
772 td_body = receiver_body;
773 g.npackets = atoi(optarg);
775 case 'l': /* pkt_size */
776 g.pkt_size = atoi(optarg);
784 case 'T': /* report interval */
785 report_interval = atoi(optarg);
788 wait_link = atoi(optarg);
790 case 'b': /* burst */
791 g.burst = atoi(optarg);
794 g.cpus = atoi(optarg);
797 g.nthreads = atoi(optarg);
804 case 'D': /* destination mac */
807 struct ether_addr *mac = ether_aton(g.dst_mac);
808 D("ether_aton(%s) gives %p", g.dst_mac, mac);
811 case 'S': /* source mac */
819 if (ifname == NULL) {
824 int n = system_ncpus();
825 if (g.cpus < 0 || g.cpus > n) {
826 D("%d cpus is too high, have only %d cpus", g.cpus, n);
832 if (g.pkt_size < 16 || g.pkt_size > 1536) {
833 D("bad pktsize %d\n", g.pkt_size);
837 if (td_body == sender_body && g.src_mac == NULL) {
838 static char mybuf[20] = "ff:ff:ff:ff:ff:ff";
839 /* retrieve source mac address. */
840 if (source_hwaddr(ifname, mybuf) == -1) {
841 D("Unable to retrieve source mac");
842 // continue, fail later
848 D("using pcap on %s", ifname);
849 g.p = pcap_open_live(ifname, 0, 1, 100, pcap_errbuf);
851 D("cannot open pcap on %s", ifname);
857 bzero(&nmr, sizeof(nmr));
858 nmr.nr_version = NETMAP_API;
860 * Open the netmap device to fetch the number of queues of our
863 * The first NIOCREGIF also detaches the card from the
864 * protocol stack and may cause a reset of the card,
865 * which in turn may take some time for the PHY to
868 fd = open("/dev/netmap", O_RDWR);
870 D("Unable to open /dev/netmap");
873 if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) {
874 D("Unable to get if info without name");
876 D("map size is %d Kb", nmr.nr_memsize >> 10);
878 bzero(&nmr, sizeof(nmr));
879 nmr.nr_version = NETMAP_API;
880 strncpy(nmr.nr_name, ifname, sizeof(nmr.nr_name));
881 if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) {
882 D("Unable to get if info for %s", ifname);
884 devqueues = nmr.nr_rx_rings;
887 /* validate provided nthreads. */
888 if (g.nthreads < 1 || g.nthreads > devqueues) {
889 D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
890 // continue, fail later
894 * Map the netmap shared memory: instead of issuing mmap()
895 * inside the body of the threads, we prefer to keep this
896 * operation here to simplify the thread logic.
898 D("mmapping %d Kbytes", nmr.nr_memsize>>10);
899 mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize,
900 PROT_WRITE | PROT_READ,
902 if (mmap_addr == MAP_FAILED) {
903 D("Unable to mmap %d KB", nmr.nr_memsize >> 10);
904 // continue, fail later
908 * Register the interface on the netmap device: from now on,
909 * we can operate on the network interface without any
910 * interference from the legacy network stack.
912 * We decide to put the first interface registration here to
913 * give time to cards that take a long time to reset the PHY.
915 nmr.nr_version = NETMAP_API;
916 if (ioctl(fd, NIOCREGIF, &nmr) == -1) {
917 D("Unable to register interface %s", ifname);
918 //continue, fail later
922 /* Print some debug information. */
924 "%s %s: %d queues, %d threads and %d cpus.\n",
925 (td_body == sender_body) ? "Sending on" : "Receiving from",
930 if (td_body == sender_body) {
931 fprintf(stdout, "%s -> %s (%s -> %s)\n",
933 g.src_mac, g.dst_mac);
936 /* Exit if something went wrong. */
944 D("special options:%s%s%s%s\n",
945 g.options & OPT_PREFETCH ? " prefetch" : "",
946 g.options & OPT_ACCESS ? " access" : "",
947 g.options & OPT_MEMCPY ? " memcpy" : "",
948 g.options & OPT_COPY ? " copy" : "");
950 /* Wait for PHY reset. */
951 D("Wait %d secs for phy reset", wait_link);
955 /* Install ^C handler. */
956 global_nthreads = g.nthreads;
957 signal(SIGINT, sigint_h);
960 g.p = pcap_open_live(ifname, 0, 1, 100, NULL);
962 D("cannot open pcap on %s", ifname);
965 D("using pcap %p on %s", g.p, ifname);
968 targs = calloc(g.nthreads, sizeof(*targs));
970 * Now create the desired number of threads, each one
971 * using a single descriptor.
973 for (i = 0; i < g.nthreads; i++) {
974 struct netmap_if *tnifp;
982 /* register interface. */
983 tfd = open("/dev/netmap", O_RDWR);
985 D("Unable to open /dev/netmap");
989 bzero(&tifreq, sizeof(tifreq));
990 strncpy(tifreq.nr_name, ifname, sizeof(tifreq.nr_name));
991 tifreq.nr_version = NETMAP_API;
992 tifreq.nr_ringid = (g.nthreads > 1) ? (i | NETMAP_HW_RING) : 0;
995 * if we are acting as a receiver only, do not touch the transmit ring.
996 * This is not the default because many apps may use the interface
997 * in both directions, but a pure receiver does not.
999 if (td_body == receiver_body) {
1000 tifreq.nr_ringid |= NETMAP_NO_TX_POLL;
1003 if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) {
1004 D("Unable to register %s", ifname);
1007 tnifp = NETMAP_IF(mmap_addr, tifreq.nr_offset);
1009 /* start threads. */
1010 bzero(&targs[i], sizeof(targs[i]));
1013 targs[i].completed = 0;
1015 targs[i].nmr = tifreq;
1016 targs[i].nifp = tnifp;
1017 targs[i].qfirst = (g.nthreads > 1) ? i : 0;
1018 targs[i].qlast = (g.nthreads > 1) ? i+1 :
1019 (td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings);
1021 targs[i].affinity = g.cpus ? i % g.cpus : -1;
1022 if (td_body == sender_body) {
1023 /* initialize the packet to send. */
1024 initialize_packet(&targs[i]);
1027 if (pthread_create(&targs[i].thread, NULL, td_body,
1029 D("Unable to create thread %d", i);
1035 uint64_t my_count = 0, prev = 0;
1038 struct timeval tic, toc;
1040 gettimeofday(&toc, NULL);
1042 struct timeval now, delta;
1046 delta.tv_sec = report_interval/1000;
1047 delta.tv_usec = (report_interval%1000)*1000;
1048 select(0, NULL, NULL, NULL, &delta);
1049 gettimeofday(&now, NULL);
1050 timersub(&now, &toc, &toc);
1052 for (i = 0; i < g.nthreads; i++) {
1053 my_count += targs[i].count;
1054 if (targs[i].used == 0)
1057 pps = toc.tv_sec* 1000000 + toc.tv_usec;
1060 pps = (my_count - prev)*1000000 / pps;
1061 D("%" PRIu64 " pps", pps);
1064 if (done == g.nthreads)
1070 for (i = 0; i < g.nthreads; i++) {
1072 * Join active threads, unregister interfaces and close
1075 pthread_join(targs[i].thread, NULL);
1076 ioctl(targs[i].fd, NIOCUNREGIF, &targs[i].nmr);
1079 if (targs[i].completed == 0)
1083 * Collect threads output and extract information about
1084 * how long it took to send all the packets.
1086 count += targs[i].count;
1087 if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <))
1089 if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >))
1094 timersub(&toc, &tic, &toc);
1095 delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
1096 if (td_body == sender_body)
1097 tx_output(count, g.pkt_size, delta_t);
1099 rx_output(count, delta_t);
1102 if (g.use_pcap == 0) {
1103 ioctl(fd, NIOCUNREGIF, &nmr);
1104 munmap(mmap_addr, nmr.nr_memsize);