13a687befSWillem de Bruijn // SPDX-License-Identifier: GPL-2.0
23a687befSWillem de Bruijn
33a687befSWillem de Bruijn #define _GNU_SOURCE
43a687befSWillem de Bruijn
53a687befSWillem de Bruijn #include <arpa/inet.h>
63a687befSWillem de Bruijn #include <errno.h>
73a687befSWillem de Bruijn #include <error.h>
879ebc3c2SFred Klassen #include <linux/errqueue.h>
979ebc3c2SFred Klassen #include <linux/net_tstamp.h>
103a687befSWillem de Bruijn #include <netinet/if_ether.h>
113a687befSWillem de Bruijn #include <netinet/in.h>
123a687befSWillem de Bruijn #include <netinet/ip.h>
133a687befSWillem de Bruijn #include <netinet/ip6.h>
143a687befSWillem de Bruijn #include <netinet/udp.h>
153a687befSWillem de Bruijn #include <poll.h>
163a687befSWillem de Bruijn #include <sched.h>
173a687befSWillem de Bruijn #include <signal.h>
183a687befSWillem de Bruijn #include <stdbool.h>
193a687befSWillem de Bruijn #include <stdio.h>
203a687befSWillem de Bruijn #include <stdlib.h>
213a687befSWillem de Bruijn #include <string.h>
223a687befSWillem de Bruijn #include <sys/socket.h>
233a687befSWillem de Bruijn #include <sys/time.h>
2479ebc3c2SFred Klassen #include <sys/poll.h>
253a687befSWillem de Bruijn #include <sys/types.h>
263a687befSWillem de Bruijn #include <unistd.h>
273a687befSWillem de Bruijn
2822f1a38aSWillem de Bruijn #include "../kselftest.h"
2922f1a38aSWillem de Bruijn
303a687befSWillem de Bruijn #ifndef ETH_MAX_MTU
313a687befSWillem de Bruijn #define ETH_MAX_MTU 0xFFFFU
323a687befSWillem de Bruijn #endif
333a687befSWillem de Bruijn
343a687befSWillem de Bruijn #ifndef UDP_SEGMENT
353a687befSWillem de Bruijn #define UDP_SEGMENT 103
363a687befSWillem de Bruijn #endif
373a687befSWillem de Bruijn
383a687befSWillem de Bruijn #ifndef SO_ZEROCOPY
393a687befSWillem de Bruijn #define SO_ZEROCOPY 60
403a687befSWillem de Bruijn #endif
413a687befSWillem de Bruijn
4279ebc3c2SFred Klassen #ifndef SO_EE_ORIGIN_ZEROCOPY
4379ebc3c2SFred Klassen #define SO_EE_ORIGIN_ZEROCOPY 5
4479ebc3c2SFred Klassen #endif
4579ebc3c2SFred Klassen
463a687befSWillem de Bruijn #ifndef MSG_ZEROCOPY
473a687befSWillem de Bruijn #define MSG_ZEROCOPY 0x4000000
483a687befSWillem de Bruijn #endif
493a687befSWillem de Bruijn
5022f1a38aSWillem de Bruijn #ifndef ENOTSUPP
5122f1a38aSWillem de Bruijn #define ENOTSUPP 524
5222f1a38aSWillem de Bruijn #endif
5322f1a38aSWillem de Bruijn
543a687befSWillem de Bruijn #define NUM_PKT 100
553a687befSWillem de Bruijn
563a687befSWillem de Bruijn static bool cfg_cache_trash;
573a687befSWillem de Bruijn static int cfg_cpu = -1;
583a687befSWillem de Bruijn static int cfg_connected = true;
593a687befSWillem de Bruijn static int cfg_family = PF_UNSPEC;
603a687befSWillem de Bruijn static uint16_t cfg_mss;
613a687befSWillem de Bruijn static int cfg_payload_len = (1472 * 42);
623a687befSWillem de Bruijn static int cfg_port = 8000;
633a687befSWillem de Bruijn static int cfg_runtime_ms = -1;
6479ebc3c2SFred Klassen static bool cfg_poll;
65*329c9cd7SAndrei Gherzan static int cfg_poll_loop_timeout_ms = 2000;
663a687befSWillem de Bruijn static bool cfg_segment;
673a687befSWillem de Bruijn static bool cfg_sendmmsg;
683a687befSWillem de Bruijn static bool cfg_tcp;
6979ebc3c2SFred Klassen static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE;
7079ebc3c2SFred Klassen static bool cfg_tx_tstamp;
7179ebc3c2SFred Klassen static bool cfg_audit;
7279ebc3c2SFred Klassen static bool cfg_verbose;
733a687befSWillem de Bruijn static bool cfg_zerocopy;
743327a9c4SPaolo Abeni static int cfg_msg_nr;
753327a9c4SPaolo Abeni static uint16_t cfg_gso_size;
7679ebc3c2SFred Klassen static unsigned long total_num_msgs;
7779ebc3c2SFred Klassen static unsigned long total_num_sends;
7879ebc3c2SFred Klassen static unsigned long stat_tx_ts;
7979ebc3c2SFred Klassen static unsigned long stat_tx_ts_errors;
8079ebc3c2SFred Klassen static unsigned long tstart;
8179ebc3c2SFred Klassen static unsigned long tend;
8279ebc3c2SFred Klassen static unsigned long stat_zcopies;
833a687befSWillem de Bruijn
843a687befSWillem de Bruijn static socklen_t cfg_alen;
853a687befSWillem de Bruijn static struct sockaddr_storage cfg_dst_addr;
863a687befSWillem de Bruijn
873a687befSWillem de Bruijn static bool interrupted;
883a687befSWillem de Bruijn static char buf[NUM_PKT][ETH_MAX_MTU];
893a687befSWillem de Bruijn
sigint_handler(int signum)903a687befSWillem de Bruijn static void sigint_handler(int signum)
913a687befSWillem de Bruijn {
923a687befSWillem de Bruijn if (signum == SIGINT)
933a687befSWillem de Bruijn interrupted = true;
943a687befSWillem de Bruijn }
953a687befSWillem de Bruijn
gettimeofday_ms(void)963a687befSWillem de Bruijn static unsigned long gettimeofday_ms(void)
973a687befSWillem de Bruijn {
983a687befSWillem de Bruijn struct timeval tv;
993a687befSWillem de Bruijn
1003a687befSWillem de Bruijn gettimeofday(&tv, NULL);
1013a687befSWillem de Bruijn return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
1023a687befSWillem de Bruijn }
1033a687befSWillem de Bruijn
set_cpu(int cpu)1043a687befSWillem de Bruijn static int set_cpu(int cpu)
1053a687befSWillem de Bruijn {
1063a687befSWillem de Bruijn cpu_set_t mask;
1073a687befSWillem de Bruijn
1083a687befSWillem de Bruijn CPU_ZERO(&mask);
1093a687befSWillem de Bruijn CPU_SET(cpu, &mask);
1103a687befSWillem de Bruijn if (sched_setaffinity(0, sizeof(mask), &mask))
1113a687befSWillem de Bruijn error(1, 0, "setaffinity %d", cpu);
1123a687befSWillem de Bruijn
1133a687befSWillem de Bruijn return 0;
1143a687befSWillem de Bruijn }
1153a687befSWillem de Bruijn
setup_sockaddr(int domain,const char * str_addr,void * sockaddr)1163a687befSWillem de Bruijn static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
1173a687befSWillem de Bruijn {
1183a687befSWillem de Bruijn struct sockaddr_in6 *addr6 = (void *) sockaddr;
1193a687befSWillem de Bruijn struct sockaddr_in *addr4 = (void *) sockaddr;
1203a687befSWillem de Bruijn
1213a687befSWillem de Bruijn switch (domain) {
1223a687befSWillem de Bruijn case PF_INET:
1233a687befSWillem de Bruijn addr4->sin_family = AF_INET;
1243a687befSWillem de Bruijn addr4->sin_port = htons(cfg_port);
1253a687befSWillem de Bruijn if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
1263a687befSWillem de Bruijn error(1, 0, "ipv4 parse error: %s", str_addr);
1273a687befSWillem de Bruijn break;
1283a687befSWillem de Bruijn case PF_INET6:
1293a687befSWillem de Bruijn addr6->sin6_family = AF_INET6;
1303a687befSWillem de Bruijn addr6->sin6_port = htons(cfg_port);
1313a687befSWillem de Bruijn if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
1323a687befSWillem de Bruijn error(1, 0, "ipv6 parse error: %s", str_addr);
1333a687befSWillem de Bruijn break;
1343a687befSWillem de Bruijn default:
1353a687befSWillem de Bruijn error(1, 0, "illegal domain");
1363a687befSWillem de Bruijn }
1373a687befSWillem de Bruijn }
1383a687befSWillem de Bruijn
flush_cmsg(struct cmsghdr * cmsg)13979ebc3c2SFred Klassen static void flush_cmsg(struct cmsghdr *cmsg)
1403a687befSWillem de Bruijn {
14179ebc3c2SFred Klassen struct sock_extended_err *err;
14279ebc3c2SFred Klassen struct scm_timestamping *tss;
14379ebc3c2SFred Klassen __u32 lo;
14479ebc3c2SFred Klassen __u32 hi;
14579ebc3c2SFred Klassen int i;
14679ebc3c2SFred Klassen
14779ebc3c2SFred Klassen switch (cmsg->cmsg_level) {
14879ebc3c2SFred Klassen case SOL_SOCKET:
14979ebc3c2SFred Klassen if (cmsg->cmsg_type == SO_TIMESTAMPING) {
15079ebc3c2SFred Klassen i = (cfg_tx_ts == SOF_TIMESTAMPING_TX_HARDWARE) ? 2 : 0;
15179ebc3c2SFred Klassen tss = (struct scm_timestamping *)CMSG_DATA(cmsg);
15279ebc3c2SFred Klassen if (tss->ts[i].tv_sec == 0)
15379ebc3c2SFred Klassen stat_tx_ts_errors++;
15479ebc3c2SFred Klassen } else {
15579ebc3c2SFred Klassen error(1, 0, "unknown SOL_SOCKET cmsg type=%u\n",
15679ebc3c2SFred Klassen cmsg->cmsg_type);
15779ebc3c2SFred Klassen }
15879ebc3c2SFred Klassen break;
15979ebc3c2SFred Klassen case SOL_IP:
16079ebc3c2SFred Klassen case SOL_IPV6:
16179ebc3c2SFred Klassen switch (cmsg->cmsg_type) {
16279ebc3c2SFred Klassen case IP_RECVERR:
16379ebc3c2SFred Klassen case IPV6_RECVERR:
16479ebc3c2SFred Klassen {
16579ebc3c2SFred Klassen err = (struct sock_extended_err *)CMSG_DATA(cmsg);
16679ebc3c2SFred Klassen switch (err->ee_origin) {
16779ebc3c2SFred Klassen case SO_EE_ORIGIN_TIMESTAMPING:
16879ebc3c2SFred Klassen /* Got a TX timestamp from error queue */
16979ebc3c2SFred Klassen stat_tx_ts++;
17079ebc3c2SFred Klassen break;
17179ebc3c2SFred Klassen case SO_EE_ORIGIN_ICMP:
17279ebc3c2SFred Klassen case SO_EE_ORIGIN_ICMP6:
17379ebc3c2SFred Klassen if (cfg_verbose)
17479ebc3c2SFred Klassen fprintf(stderr,
17579ebc3c2SFred Klassen "received ICMP error: type=%u, code=%u\n",
17679ebc3c2SFred Klassen err->ee_type, err->ee_code);
17779ebc3c2SFred Klassen break;
17879ebc3c2SFred Klassen case SO_EE_ORIGIN_ZEROCOPY:
17979ebc3c2SFred Klassen {
18079ebc3c2SFred Klassen lo = err->ee_info;
18179ebc3c2SFred Klassen hi = err->ee_data;
18279ebc3c2SFred Klassen /* range of IDs acknowledged */
18379ebc3c2SFred Klassen stat_zcopies += hi - lo + 1;
18479ebc3c2SFred Klassen break;
18579ebc3c2SFred Klassen }
18679ebc3c2SFred Klassen case SO_EE_ORIGIN_LOCAL:
18779ebc3c2SFred Klassen if (cfg_verbose)
18879ebc3c2SFred Klassen fprintf(stderr,
18979ebc3c2SFred Klassen "received packet with local origin: %u\n",
19079ebc3c2SFred Klassen err->ee_origin);
19179ebc3c2SFred Klassen break;
19279ebc3c2SFred Klassen default:
19379ebc3c2SFred Klassen error(0, 1, "received packet with origin: %u",
19479ebc3c2SFred Klassen err->ee_origin);
19579ebc3c2SFred Klassen }
19679ebc3c2SFred Klassen break;
19779ebc3c2SFred Klassen }
19879ebc3c2SFred Klassen default:
19979ebc3c2SFred Klassen error(0, 1, "unknown IP msg type=%u\n",
20079ebc3c2SFred Klassen cmsg->cmsg_type);
20179ebc3c2SFred Klassen break;
20279ebc3c2SFred Klassen }
20379ebc3c2SFred Klassen break;
20479ebc3c2SFred Klassen default:
20579ebc3c2SFred Klassen error(0, 1, "unknown cmsg level=%u\n",
20679ebc3c2SFred Klassen cmsg->cmsg_level);
20779ebc3c2SFred Klassen }
20879ebc3c2SFred Klassen }
20979ebc3c2SFred Klassen
flush_errqueue_recv(int fd)21079ebc3c2SFred Klassen static void flush_errqueue_recv(int fd)
21179ebc3c2SFred Klassen {
21279ebc3c2SFred Klassen char control[CMSG_SPACE(sizeof(struct scm_timestamping)) +
21379ebc3c2SFred Klassen CMSG_SPACE(sizeof(struct sock_extended_err)) +
21479ebc3c2SFred Klassen CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
21579ebc3c2SFred Klassen struct msghdr msg = {0};
21679ebc3c2SFred Klassen struct cmsghdr *cmsg;
2173a687befSWillem de Bruijn int ret;
2183a687befSWillem de Bruijn
2193a687befSWillem de Bruijn while (1) {
22079ebc3c2SFred Klassen msg.msg_control = control;
22179ebc3c2SFred Klassen msg.msg_controllen = sizeof(control);
2223a687befSWillem de Bruijn ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
2233a687befSWillem de Bruijn if (ret == -1 && errno == EAGAIN)
2243a687befSWillem de Bruijn break;
2253a687befSWillem de Bruijn if (ret == -1)
2263a687befSWillem de Bruijn error(1, errno, "errqueue");
22779ebc3c2SFred Klassen if (msg.msg_flags != MSG_ERRQUEUE)
2283a687befSWillem de Bruijn error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
22979ebc3c2SFred Klassen if (cfg_audit) {
23079ebc3c2SFred Klassen for (cmsg = CMSG_FIRSTHDR(&msg);
23179ebc3c2SFred Klassen cmsg;
23279ebc3c2SFred Klassen cmsg = CMSG_NXTHDR(&msg, cmsg))
23379ebc3c2SFred Klassen flush_cmsg(cmsg);
23479ebc3c2SFred Klassen }
2353a687befSWillem de Bruijn msg.msg_flags = 0;
2363a687befSWillem de Bruijn }
2373a687befSWillem de Bruijn }
2383a687befSWillem de Bruijn
flush_errqueue(int fd,const bool do_poll,unsigned long poll_timeout,const bool poll_err)239*329c9cd7SAndrei Gherzan static void flush_errqueue(int fd, const bool do_poll,
240*329c9cd7SAndrei Gherzan unsigned long poll_timeout, const bool poll_err)
24179ebc3c2SFred Klassen {
24279ebc3c2SFred Klassen if (do_poll) {
24379ebc3c2SFred Klassen struct pollfd fds = {0};
24479ebc3c2SFred Klassen int ret;
24579ebc3c2SFred Klassen
24679ebc3c2SFred Klassen fds.fd = fd;
247*329c9cd7SAndrei Gherzan ret = poll(&fds, 1, poll_timeout);
24879ebc3c2SFred Klassen if (ret == 0) {
249*329c9cd7SAndrei Gherzan if ((cfg_verbose) && (poll_err))
25079ebc3c2SFred Klassen fprintf(stderr, "poll timeout\n");
25179ebc3c2SFred Klassen } else if (ret < 0) {
25279ebc3c2SFred Klassen error(1, errno, "poll");
25379ebc3c2SFred Klassen }
25479ebc3c2SFred Klassen }
25579ebc3c2SFred Klassen
25679ebc3c2SFred Klassen flush_errqueue_recv(fd);
25779ebc3c2SFred Klassen }
25879ebc3c2SFred Klassen
flush_errqueue_retry(int fd,unsigned long num_sends)259*329c9cd7SAndrei Gherzan static void flush_errqueue_retry(int fd, unsigned long num_sends)
260*329c9cd7SAndrei Gherzan {
261*329c9cd7SAndrei Gherzan unsigned long tnow, tstop;
262*329c9cd7SAndrei Gherzan bool first_try = true;
263*329c9cd7SAndrei Gherzan
264*329c9cd7SAndrei Gherzan tnow = gettimeofday_ms();
265*329c9cd7SAndrei Gherzan tstop = tnow + cfg_poll_loop_timeout_ms;
266*329c9cd7SAndrei Gherzan do {
267*329c9cd7SAndrei Gherzan flush_errqueue(fd, true, tstop - tnow, first_try);
268*329c9cd7SAndrei Gherzan first_try = false;
269*329c9cd7SAndrei Gherzan tnow = gettimeofday_ms();
270*329c9cd7SAndrei Gherzan } while ((stat_zcopies != num_sends) && (tnow < tstop));
271*329c9cd7SAndrei Gherzan }
272*329c9cd7SAndrei Gherzan
send_tcp(int fd,char * data)2733a687befSWillem de Bruijn static int send_tcp(int fd, char *data)
2743a687befSWillem de Bruijn {
2753a687befSWillem de Bruijn int ret, done = 0, count = 0;
2763a687befSWillem de Bruijn
2773a687befSWillem de Bruijn while (done < cfg_payload_len) {
2783a687befSWillem de Bruijn ret = send(fd, data + done, cfg_payload_len - done,
2793a687befSWillem de Bruijn cfg_zerocopy ? MSG_ZEROCOPY : 0);
2803a687befSWillem de Bruijn if (ret == -1)
2813a687befSWillem de Bruijn error(1, errno, "write");
2823a687befSWillem de Bruijn
2833a687befSWillem de Bruijn done += ret;
2843a687befSWillem de Bruijn count++;
2853a687befSWillem de Bruijn }
2863a687befSWillem de Bruijn
2873a687befSWillem de Bruijn return count;
2883a687befSWillem de Bruijn }
2893a687befSWillem de Bruijn
send_udp(int fd,char * data)2903a687befSWillem de Bruijn static int send_udp(int fd, char *data)
2913a687befSWillem de Bruijn {
2923a687befSWillem de Bruijn int ret, total_len, len, count = 0;
2933a687befSWillem de Bruijn
2943a687befSWillem de Bruijn total_len = cfg_payload_len;
2953a687befSWillem de Bruijn
2963a687befSWillem de Bruijn while (total_len) {
2973a687befSWillem de Bruijn len = total_len < cfg_mss ? total_len : cfg_mss;
2983a687befSWillem de Bruijn
2993a687befSWillem de Bruijn ret = sendto(fd, data, len, cfg_zerocopy ? MSG_ZEROCOPY : 0,
3003a687befSWillem de Bruijn cfg_connected ? NULL : (void *)&cfg_dst_addr,
3013a687befSWillem de Bruijn cfg_connected ? 0 : cfg_alen);
3023a687befSWillem de Bruijn if (ret == -1)
3033a687befSWillem de Bruijn error(1, errno, "write");
3043a687befSWillem de Bruijn if (ret != len)
3053a687befSWillem de Bruijn error(1, errno, "write: %uB != %uB\n", ret, len);
3063a687befSWillem de Bruijn
3073a687befSWillem de Bruijn total_len -= len;
3083a687befSWillem de Bruijn count++;
3093a687befSWillem de Bruijn }
3103a687befSWillem de Bruijn
3113a687befSWillem de Bruijn return count;
3123a687befSWillem de Bruijn }
3133a687befSWillem de Bruijn
send_ts_cmsg(struct cmsghdr * cm)31479ebc3c2SFred Klassen static void send_ts_cmsg(struct cmsghdr *cm)
31579ebc3c2SFred Klassen {
31679ebc3c2SFred Klassen uint32_t *valp;
31779ebc3c2SFred Klassen
31879ebc3c2SFred Klassen cm->cmsg_level = SOL_SOCKET;
31979ebc3c2SFred Klassen cm->cmsg_type = SO_TIMESTAMPING;
32079ebc3c2SFred Klassen cm->cmsg_len = CMSG_LEN(sizeof(cfg_tx_ts));
32179ebc3c2SFred Klassen valp = (void *)CMSG_DATA(cm);
32279ebc3c2SFred Klassen *valp = cfg_tx_ts;
32379ebc3c2SFred Klassen }
32479ebc3c2SFred Klassen
send_udp_sendmmsg(int fd,char * data)3253a687befSWillem de Bruijn static int send_udp_sendmmsg(int fd, char *data)
3263a687befSWillem de Bruijn {
32779ebc3c2SFred Klassen char control[CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
3283a687befSWillem de Bruijn const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN;
3293a687befSWillem de Bruijn struct mmsghdr mmsgs[max_nr_msg];
3303a687befSWillem de Bruijn struct iovec iov[max_nr_msg];
3313a687befSWillem de Bruijn unsigned int off = 0, left;
33279ebc3c2SFred Klassen size_t msg_controllen = 0;
3333a687befSWillem de Bruijn int i = 0, ret;
3343a687befSWillem de Bruijn
3353a687befSWillem de Bruijn memset(mmsgs, 0, sizeof(mmsgs));
3363a687befSWillem de Bruijn
33779ebc3c2SFred Klassen if (cfg_tx_tstamp) {
33879ebc3c2SFred Klassen struct msghdr msg = {0};
33979ebc3c2SFred Klassen struct cmsghdr *cmsg;
34079ebc3c2SFred Klassen
34179ebc3c2SFred Klassen msg.msg_control = control;
34279ebc3c2SFred Klassen msg.msg_controllen = sizeof(control);
34379ebc3c2SFred Klassen cmsg = CMSG_FIRSTHDR(&msg);
34479ebc3c2SFred Klassen send_ts_cmsg(cmsg);
34579ebc3c2SFred Klassen msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
34679ebc3c2SFred Klassen }
34779ebc3c2SFred Klassen
3483a687befSWillem de Bruijn left = cfg_payload_len;
3493a687befSWillem de Bruijn while (left) {
3503a687befSWillem de Bruijn if (i == max_nr_msg)
3513a687befSWillem de Bruijn error(1, 0, "sendmmsg: exceeds max_nr_msg");
3523a687befSWillem de Bruijn
3533a687befSWillem de Bruijn iov[i].iov_base = data + off;
3543a687befSWillem de Bruijn iov[i].iov_len = cfg_mss < left ? cfg_mss : left;
3553a687befSWillem de Bruijn
3563a687befSWillem de Bruijn mmsgs[i].msg_hdr.msg_iov = iov + i;
3573a687befSWillem de Bruijn mmsgs[i].msg_hdr.msg_iovlen = 1;
3583a687befSWillem de Bruijn
35979ebc3c2SFred Klassen mmsgs[i].msg_hdr.msg_name = (void *)&cfg_dst_addr;
36079ebc3c2SFred Klassen mmsgs[i].msg_hdr.msg_namelen = cfg_alen;
36179ebc3c2SFred Klassen if (msg_controllen) {
36279ebc3c2SFred Klassen mmsgs[i].msg_hdr.msg_control = control;
36379ebc3c2SFred Klassen mmsgs[i].msg_hdr.msg_controllen = msg_controllen;
36479ebc3c2SFred Klassen }
36579ebc3c2SFred Klassen
3663a687befSWillem de Bruijn off += iov[i].iov_len;
3673a687befSWillem de Bruijn left -= iov[i].iov_len;
3683a687befSWillem de Bruijn i++;
3693a687befSWillem de Bruijn }
3703a687befSWillem de Bruijn
3713a687befSWillem de Bruijn ret = sendmmsg(fd, mmsgs, i, cfg_zerocopy ? MSG_ZEROCOPY : 0);
3723a687befSWillem de Bruijn if (ret == -1)
3733a687befSWillem de Bruijn error(1, errno, "sendmmsg");
3743a687befSWillem de Bruijn
3753a687befSWillem de Bruijn return ret;
3763a687befSWillem de Bruijn }
3773a687befSWillem de Bruijn
send_udp_segment_cmsg(struct cmsghdr * cm)3783a687befSWillem de Bruijn static void send_udp_segment_cmsg(struct cmsghdr *cm)
3793a687befSWillem de Bruijn {
3803a687befSWillem de Bruijn uint16_t *valp;
3813a687befSWillem de Bruijn
3823a687befSWillem de Bruijn cm->cmsg_level = SOL_UDP;
3833a687befSWillem de Bruijn cm->cmsg_type = UDP_SEGMENT;
3843327a9c4SPaolo Abeni cm->cmsg_len = CMSG_LEN(sizeof(cfg_gso_size));
3853a687befSWillem de Bruijn valp = (void *)CMSG_DATA(cm);
3863327a9c4SPaolo Abeni *valp = cfg_gso_size;
3873a687befSWillem de Bruijn }
3883a687befSWillem de Bruijn
send_udp_segment(int fd,char * data)3893a687befSWillem de Bruijn static int send_udp_segment(int fd, char *data)
3903a687befSWillem de Bruijn {
39179ebc3c2SFred Klassen char control[CMSG_SPACE(sizeof(cfg_gso_size)) +
39279ebc3c2SFred Klassen CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
3933a687befSWillem de Bruijn struct msghdr msg = {0};
3943a687befSWillem de Bruijn struct iovec iov = {0};
39579ebc3c2SFred Klassen size_t msg_controllen;
39679ebc3c2SFred Klassen struct cmsghdr *cmsg;
3973a687befSWillem de Bruijn int ret;
3983a687befSWillem de Bruijn
3993a687befSWillem de Bruijn iov.iov_base = data;
4003a687befSWillem de Bruijn iov.iov_len = cfg_payload_len;
4013a687befSWillem de Bruijn
4023a687befSWillem de Bruijn msg.msg_iov = &iov;
4033a687befSWillem de Bruijn msg.msg_iovlen = 1;
4043a687befSWillem de Bruijn
4053a687befSWillem de Bruijn msg.msg_control = control;
4063a687befSWillem de Bruijn msg.msg_controllen = sizeof(control);
40779ebc3c2SFred Klassen cmsg = CMSG_FIRSTHDR(&msg);
40879ebc3c2SFred Klassen send_udp_segment_cmsg(cmsg);
40979ebc3c2SFred Klassen msg_controllen = CMSG_SPACE(sizeof(cfg_mss));
41079ebc3c2SFred Klassen if (cfg_tx_tstamp) {
41179ebc3c2SFred Klassen cmsg = CMSG_NXTHDR(&msg, cmsg);
41279ebc3c2SFred Klassen send_ts_cmsg(cmsg);
41379ebc3c2SFred Klassen msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
41479ebc3c2SFred Klassen }
4153a687befSWillem de Bruijn
41679ebc3c2SFred Klassen msg.msg_controllen = msg_controllen;
4173a687befSWillem de Bruijn msg.msg_name = (void *)&cfg_dst_addr;
4183a687befSWillem de Bruijn msg.msg_namelen = cfg_alen;
4193a687befSWillem de Bruijn
4203a687befSWillem de Bruijn ret = sendmsg(fd, &msg, cfg_zerocopy ? MSG_ZEROCOPY : 0);
4213a687befSWillem de Bruijn if (ret == -1)
4223a687befSWillem de Bruijn error(1, errno, "sendmsg");
4233a687befSWillem de Bruijn if (ret != iov.iov_len)
424670cd684SMasami Hiramatsu error(1, 0, "sendmsg: %u != %llu\n", ret,
425670cd684SMasami Hiramatsu (unsigned long long)iov.iov_len);
4263a687befSWillem de Bruijn
4273a687befSWillem de Bruijn return 1;
4283a687befSWillem de Bruijn }
4293a687befSWillem de Bruijn
usage(const char * filepath)4303a687befSWillem de Bruijn static void usage(const char *filepath)
4313a687befSWillem de Bruijn {
432*329c9cd7SAndrei Gherzan error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] "
433*329c9cd7SAndrei Gherzan "[-L secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
4343a687befSWillem de Bruijn filepath);
4353a687befSWillem de Bruijn }
4363a687befSWillem de Bruijn
parse_opts(int argc,char ** argv)4373a687befSWillem de Bruijn static void parse_opts(int argc, char **argv)
4383a687befSWillem de Bruijn {
4399c1952aeSwujianguo const char *bind_addr = NULL;
4403a687befSWillem de Bruijn int max_len, hdrlen;
4413a687befSWillem de Bruijn int c;
4423a687befSWillem de Bruijn
443*329c9cd7SAndrei Gherzan while ((c = getopt(argc, argv, "46acC:D:Hl:L:mM:p:s:PS:tTuvz")) != -1) {
4443a687befSWillem de Bruijn switch (c) {
4453a687befSWillem de Bruijn case '4':
4463a687befSWillem de Bruijn if (cfg_family != PF_UNSPEC)
4473a687befSWillem de Bruijn error(1, 0, "Pass one of -4 or -6");
4483a687befSWillem de Bruijn cfg_family = PF_INET;
4493a687befSWillem de Bruijn cfg_alen = sizeof(struct sockaddr_in);
4503a687befSWillem de Bruijn break;
4513a687befSWillem de Bruijn case '6':
4523a687befSWillem de Bruijn if (cfg_family != PF_UNSPEC)
4533a687befSWillem de Bruijn error(1, 0, "Pass one of -4 or -6");
4543a687befSWillem de Bruijn cfg_family = PF_INET6;
4553a687befSWillem de Bruijn cfg_alen = sizeof(struct sockaddr_in6);
4563a687befSWillem de Bruijn break;
45779ebc3c2SFred Klassen case 'a':
45879ebc3c2SFred Klassen cfg_audit = true;
45979ebc3c2SFred Klassen break;
4603a687befSWillem de Bruijn case 'c':
4613a687befSWillem de Bruijn cfg_cache_trash = true;
4623a687befSWillem de Bruijn break;
4633a687befSWillem de Bruijn case 'C':
4643a687befSWillem de Bruijn cfg_cpu = strtol(optarg, NULL, 0);
4653a687befSWillem de Bruijn break;
4663a687befSWillem de Bruijn case 'D':
4679c1952aeSwujianguo bind_addr = optarg;
4683a687befSWillem de Bruijn break;
4693a687befSWillem de Bruijn case 'l':
4703a687befSWillem de Bruijn cfg_runtime_ms = strtoul(optarg, NULL, 10) * 1000;
4713a687befSWillem de Bruijn break;
472*329c9cd7SAndrei Gherzan case 'L':
473*329c9cd7SAndrei Gherzan cfg_poll_loop_timeout_ms = strtoul(optarg, NULL, 10) * 1000;
474*329c9cd7SAndrei Gherzan break;
4753a687befSWillem de Bruijn case 'm':
4763a687befSWillem de Bruijn cfg_sendmmsg = true;
4773a687befSWillem de Bruijn break;
4783327a9c4SPaolo Abeni case 'M':
4793327a9c4SPaolo Abeni cfg_msg_nr = strtoul(optarg, NULL, 10);
4803327a9c4SPaolo Abeni break;
4813a687befSWillem de Bruijn case 'p':
4823a687befSWillem de Bruijn cfg_port = strtoul(optarg, NULL, 0);
4833a687befSWillem de Bruijn break;
48479ebc3c2SFred Klassen case 'P':
48579ebc3c2SFred Klassen cfg_poll = true;
48679ebc3c2SFred Klassen break;
4873a687befSWillem de Bruijn case 's':
4883a687befSWillem de Bruijn cfg_payload_len = strtoul(optarg, NULL, 0);
4893a687befSWillem de Bruijn break;
4903a687befSWillem de Bruijn case 'S':
4913327a9c4SPaolo Abeni cfg_gso_size = strtoul(optarg, NULL, 0);
4923a687befSWillem de Bruijn cfg_segment = true;
4933a687befSWillem de Bruijn break;
49479ebc3c2SFred Klassen case 'H':
49579ebc3c2SFred Klassen cfg_tx_ts = SOF_TIMESTAMPING_TX_HARDWARE;
49679ebc3c2SFred Klassen cfg_tx_tstamp = true;
49779ebc3c2SFred Klassen break;
4983a687befSWillem de Bruijn case 't':
4993a687befSWillem de Bruijn cfg_tcp = true;
5003a687befSWillem de Bruijn break;
50179ebc3c2SFred Klassen case 'T':
50279ebc3c2SFred Klassen cfg_tx_tstamp = true;
50379ebc3c2SFred Klassen break;
5043a687befSWillem de Bruijn case 'u':
5053a687befSWillem de Bruijn cfg_connected = false;
5063a687befSWillem de Bruijn break;
50779ebc3c2SFred Klassen case 'v':
50879ebc3c2SFred Klassen cfg_verbose = true;
50979ebc3c2SFred Klassen break;
5103a687befSWillem de Bruijn case 'z':
5113a687befSWillem de Bruijn cfg_zerocopy = true;
5123a687befSWillem de Bruijn break;
513db9b47eeSAndrei Gherzan default:
514db9b47eeSAndrei Gherzan exit(1);
5153a687befSWillem de Bruijn }
5163a687befSWillem de Bruijn }
5173a687befSWillem de Bruijn
5189c1952aeSwujianguo if (!bind_addr)
5199c1952aeSwujianguo bind_addr = cfg_family == PF_INET6 ? "::" : "0.0.0.0";
5209c1952aeSwujianguo
5219c1952aeSwujianguo setup_sockaddr(cfg_family, bind_addr, &cfg_dst_addr);
5229c1952aeSwujianguo
5233a687befSWillem de Bruijn if (optind != argc)
5243a687befSWillem de Bruijn usage(argv[0]);
5253a687befSWillem de Bruijn
5263a687befSWillem de Bruijn if (cfg_family == PF_UNSPEC)
5273a687befSWillem de Bruijn error(1, 0, "must pass one of -4 or -6");
5283a687befSWillem de Bruijn if (cfg_tcp && !cfg_connected)
5293a687befSWillem de Bruijn error(1, 0, "connectionless tcp makes no sense");
5303a687befSWillem de Bruijn if (cfg_segment && cfg_sendmmsg)
5313a687befSWillem de Bruijn error(1, 0, "cannot combine segment offload and sendmmsg");
53279ebc3c2SFred Klassen if (cfg_tx_tstamp && !(cfg_segment || cfg_sendmmsg))
53379ebc3c2SFred Klassen error(1, 0, "Options -T and -H require either -S or -m option");
5343a687befSWillem de Bruijn
5353a687befSWillem de Bruijn if (cfg_family == PF_INET)
5363a687befSWillem de Bruijn hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
5373a687befSWillem de Bruijn else
5383a687befSWillem de Bruijn hdrlen = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
5393a687befSWillem de Bruijn
5403a687befSWillem de Bruijn cfg_mss = ETH_DATA_LEN - hdrlen;
5413a687befSWillem de Bruijn max_len = ETH_MAX_MTU - hdrlen;
5423327a9c4SPaolo Abeni if (!cfg_gso_size)
5433327a9c4SPaolo Abeni cfg_gso_size = cfg_mss;
5443a687befSWillem de Bruijn
5453a687befSWillem de Bruijn if (cfg_payload_len > max_len)
5463a687befSWillem de Bruijn error(1, 0, "payload length %u exceeds max %u",
5473a687befSWillem de Bruijn cfg_payload_len, max_len);
5483a687befSWillem de Bruijn }
5493a687befSWillem de Bruijn
set_pmtu_discover(int fd,bool is_ipv4)5503a687befSWillem de Bruijn static void set_pmtu_discover(int fd, bool is_ipv4)
5513a687befSWillem de Bruijn {
5523a687befSWillem de Bruijn int level, name, val;
5533a687befSWillem de Bruijn
5543a687befSWillem de Bruijn if (is_ipv4) {
5553a687befSWillem de Bruijn level = SOL_IP;
5563a687befSWillem de Bruijn name = IP_MTU_DISCOVER;
5573a687befSWillem de Bruijn val = IP_PMTUDISC_DO;
5583a687befSWillem de Bruijn } else {
5593a687befSWillem de Bruijn level = SOL_IPV6;
5603a687befSWillem de Bruijn name = IPV6_MTU_DISCOVER;
5613a687befSWillem de Bruijn val = IPV6_PMTUDISC_DO;
5623a687befSWillem de Bruijn }
5633a687befSWillem de Bruijn
5643a687befSWillem de Bruijn if (setsockopt(fd, level, name, &val, sizeof(val)))
5653a687befSWillem de Bruijn error(1, errno, "setsockopt path mtu");
5663a687befSWillem de Bruijn }
5673a687befSWillem de Bruijn
set_tx_timestamping(int fd)56879ebc3c2SFred Klassen static void set_tx_timestamping(int fd)
56979ebc3c2SFred Klassen {
57079ebc3c2SFred Klassen int val = SOF_TIMESTAMPING_OPT_CMSG | SOF_TIMESTAMPING_OPT_ID |
57179ebc3c2SFred Klassen SOF_TIMESTAMPING_OPT_TSONLY;
57279ebc3c2SFred Klassen
57379ebc3c2SFred Klassen if (cfg_tx_ts == SOF_TIMESTAMPING_TX_SOFTWARE)
57479ebc3c2SFred Klassen val |= SOF_TIMESTAMPING_SOFTWARE;
57579ebc3c2SFred Klassen else
57679ebc3c2SFred Klassen val |= SOF_TIMESTAMPING_RAW_HARDWARE;
57779ebc3c2SFred Klassen
57879ebc3c2SFred Klassen if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)))
57979ebc3c2SFred Klassen error(1, errno, "setsockopt tx timestamping");
58079ebc3c2SFred Klassen }
58179ebc3c2SFred Klassen
print_audit_report(unsigned long num_msgs,unsigned long num_sends)58279ebc3c2SFred Klassen static void print_audit_report(unsigned long num_msgs, unsigned long num_sends)
58379ebc3c2SFred Klassen {
58479ebc3c2SFred Klassen unsigned long tdelta;
58579ebc3c2SFred Klassen
58679ebc3c2SFred Klassen tdelta = tend - tstart;
58779ebc3c2SFred Klassen if (!tdelta)
58879ebc3c2SFred Klassen return;
58979ebc3c2SFred Klassen
59079ebc3c2SFred Klassen fprintf(stderr, "Summary over %lu.%03lu seconds...\n",
59179ebc3c2SFred Klassen tdelta / 1000, tdelta % 1000);
59279ebc3c2SFred Klassen fprintf(stderr,
59379ebc3c2SFred Klassen "sum %s tx: %6lu MB/s %10lu calls (%lu/s) %10lu msgs (%lu/s)\n",
59479ebc3c2SFred Klassen cfg_tcp ? "tcp" : "udp",
59579ebc3c2SFred Klassen ((num_msgs * cfg_payload_len) >> 10) / tdelta,
59679ebc3c2SFred Klassen num_sends, num_sends * 1000 / tdelta,
59779ebc3c2SFred Klassen num_msgs, num_msgs * 1000 / tdelta);
59879ebc3c2SFred Klassen
59979ebc3c2SFred Klassen if (cfg_tx_tstamp) {
60079ebc3c2SFred Klassen if (stat_tx_ts_errors)
60179ebc3c2SFred Klassen error(1, 0,
60279ebc3c2SFred Klassen "Expected clean TX Timestamps: %9lu msgs received %6lu errors",
60379ebc3c2SFred Klassen stat_tx_ts, stat_tx_ts_errors);
60479ebc3c2SFred Klassen if (stat_tx_ts != num_sends)
60579ebc3c2SFred Klassen error(1, 0,
60679ebc3c2SFred Klassen "Unexpected number of TX Timestamps: %9lu expected %9lu received",
60779ebc3c2SFred Klassen num_sends, stat_tx_ts);
60879ebc3c2SFred Klassen fprintf(stderr,
60979ebc3c2SFred Klassen "Tx Timestamps: %19lu received %17lu errors\n",
61079ebc3c2SFred Klassen stat_tx_ts, stat_tx_ts_errors);
61179ebc3c2SFred Klassen }
61279ebc3c2SFred Klassen
61379ebc3c2SFred Klassen if (cfg_zerocopy) {
61479ebc3c2SFred Klassen if (stat_zcopies != num_sends)
61579ebc3c2SFred Klassen error(1, 0, "Unexpected number of Zerocopy completions: %9lu expected %9lu received",
61679ebc3c2SFred Klassen num_sends, stat_zcopies);
61779ebc3c2SFred Klassen fprintf(stderr,
61879ebc3c2SFred Klassen "Zerocopy acks: %19lu\n",
61979ebc3c2SFred Klassen stat_zcopies);
62079ebc3c2SFred Klassen }
62179ebc3c2SFred Klassen }
62279ebc3c2SFred Klassen
print_report(unsigned long num_msgs,unsigned long num_sends)62379ebc3c2SFred Klassen static void print_report(unsigned long num_msgs, unsigned long num_sends)
62479ebc3c2SFred Klassen {
62579ebc3c2SFred Klassen fprintf(stderr,
62679ebc3c2SFred Klassen "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
62779ebc3c2SFred Klassen cfg_tcp ? "tcp" : "udp",
62879ebc3c2SFred Klassen (num_msgs * cfg_payload_len) >> 20,
62979ebc3c2SFred Klassen num_sends, num_msgs);
63079ebc3c2SFred Klassen
63179ebc3c2SFred Klassen if (cfg_audit) {
63279ebc3c2SFred Klassen total_num_msgs += num_msgs;
63379ebc3c2SFred Klassen total_num_sends += num_sends;
63479ebc3c2SFred Klassen }
63579ebc3c2SFred Klassen }
63679ebc3c2SFred Klassen
main(int argc,char ** argv)6373a687befSWillem de Bruijn int main(int argc, char **argv)
6383a687befSWillem de Bruijn {
6393a687befSWillem de Bruijn unsigned long num_msgs, num_sends;
6403a687befSWillem de Bruijn unsigned long tnow, treport, tstop;
64122f1a38aSWillem de Bruijn int fd, i, val, ret;
6423a687befSWillem de Bruijn
6433a687befSWillem de Bruijn parse_opts(argc, argv);
6443a687befSWillem de Bruijn
6453a687befSWillem de Bruijn if (cfg_cpu > 0)
6463a687befSWillem de Bruijn set_cpu(cfg_cpu);
6473a687befSWillem de Bruijn
6483a687befSWillem de Bruijn for (i = 0; i < sizeof(buf[0]); i++)
6493a687befSWillem de Bruijn buf[0][i] = 'a' + (i % 26);
6503a687befSWillem de Bruijn for (i = 1; i < NUM_PKT; i++)
6513a687befSWillem de Bruijn memcpy(buf[i], buf[0], sizeof(buf[0]));
6523a687befSWillem de Bruijn
6533a687befSWillem de Bruijn signal(SIGINT, sigint_handler);
6543a687befSWillem de Bruijn
6553a687befSWillem de Bruijn fd = socket(cfg_family, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
6563a687befSWillem de Bruijn if (fd == -1)
6573a687befSWillem de Bruijn error(1, errno, "socket");
6583a687befSWillem de Bruijn
6593a687befSWillem de Bruijn if (cfg_zerocopy) {
6603a687befSWillem de Bruijn val = 1;
66122f1a38aSWillem de Bruijn
66222f1a38aSWillem de Bruijn ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY,
66322f1a38aSWillem de Bruijn &val, sizeof(val));
66422f1a38aSWillem de Bruijn if (ret) {
66522f1a38aSWillem de Bruijn if (errno == ENOPROTOOPT || errno == ENOTSUPP) {
66622f1a38aSWillem de Bruijn fprintf(stderr, "SO_ZEROCOPY not supported");
66722f1a38aSWillem de Bruijn exit(KSFT_SKIP);
66822f1a38aSWillem de Bruijn }
6693a687befSWillem de Bruijn error(1, errno, "setsockopt zerocopy");
6703a687befSWillem de Bruijn }
67122f1a38aSWillem de Bruijn }
6723a687befSWillem de Bruijn
6733a687befSWillem de Bruijn if (cfg_connected &&
6743a687befSWillem de Bruijn connect(fd, (void *)&cfg_dst_addr, cfg_alen))
6753a687befSWillem de Bruijn error(1, errno, "connect");
6763a687befSWillem de Bruijn
6773a687befSWillem de Bruijn if (cfg_segment)
6783a687befSWillem de Bruijn set_pmtu_discover(fd, cfg_family == PF_INET);
6793a687befSWillem de Bruijn
68079ebc3c2SFred Klassen if (cfg_tx_tstamp)
68179ebc3c2SFred Klassen set_tx_timestamping(fd);
68279ebc3c2SFred Klassen
6833a687befSWillem de Bruijn num_msgs = num_sends = 0;
6843a687befSWillem de Bruijn tnow = gettimeofday_ms();
68579ebc3c2SFred Klassen tstart = tnow;
68679ebc3c2SFred Klassen tend = tnow;
6873a687befSWillem de Bruijn tstop = tnow + cfg_runtime_ms;
6883a687befSWillem de Bruijn treport = tnow + 1000;
6893a687befSWillem de Bruijn
6903a687befSWillem de Bruijn i = 0;
6913a687befSWillem de Bruijn do {
6923a687befSWillem de Bruijn if (cfg_tcp)
6933a687befSWillem de Bruijn num_sends += send_tcp(fd, buf[i]);
6943a687befSWillem de Bruijn else if (cfg_segment)
6953a687befSWillem de Bruijn num_sends += send_udp_segment(fd, buf[i]);
6963a687befSWillem de Bruijn else if (cfg_sendmmsg)
6973a687befSWillem de Bruijn num_sends += send_udp_sendmmsg(fd, buf[i]);
6983a687befSWillem de Bruijn else
6993a687befSWillem de Bruijn num_sends += send_udp(fd, buf[i]);
7003a687befSWillem de Bruijn num_msgs++;
70179ebc3c2SFred Klassen if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
702*329c9cd7SAndrei Gherzan flush_errqueue(fd, cfg_poll, 500, true);
7033a687befSWillem de Bruijn
7043327a9c4SPaolo Abeni if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
7053327a9c4SPaolo Abeni break;
7063327a9c4SPaolo Abeni
7073a687befSWillem de Bruijn tnow = gettimeofday_ms();
70879ebc3c2SFred Klassen if (tnow >= treport) {
70979ebc3c2SFred Klassen print_report(num_msgs, num_sends);
7103a687befSWillem de Bruijn num_msgs = num_sends = 0;
7113a687befSWillem de Bruijn treport = tnow + 1000;
7123a687befSWillem de Bruijn }
7133a687befSWillem de Bruijn
7143a687befSWillem de Bruijn /* cold cache when writing buffer */
7153a687befSWillem de Bruijn if (cfg_cache_trash)
7163a687befSWillem de Bruijn i = ++i < NUM_PKT ? i : 0;
7173a687befSWillem de Bruijn
7183a687befSWillem de Bruijn } while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
7193a687befSWillem de Bruijn
72079ebc3c2SFred Klassen if (cfg_zerocopy || cfg_tx_tstamp)
721*329c9cd7SAndrei Gherzan flush_errqueue_retry(fd, num_sends);
72279ebc3c2SFred Klassen
7233a687befSWillem de Bruijn if (close(fd))
7243a687befSWillem de Bruijn error(1, errno, "close");
7253a687befSWillem de Bruijn
72679ebc3c2SFred Klassen if (cfg_audit) {
72779ebc3c2SFred Klassen tend = tnow;
72879ebc3c2SFred Klassen total_num_msgs += num_msgs;
72979ebc3c2SFred Klassen total_num_sends += num_sends;
73079ebc3c2SFred Klassen print_audit_report(total_num_msgs, total_num_sends);
73179ebc3c2SFred Klassen }
73279ebc3c2SFred Klassen
7333a687befSWillem de Bruijn return 0;
7343a687befSWillem de Bruijn }
735