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