xref: /linux/tools/testing/selftests/net/msg_zerocopy.c (revision 033771c085c2ed73cb29dd25e1ec8c4b2991cad9)
107b65c5bSWillem de Bruijn /* Evaluate MSG_ZEROCOPY
207b65c5bSWillem de Bruijn  *
307b65c5bSWillem de Bruijn  * Send traffic between two processes over one of the supported
407b65c5bSWillem de Bruijn  * protocols and modes:
507b65c5bSWillem de Bruijn  *
607b65c5bSWillem de Bruijn  * PF_INET/PF_INET6
707b65c5bSWillem de Bruijn  * - SOCK_STREAM
807b65c5bSWillem de Bruijn  * - SOCK_DGRAM
907b65c5bSWillem de Bruijn  * - SOCK_DGRAM with UDP_CORK
1007b65c5bSWillem de Bruijn  * - SOCK_RAW
1107b65c5bSWillem de Bruijn  * - SOCK_RAW with IP_HDRINCL
1207b65c5bSWillem de Bruijn  *
1307b65c5bSWillem de Bruijn  * PF_PACKET
1407b65c5bSWillem de Bruijn  * - SOCK_DGRAM
1507b65c5bSWillem de Bruijn  * - SOCK_RAW
1607b65c5bSWillem de Bruijn  *
17b16ac920SSowmini Varadhan  * PF_RDS
18b16ac920SSowmini Varadhan  * - SOCK_SEQPACKET
19b16ac920SSowmini Varadhan  *
2007b65c5bSWillem de Bruijn  * Start this program on two connected hosts, one in send mode and
2107b65c5bSWillem de Bruijn  * the other with option '-r' to put it in receiver mode.
2207b65c5bSWillem de Bruijn  *
2307b65c5bSWillem de Bruijn  * If zerocopy mode ('-z') is enabled, the sender will verify that
2407b65c5bSWillem de Bruijn  * the kernel queues completions on the error queue for all zerocopy
2507b65c5bSWillem de Bruijn  * transfers.
2607b65c5bSWillem de Bruijn  */
2707b65c5bSWillem de Bruijn 
2807b65c5bSWillem de Bruijn #define _GNU_SOURCE
2907b65c5bSWillem de Bruijn 
3007b65c5bSWillem de Bruijn #include <arpa/inet.h>
3107b65c5bSWillem de Bruijn #include <error.h>
3207b65c5bSWillem de Bruijn #include <errno.h>
3307b65c5bSWillem de Bruijn #include <limits.h>
3407b65c5bSWillem de Bruijn #include <linux/errqueue.h>
3507b65c5bSWillem de Bruijn #include <linux/if_packet.h>
3607b65c5bSWillem de Bruijn #include <linux/ipv6.h>
3707b65c5bSWillem de Bruijn #include <linux/socket.h>
3807b65c5bSWillem de Bruijn #include <linux/sockios.h>
3907b65c5bSWillem de Bruijn #include <net/ethernet.h>
4007b65c5bSWillem de Bruijn #include <net/if.h>
4107b65c5bSWillem de Bruijn #include <netinet/ip.h>
4207b65c5bSWillem de Bruijn #include <netinet/ip6.h>
4307b65c5bSWillem de Bruijn #include <netinet/tcp.h>
4407b65c5bSWillem de Bruijn #include <netinet/udp.h>
4507b65c5bSWillem de Bruijn #include <poll.h>
4607b65c5bSWillem de Bruijn #include <sched.h>
4707b65c5bSWillem de Bruijn #include <stdbool.h>
4807b65c5bSWillem de Bruijn #include <stdio.h>
4907b65c5bSWillem de Bruijn #include <stdint.h>
5007b65c5bSWillem de Bruijn #include <stdlib.h>
5107b65c5bSWillem de Bruijn #include <string.h>
5207b65c5bSWillem de Bruijn #include <sys/ioctl.h>
5307b65c5bSWillem de Bruijn #include <sys/socket.h>
5407b65c5bSWillem de Bruijn #include <sys/stat.h>
5507b65c5bSWillem de Bruijn #include <sys/time.h>
5607b65c5bSWillem de Bruijn #include <sys/types.h>
5707b65c5bSWillem de Bruijn #include <sys/wait.h>
5807b65c5bSWillem de Bruijn #include <unistd.h>
59b16ac920SSowmini Varadhan #include <linux/rds.h>
6007b65c5bSWillem de Bruijn 
6107b65c5bSWillem de Bruijn #ifndef SO_EE_ORIGIN_ZEROCOPY
6206e8852cSThomas Meyer #define SO_EE_ORIGIN_ZEROCOPY		5
6307b65c5bSWillem de Bruijn #endif
6407b65c5bSWillem de Bruijn 
6507b65c5bSWillem de Bruijn #ifndef SO_ZEROCOPY
66bbd9644eSWillem de Bruijn #define SO_ZEROCOPY	60
6707b65c5bSWillem de Bruijn #endif
6807b65c5bSWillem de Bruijn 
6907b65c5bSWillem de Bruijn #ifndef SO_EE_CODE_ZEROCOPY_COPIED
7007b65c5bSWillem de Bruijn #define SO_EE_CODE_ZEROCOPY_COPIED	1
7107b65c5bSWillem de Bruijn #endif
7207b65c5bSWillem de Bruijn 
7307b65c5bSWillem de Bruijn #ifndef MSG_ZEROCOPY
7407b65c5bSWillem de Bruijn #define MSG_ZEROCOPY	0x4000000
7507b65c5bSWillem de Bruijn #endif
7607b65c5bSWillem de Bruijn 
7707b65c5bSWillem de Bruijn static int  cfg_cork;
7807b65c5bSWillem de Bruijn static bool cfg_cork_mixed;
7907b65c5bSWillem de Bruijn static int  cfg_cpu		= -1;		/* default: pin to last cpu */
8007b65c5bSWillem de Bruijn static int  cfg_family		= PF_UNSPEC;
8107b65c5bSWillem de Bruijn static int  cfg_ifindex		= 1;
8207b65c5bSWillem de Bruijn static int  cfg_payload_len;
8307b65c5bSWillem de Bruijn static int  cfg_port		= 8000;
8407b65c5bSWillem de Bruijn static bool cfg_rx;
8507b65c5bSWillem de Bruijn static int  cfg_runtime_ms	= 4200;
8607b65c5bSWillem de Bruijn static int  cfg_verbose;
8707b65c5bSWillem de Bruijn static int  cfg_waittime_ms	= 500;
88af2b7e5bSZijian Zhang static int  cfg_notification_limit = 32;
8907b65c5bSWillem de Bruijn static bool cfg_zerocopy;
9007b65c5bSWillem de Bruijn 
9107b65c5bSWillem de Bruijn static socklen_t cfg_alen;
9207b65c5bSWillem de Bruijn static struct sockaddr_storage cfg_dst_addr;
9307b65c5bSWillem de Bruijn static struct sockaddr_storage cfg_src_addr;
9407b65c5bSWillem de Bruijn 
9507b65c5bSWillem de Bruijn static char payload[IP_MAXPACKET];
9607b65c5bSWillem de Bruijn static long packets, bytes, completions, expected_completions;
9707b65c5bSWillem de Bruijn static int  zerocopied = -1;
9807b65c5bSWillem de Bruijn static uint32_t next_completion;
99af2b7e5bSZijian Zhang static uint32_t sends_since_notify;
10007b65c5bSWillem de Bruijn 
gettimeofday_ms(void)10107b65c5bSWillem de Bruijn static unsigned long gettimeofday_ms(void)
10207b65c5bSWillem de Bruijn {
10307b65c5bSWillem de Bruijn 	struct timeval tv;
10407b65c5bSWillem de Bruijn 
10507b65c5bSWillem de Bruijn 	gettimeofday(&tv, NULL);
10607b65c5bSWillem de Bruijn 	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
10707b65c5bSWillem de Bruijn }
10807b65c5bSWillem de Bruijn 
get_ip_csum(const uint16_t * start,int num_words)10907b65c5bSWillem de Bruijn static uint16_t get_ip_csum(const uint16_t *start, int num_words)
11007b65c5bSWillem de Bruijn {
11107b65c5bSWillem de Bruijn 	unsigned long sum = 0;
11207b65c5bSWillem de Bruijn 	int i;
11307b65c5bSWillem de Bruijn 
11407b65c5bSWillem de Bruijn 	for (i = 0; i < num_words; i++)
11507b65c5bSWillem de Bruijn 		sum += start[i];
11607b65c5bSWillem de Bruijn 
11707b65c5bSWillem de Bruijn 	while (sum >> 16)
11807b65c5bSWillem de Bruijn 		sum = (sum & 0xFFFF) + (sum >> 16);
11907b65c5bSWillem de Bruijn 
12007b65c5bSWillem de Bruijn 	return ~sum;
12107b65c5bSWillem de Bruijn }
12207b65c5bSWillem de Bruijn 
do_setcpu(int cpu)12307b65c5bSWillem de Bruijn static int do_setcpu(int cpu)
12407b65c5bSWillem de Bruijn {
12507b65c5bSWillem de Bruijn 	cpu_set_t mask;
12607b65c5bSWillem de Bruijn 
12707b65c5bSWillem de Bruijn 	CPU_ZERO(&mask);
12807b65c5bSWillem de Bruijn 	CPU_SET(cpu, &mask);
12907b65c5bSWillem de Bruijn 	if (sched_setaffinity(0, sizeof(mask), &mask))
13016f6458fSWillem de Bruijn 		fprintf(stderr, "cpu: unable to pin, may increase variance.\n");
13116f6458fSWillem de Bruijn 	else if (cfg_verbose)
13207b65c5bSWillem de Bruijn 		fprintf(stderr, "cpu: %u\n", cpu);
13307b65c5bSWillem de Bruijn 
13407b65c5bSWillem de Bruijn 	return 0;
13507b65c5bSWillem de Bruijn }
13607b65c5bSWillem de Bruijn 
do_setsockopt(int fd,int level,int optname,int val)13707b65c5bSWillem de Bruijn static void do_setsockopt(int fd, int level, int optname, int val)
13807b65c5bSWillem de Bruijn {
13907b65c5bSWillem de Bruijn 	if (setsockopt(fd, level, optname, &val, sizeof(val)))
14007b65c5bSWillem de Bruijn 		error(1, errno, "setsockopt %d.%d: %d", level, optname, val);
14107b65c5bSWillem de Bruijn }
14207b65c5bSWillem de Bruijn 
do_poll(int fd,int events)14307b65c5bSWillem de Bruijn static int do_poll(int fd, int events)
14407b65c5bSWillem de Bruijn {
14507b65c5bSWillem de Bruijn 	struct pollfd pfd;
14607b65c5bSWillem de Bruijn 	int ret;
14707b65c5bSWillem de Bruijn 
14807b65c5bSWillem de Bruijn 	pfd.events = events;
14907b65c5bSWillem de Bruijn 	pfd.revents = 0;
15007b65c5bSWillem de Bruijn 	pfd.fd = fd;
15107b65c5bSWillem de Bruijn 
15207b65c5bSWillem de Bruijn 	ret = poll(&pfd, 1, cfg_waittime_ms);
15307b65c5bSWillem de Bruijn 	if (ret == -1)
15407b65c5bSWillem de Bruijn 		error(1, errno, "poll");
15507b65c5bSWillem de Bruijn 
15607b65c5bSWillem de Bruijn 	return ret && (pfd.revents & events);
15707b65c5bSWillem de Bruijn }
15807b65c5bSWillem de Bruijn 
do_accept(int fd)15907b65c5bSWillem de Bruijn static int do_accept(int fd)
16007b65c5bSWillem de Bruijn {
16107b65c5bSWillem de Bruijn 	int fda = fd;
16207b65c5bSWillem de Bruijn 
16307b65c5bSWillem de Bruijn 	fd = accept(fda, NULL, NULL);
16407b65c5bSWillem de Bruijn 	if (fd == -1)
16507b65c5bSWillem de Bruijn 		error(1, errno, "accept");
16607b65c5bSWillem de Bruijn 	if (close(fda))
16707b65c5bSWillem de Bruijn 		error(1, errno, "close listen sock");
16807b65c5bSWillem de Bruijn 
16907b65c5bSWillem de Bruijn 	return fd;
17007b65c5bSWillem de Bruijn }
17107b65c5bSWillem de Bruijn 
add_zcopy_cookie(struct msghdr * msg,uint32_t cookie)172dfb8434bSSowmini Varadhan static void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie)
173dfb8434bSSowmini Varadhan {
174dfb8434bSSowmini Varadhan 	struct cmsghdr *cm;
175dfb8434bSSowmini Varadhan 
176dfb8434bSSowmini Varadhan 	if (!msg->msg_control)
177dfb8434bSSowmini Varadhan 		error(1, errno, "NULL cookie");
178dfb8434bSSowmini Varadhan 	cm = (void *)msg->msg_control;
179dfb8434bSSowmini Varadhan 	cm->cmsg_len = CMSG_LEN(sizeof(cookie));
180dfb8434bSSowmini Varadhan 	cm->cmsg_level = SOL_RDS;
181dfb8434bSSowmini Varadhan 	cm->cmsg_type = RDS_CMSG_ZCOPY_COOKIE;
182dfb8434bSSowmini Varadhan 	memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie));
183dfb8434bSSowmini Varadhan }
184dfb8434bSSowmini Varadhan 
do_sendmsg(int fd,struct msghdr * msg,bool do_zerocopy,int domain)185dfb8434bSSowmini Varadhan static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
18607b65c5bSWillem de Bruijn {
18707b65c5bSWillem de Bruijn 	int ret, len, i, flags;
188dfb8434bSSowmini Varadhan 	static uint32_t cookie;
189dfb8434bSSowmini Varadhan 	char ckbuf[CMSG_SPACE(sizeof(cookie))];
19007b65c5bSWillem de Bruijn 
19107b65c5bSWillem de Bruijn 	len = 0;
19207b65c5bSWillem de Bruijn 	for (i = 0; i < msg->msg_iovlen; i++)
19307b65c5bSWillem de Bruijn 		len += msg->msg_iov[i].iov_len;
19407b65c5bSWillem de Bruijn 
19507b65c5bSWillem de Bruijn 	flags = MSG_DONTWAIT;
196dfb8434bSSowmini Varadhan 	if (do_zerocopy) {
19707b65c5bSWillem de Bruijn 		flags |= MSG_ZEROCOPY;
198dfb8434bSSowmini Varadhan 		if (domain == PF_RDS) {
199dfb8434bSSowmini Varadhan 			memset(&msg->msg_control, 0, sizeof(msg->msg_control));
200dfb8434bSSowmini Varadhan 			msg->msg_controllen = CMSG_SPACE(sizeof(cookie));
201dfb8434bSSowmini Varadhan 			msg->msg_control = (struct cmsghdr *)ckbuf;
202dfb8434bSSowmini Varadhan 			add_zcopy_cookie(msg, ++cookie);
203dfb8434bSSowmini Varadhan 		}
204dfb8434bSSowmini Varadhan 	}
20507b65c5bSWillem de Bruijn 
20607b65c5bSWillem de Bruijn 	ret = sendmsg(fd, msg, flags);
20707b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
20807b65c5bSWillem de Bruijn 		return false;
20907b65c5bSWillem de Bruijn 	if (ret == -1)
21007b65c5bSWillem de Bruijn 		error(1, errno, "send");
21107b65c5bSWillem de Bruijn 	if (cfg_verbose && ret != len)
21207b65c5bSWillem de Bruijn 		fprintf(stderr, "send: ret=%u != %u\n", ret, len);
213af2b7e5bSZijian Zhang 	sends_since_notify++;
21407b65c5bSWillem de Bruijn 
21507b65c5bSWillem de Bruijn 	if (len) {
21607b65c5bSWillem de Bruijn 		packets++;
21707b65c5bSWillem de Bruijn 		bytes += ret;
21807b65c5bSWillem de Bruijn 		if (do_zerocopy && ret)
21907b65c5bSWillem de Bruijn 			expected_completions++;
22007b65c5bSWillem de Bruijn 	}
221dfb8434bSSowmini Varadhan 	if (do_zerocopy && domain == PF_RDS) {
222dfb8434bSSowmini Varadhan 		msg->msg_control = NULL;
223dfb8434bSSowmini Varadhan 		msg->msg_controllen = 0;
224dfb8434bSSowmini Varadhan 	}
22507b65c5bSWillem de Bruijn 
22607b65c5bSWillem de Bruijn 	return true;
22707b65c5bSWillem de Bruijn }
22807b65c5bSWillem de Bruijn 
do_sendmsg_corked(int fd,struct msghdr * msg)22907b65c5bSWillem de Bruijn static void do_sendmsg_corked(int fd, struct msghdr *msg)
23007b65c5bSWillem de Bruijn {
23107b65c5bSWillem de Bruijn 	bool do_zerocopy = cfg_zerocopy;
23207b65c5bSWillem de Bruijn 	int i, payload_len, extra_len;
23307b65c5bSWillem de Bruijn 
23407b65c5bSWillem de Bruijn 	/* split up the packet. for non-multiple, make first buffer longer */
23507b65c5bSWillem de Bruijn 	payload_len = cfg_payload_len / cfg_cork;
23607b65c5bSWillem de Bruijn 	extra_len = cfg_payload_len - (cfg_cork * payload_len);
23707b65c5bSWillem de Bruijn 
23807b65c5bSWillem de Bruijn 	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 1);
23907b65c5bSWillem de Bruijn 
24007b65c5bSWillem de Bruijn 	for (i = 0; i < cfg_cork; i++) {
24107b65c5bSWillem de Bruijn 
24207b65c5bSWillem de Bruijn 		/* in mixed-frags mode, alternate zerocopy and copy frags
24307b65c5bSWillem de Bruijn 		 * start with non-zerocopy, to ensure attach later works
24407b65c5bSWillem de Bruijn 		 */
24507b65c5bSWillem de Bruijn 		if (cfg_cork_mixed)
24607b65c5bSWillem de Bruijn 			do_zerocopy = (i & 1);
24707b65c5bSWillem de Bruijn 
24807b65c5bSWillem de Bruijn 		msg->msg_iov[0].iov_len = payload_len + extra_len;
24907b65c5bSWillem de Bruijn 		extra_len = 0;
25007b65c5bSWillem de Bruijn 
251dfb8434bSSowmini Varadhan 		do_sendmsg(fd, msg, do_zerocopy,
252dfb8434bSSowmini Varadhan 			   (cfg_dst_addr.ss_family == AF_INET ?
253dfb8434bSSowmini Varadhan 			    PF_INET : PF_INET6));
25407b65c5bSWillem de Bruijn 	}
25507b65c5bSWillem de Bruijn 
25607b65c5bSWillem de Bruijn 	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0);
25707b65c5bSWillem de Bruijn }
25807b65c5bSWillem de Bruijn 
setup_iph(struct iphdr * iph,uint16_t payload_len)25907b65c5bSWillem de Bruijn static int setup_iph(struct iphdr *iph, uint16_t payload_len)
26007b65c5bSWillem de Bruijn {
26107b65c5bSWillem de Bruijn 	struct sockaddr_in *daddr = (void *) &cfg_dst_addr;
26207b65c5bSWillem de Bruijn 	struct sockaddr_in *saddr = (void *) &cfg_src_addr;
26307b65c5bSWillem de Bruijn 
26407b65c5bSWillem de Bruijn 	memset(iph, 0, sizeof(*iph));
26507b65c5bSWillem de Bruijn 
26607b65c5bSWillem de Bruijn 	iph->version	= 4;
26707b65c5bSWillem de Bruijn 	iph->tos	= 0;
26807b65c5bSWillem de Bruijn 	iph->ihl	= 5;
26907b65c5bSWillem de Bruijn 	iph->ttl	= 2;
27007b65c5bSWillem de Bruijn 	iph->saddr	= saddr->sin_addr.s_addr;
27107b65c5bSWillem de Bruijn 	iph->daddr	= daddr->sin_addr.s_addr;
27207b65c5bSWillem de Bruijn 	iph->protocol	= IPPROTO_EGP;
27307b65c5bSWillem de Bruijn 	iph->tot_len	= htons(sizeof(*iph) + payload_len);
27407b65c5bSWillem de Bruijn 	iph->check	= get_ip_csum((void *) iph, iph->ihl << 1);
27507b65c5bSWillem de Bruijn 
27607b65c5bSWillem de Bruijn 	return sizeof(*iph);
27707b65c5bSWillem de Bruijn }
27807b65c5bSWillem de Bruijn 
setup_ip6h(struct ipv6hdr * ip6h,uint16_t payload_len)27907b65c5bSWillem de Bruijn static int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len)
28007b65c5bSWillem de Bruijn {
28107b65c5bSWillem de Bruijn 	struct sockaddr_in6 *daddr = (void *) &cfg_dst_addr;
28207b65c5bSWillem de Bruijn 	struct sockaddr_in6 *saddr = (void *) &cfg_src_addr;
28307b65c5bSWillem de Bruijn 
28407b65c5bSWillem de Bruijn 	memset(ip6h, 0, sizeof(*ip6h));
28507b65c5bSWillem de Bruijn 
28607b65c5bSWillem de Bruijn 	ip6h->version		= 6;
28707b65c5bSWillem de Bruijn 	ip6h->payload_len	= htons(payload_len);
28807b65c5bSWillem de Bruijn 	ip6h->nexthdr		= IPPROTO_EGP;
28907b65c5bSWillem de Bruijn 	ip6h->hop_limit		= 2;
29007b65c5bSWillem de Bruijn 	ip6h->saddr		= saddr->sin6_addr;
29107b65c5bSWillem de Bruijn 	ip6h->daddr		= daddr->sin6_addr;
29207b65c5bSWillem de Bruijn 
29307b65c5bSWillem de Bruijn 	return sizeof(*ip6h);
29407b65c5bSWillem de Bruijn }
29507b65c5bSWillem de Bruijn 
296d36f45e5SSowmini Varadhan 
setup_sockaddr(int domain,const char * str_addr,struct sockaddr_storage * sockaddr)297d36f45e5SSowmini Varadhan static void setup_sockaddr(int domain, const char *str_addr,
298d36f45e5SSowmini Varadhan 			   struct sockaddr_storage *sockaddr)
29907b65c5bSWillem de Bruijn {
30007b65c5bSWillem de Bruijn 	struct sockaddr_in6 *addr6 = (void *) sockaddr;
30107b65c5bSWillem de Bruijn 	struct sockaddr_in *addr4 = (void *) sockaddr;
30207b65c5bSWillem de Bruijn 
30307b65c5bSWillem de Bruijn 	switch (domain) {
30407b65c5bSWillem de Bruijn 	case PF_INET:
305d36f45e5SSowmini Varadhan 		memset(addr4, 0, sizeof(*addr4));
30607b65c5bSWillem de Bruijn 		addr4->sin_family = AF_INET;
30707b65c5bSWillem de Bruijn 		addr4->sin_port = htons(cfg_port);
308d36f45e5SSowmini Varadhan 		if (str_addr &&
309d36f45e5SSowmini Varadhan 		    inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
31007b65c5bSWillem de Bruijn 			error(1, 0, "ipv4 parse error: %s", str_addr);
31107b65c5bSWillem de Bruijn 		break;
31207b65c5bSWillem de Bruijn 	case PF_INET6:
313d36f45e5SSowmini Varadhan 		memset(addr6, 0, sizeof(*addr6));
31407b65c5bSWillem de Bruijn 		addr6->sin6_family = AF_INET6;
31507b65c5bSWillem de Bruijn 		addr6->sin6_port = htons(cfg_port);
316d36f45e5SSowmini Varadhan 		if (str_addr &&
317d36f45e5SSowmini Varadhan 		    inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
31807b65c5bSWillem de Bruijn 			error(1, 0, "ipv6 parse error: %s", str_addr);
31907b65c5bSWillem de Bruijn 		break;
32007b65c5bSWillem de Bruijn 	default:
32107b65c5bSWillem de Bruijn 		error(1, 0, "illegal domain");
32207b65c5bSWillem de Bruijn 	}
32307b65c5bSWillem de Bruijn }
32407b65c5bSWillem de Bruijn 
do_setup_tx(int domain,int type,int protocol)32507b65c5bSWillem de Bruijn static int do_setup_tx(int domain, int type, int protocol)
32607b65c5bSWillem de Bruijn {
32707b65c5bSWillem de Bruijn 	int fd;
32807b65c5bSWillem de Bruijn 
32907b65c5bSWillem de Bruijn 	fd = socket(domain, type, protocol);
33007b65c5bSWillem de Bruijn 	if (fd == -1)
33107b65c5bSWillem de Bruijn 		error(1, errno, "socket t");
33207b65c5bSWillem de Bruijn 
33307b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 1 << 21);
33407b65c5bSWillem de Bruijn 	if (cfg_zerocopy)
33507b65c5bSWillem de Bruijn 		do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1);
33607b65c5bSWillem de Bruijn 
337b16ac920SSowmini Varadhan 	if (domain != PF_PACKET && domain != PF_RDS)
33807b65c5bSWillem de Bruijn 		if (connect(fd, (void *) &cfg_dst_addr, cfg_alen))
33907b65c5bSWillem de Bruijn 			error(1, errno, "connect");
34007b65c5bSWillem de Bruijn 
341b16ac920SSowmini Varadhan 	if (domain == PF_RDS) {
342b16ac920SSowmini Varadhan 		if (bind(fd, (void *) &cfg_src_addr, cfg_alen))
343b16ac920SSowmini Varadhan 			error(1, errno, "bind");
344b16ac920SSowmini Varadhan 	}
345b16ac920SSowmini Varadhan 
34607b65c5bSWillem de Bruijn 	return fd;
34707b65c5bSWillem de Bruijn }
34807b65c5bSWillem de Bruijn 
do_process_zerocopy_cookies(struct rds_zcopy_cookies * ck)3496f3899e6SSowmini Varadhan static uint32_t do_process_zerocopy_cookies(struct rds_zcopy_cookies *ck)
3506f3899e6SSowmini Varadhan {
3516f3899e6SSowmini Varadhan 	int i;
3526f3899e6SSowmini Varadhan 
3536f3899e6SSowmini Varadhan 	if (ck->num > RDS_MAX_ZCOOKIES)
3546f3899e6SSowmini Varadhan 		error(1, 0, "Returned %d cookies, max expected %d\n",
3556f3899e6SSowmini Varadhan 		      ck->num, RDS_MAX_ZCOOKIES);
3566f3899e6SSowmini Varadhan 	for (i = 0; i < ck->num; i++)
3576f3899e6SSowmini Varadhan 		if (cfg_verbose >= 2)
3586f3899e6SSowmini Varadhan 			fprintf(stderr, "%d\n", ck->cookies[i]);
3596f3899e6SSowmini Varadhan 	return ck->num;
3606f3899e6SSowmini Varadhan }
3616f3899e6SSowmini Varadhan 
do_recvmsg_completion(int fd)3626f3899e6SSowmini Varadhan static bool do_recvmsg_completion(int fd)
3636f3899e6SSowmini Varadhan {
3646f3899e6SSowmini Varadhan 	char cmsgbuf[CMSG_SPACE(sizeof(struct rds_zcopy_cookies))];
3656f3899e6SSowmini Varadhan 	struct rds_zcopy_cookies *ck;
3666f3899e6SSowmini Varadhan 	struct cmsghdr *cmsg;
3676f3899e6SSowmini Varadhan 	struct msghdr msg;
3686f3899e6SSowmini Varadhan 	bool ret = false;
3696f3899e6SSowmini Varadhan 
3706f3899e6SSowmini Varadhan 	memset(&msg, 0, sizeof(msg));
3716f3899e6SSowmini Varadhan 	msg.msg_control = cmsgbuf;
3726f3899e6SSowmini Varadhan 	msg.msg_controllen = sizeof(cmsgbuf);
3736f3899e6SSowmini Varadhan 
3746f3899e6SSowmini Varadhan 	if (recvmsg(fd, &msg, MSG_DONTWAIT))
3756f3899e6SSowmini Varadhan 		return ret;
3766f3899e6SSowmini Varadhan 
3776f3899e6SSowmini Varadhan 	if (msg.msg_flags & MSG_CTRUNC)
3786f3899e6SSowmini Varadhan 		error(1, errno, "recvmsg notification: truncated");
3796f3899e6SSowmini Varadhan 
3806f3899e6SSowmini Varadhan 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
3816f3899e6SSowmini Varadhan 		if (cmsg->cmsg_level == SOL_RDS &&
3826f3899e6SSowmini Varadhan 		    cmsg->cmsg_type == RDS_CMSG_ZCOPY_COMPLETION) {
3836f3899e6SSowmini Varadhan 
3846f3899e6SSowmini Varadhan 			ck = (struct rds_zcopy_cookies *)CMSG_DATA(cmsg);
3856f3899e6SSowmini Varadhan 			completions += do_process_zerocopy_cookies(ck);
3866f3899e6SSowmini Varadhan 			ret = true;
3876f3899e6SSowmini Varadhan 			break;
3886f3899e6SSowmini Varadhan 		}
3896f3899e6SSowmini Varadhan 		error(0, 0, "ignoring cmsg at level %d type %d\n",
3906f3899e6SSowmini Varadhan 			    cmsg->cmsg_level, cmsg->cmsg_type);
3916f3899e6SSowmini Varadhan 	}
3926f3899e6SSowmini Varadhan 	return ret;
3936f3899e6SSowmini Varadhan }
3946f3899e6SSowmini Varadhan 
do_recv_completion(int fd,int domain)3956f3899e6SSowmini Varadhan static bool do_recv_completion(int fd, int domain)
39607b65c5bSWillem de Bruijn {
39707b65c5bSWillem de Bruijn 	struct sock_extended_err *serr;
39807b65c5bSWillem de Bruijn 	struct msghdr msg = {};
39907b65c5bSWillem de Bruijn 	struct cmsghdr *cm;
40007b65c5bSWillem de Bruijn 	uint32_t hi, lo, range;
40107b65c5bSWillem de Bruijn 	int ret, zerocopy;
40207b65c5bSWillem de Bruijn 	char control[100];
40307b65c5bSWillem de Bruijn 
4046f3899e6SSowmini Varadhan 	if (domain == PF_RDS)
4056f3899e6SSowmini Varadhan 		return do_recvmsg_completion(fd);
4066f3899e6SSowmini Varadhan 
40707b65c5bSWillem de Bruijn 	msg.msg_control = control;
40807b65c5bSWillem de Bruijn 	msg.msg_controllen = sizeof(control);
40907b65c5bSWillem de Bruijn 
41007b65c5bSWillem de Bruijn 	ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
41107b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
41207b65c5bSWillem de Bruijn 		return false;
41307b65c5bSWillem de Bruijn 	if (ret == -1)
41407b65c5bSWillem de Bruijn 		error(1, errno, "recvmsg notification");
41507b65c5bSWillem de Bruijn 	if (msg.msg_flags & MSG_CTRUNC)
41607b65c5bSWillem de Bruijn 		error(1, errno, "recvmsg notification: truncated");
41707b65c5bSWillem de Bruijn 
41807b65c5bSWillem de Bruijn 	cm = CMSG_FIRSTHDR(&msg);
41907b65c5bSWillem de Bruijn 	if (!cm)
42007b65c5bSWillem de Bruijn 		error(1, 0, "cmsg: no cmsg");
42107b65c5bSWillem de Bruijn 	if (!((cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) ||
42207b65c5bSWillem de Bruijn 	      (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_RECVERR) ||
42307b65c5bSWillem de Bruijn 	      (cm->cmsg_level == SOL_PACKET && cm->cmsg_type == PACKET_TX_TIMESTAMP)))
42407b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong type: %d.%d",
42507b65c5bSWillem de Bruijn 		      cm->cmsg_level, cm->cmsg_type);
42607b65c5bSWillem de Bruijn 
42707b65c5bSWillem de Bruijn 	serr = (void *) CMSG_DATA(cm);
428dfb8434bSSowmini Varadhan 
42907b65c5bSWillem de Bruijn 	if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
43007b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong origin: %u", serr->ee_origin);
43107b65c5bSWillem de Bruijn 	if (serr->ee_errno != 0)
43207b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong error code: %u", serr->ee_errno);
43307b65c5bSWillem de Bruijn 
43407b65c5bSWillem de Bruijn 	hi = serr->ee_data;
43507b65c5bSWillem de Bruijn 	lo = serr->ee_info;
43607b65c5bSWillem de Bruijn 	range = hi - lo + 1;
43707b65c5bSWillem de Bruijn 
43807b65c5bSWillem de Bruijn 	/* Detect notification gaps. These should not happen often, if at all.
43907b65c5bSWillem de Bruijn 	 * Gaps can occur due to drops, reordering and retransmissions.
44007b65c5bSWillem de Bruijn 	 */
441*7d6d8f0cSZijian Zhang 	if (cfg_verbose && lo != next_completion)
44207b65c5bSWillem de Bruijn 		fprintf(stderr, "gap: %u..%u does not append to %u\n",
44307b65c5bSWillem de Bruijn 			lo, hi, next_completion);
44407b65c5bSWillem de Bruijn 	next_completion = hi + 1;
44507b65c5bSWillem de Bruijn 
44607b65c5bSWillem de Bruijn 	zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED);
44707b65c5bSWillem de Bruijn 	if (zerocopied == -1)
44807b65c5bSWillem de Bruijn 		zerocopied = zerocopy;
44907b65c5bSWillem de Bruijn 	else if (zerocopied != zerocopy) {
45007b65c5bSWillem de Bruijn 		fprintf(stderr, "serr: inconsistent\n");
45107b65c5bSWillem de Bruijn 		zerocopied = zerocopy;
45207b65c5bSWillem de Bruijn 	}
45307b65c5bSWillem de Bruijn 
45407b65c5bSWillem de Bruijn 	if (cfg_verbose >= 2)
45507b65c5bSWillem de Bruijn 		fprintf(stderr, "completed: %u (h=%u l=%u)\n",
45607b65c5bSWillem de Bruijn 			range, hi, lo);
45707b65c5bSWillem de Bruijn 
45807b65c5bSWillem de Bruijn 	completions += range;
45907b65c5bSWillem de Bruijn 	return true;
46007b65c5bSWillem de Bruijn }
46107b65c5bSWillem de Bruijn 
46207b65c5bSWillem de Bruijn /* Read all outstanding messages on the errqueue */
do_recv_completions(int fd,int domain)4636f3899e6SSowmini Varadhan static void do_recv_completions(int fd, int domain)
46407b65c5bSWillem de Bruijn {
4656f3899e6SSowmini Varadhan 	while (do_recv_completion(fd, domain)) {}
466af2b7e5bSZijian Zhang 	sends_since_notify = 0;
46707b65c5bSWillem de Bruijn }
46807b65c5bSWillem de Bruijn 
46907b65c5bSWillem de Bruijn /* Wait for all remaining completions on the errqueue */
do_recv_remaining_completions(int fd,int domain)4706f3899e6SSowmini Varadhan static void do_recv_remaining_completions(int fd, int domain)
47107b65c5bSWillem de Bruijn {
47207b65c5bSWillem de Bruijn 	int64_t tstop = gettimeofday_ms() + cfg_waittime_ms;
47307b65c5bSWillem de Bruijn 
47407b65c5bSWillem de Bruijn 	while (completions < expected_completions &&
47507b65c5bSWillem de Bruijn 	       gettimeofday_ms() < tstop) {
4766f3899e6SSowmini Varadhan 		if (do_poll(fd, domain == PF_RDS ? POLLIN : POLLERR))
4776f3899e6SSowmini Varadhan 			do_recv_completions(fd, domain);
47807b65c5bSWillem de Bruijn 	}
47907b65c5bSWillem de Bruijn 
48007b65c5bSWillem de Bruijn 	if (completions < expected_completions)
481bbd9644eSWillem de Bruijn 		fprintf(stderr, "missing notifications: %lu < %lu\n",
48207b65c5bSWillem de Bruijn 			completions, expected_completions);
48307b65c5bSWillem de Bruijn }
48407b65c5bSWillem de Bruijn 
do_tx(int domain,int type,int protocol)48507b65c5bSWillem de Bruijn static void do_tx(int domain, int type, int protocol)
48607b65c5bSWillem de Bruijn {
48707b65c5bSWillem de Bruijn 	struct iovec iov[3] = { {0} };
48807b65c5bSWillem de Bruijn 	struct sockaddr_ll laddr;
48907b65c5bSWillem de Bruijn 	struct msghdr msg = {0};
49007b65c5bSWillem de Bruijn 	struct ethhdr eth;
49107b65c5bSWillem de Bruijn 	union {
49207b65c5bSWillem de Bruijn 		struct ipv6hdr ip6h;
49307b65c5bSWillem de Bruijn 		struct iphdr iph;
49407b65c5bSWillem de Bruijn 	} nh;
49507b65c5bSWillem de Bruijn 	uint64_t tstop;
49607b65c5bSWillem de Bruijn 	int fd;
49707b65c5bSWillem de Bruijn 
49807b65c5bSWillem de Bruijn 	fd = do_setup_tx(domain, type, protocol);
49907b65c5bSWillem de Bruijn 
50007b65c5bSWillem de Bruijn 	if (domain == PF_PACKET) {
50107b65c5bSWillem de Bruijn 		uint16_t proto = cfg_family == PF_INET ? ETH_P_IP : ETH_P_IPV6;
50207b65c5bSWillem de Bruijn 
50307b65c5bSWillem de Bruijn 		/* sock_raw passes ll header as data */
50407b65c5bSWillem de Bruijn 		if (type == SOCK_RAW) {
50507b65c5bSWillem de Bruijn 			memset(eth.h_dest, 0x06, ETH_ALEN);
50607b65c5bSWillem de Bruijn 			memset(eth.h_source, 0x02, ETH_ALEN);
50707b65c5bSWillem de Bruijn 			eth.h_proto = htons(proto);
50807b65c5bSWillem de Bruijn 			iov[0].iov_base = &eth;
50907b65c5bSWillem de Bruijn 			iov[0].iov_len = sizeof(eth);
51007b65c5bSWillem de Bruijn 			msg.msg_iovlen++;
51107b65c5bSWillem de Bruijn 		}
51207b65c5bSWillem de Bruijn 
51307b65c5bSWillem de Bruijn 		/* both sock_raw and sock_dgram expect name */
51407b65c5bSWillem de Bruijn 		memset(&laddr, 0, sizeof(laddr));
51507b65c5bSWillem de Bruijn 		laddr.sll_family	= AF_PACKET;
51607b65c5bSWillem de Bruijn 		laddr.sll_ifindex	= cfg_ifindex;
51707b65c5bSWillem de Bruijn 		laddr.sll_protocol	= htons(proto);
51807b65c5bSWillem de Bruijn 		laddr.sll_halen		= ETH_ALEN;
51907b65c5bSWillem de Bruijn 
52007b65c5bSWillem de Bruijn 		memset(laddr.sll_addr, 0x06, ETH_ALEN);
52107b65c5bSWillem de Bruijn 
52207b65c5bSWillem de Bruijn 		msg.msg_name		= &laddr;
52307b65c5bSWillem de Bruijn 		msg.msg_namelen		= sizeof(laddr);
52407b65c5bSWillem de Bruijn 	}
52507b65c5bSWillem de Bruijn 
52607b65c5bSWillem de Bruijn 	/* packet and raw sockets with hdrincl must pass network header */
52707b65c5bSWillem de Bruijn 	if (domain == PF_PACKET || protocol == IPPROTO_RAW) {
52807b65c5bSWillem de Bruijn 		if (cfg_family == PF_INET)
52907b65c5bSWillem de Bruijn 			iov[1].iov_len = setup_iph(&nh.iph, cfg_payload_len);
53007b65c5bSWillem de Bruijn 		else
53107b65c5bSWillem de Bruijn 			iov[1].iov_len = setup_ip6h(&nh.ip6h, cfg_payload_len);
53207b65c5bSWillem de Bruijn 
53307b65c5bSWillem de Bruijn 		iov[1].iov_base = (void *) &nh;
53407b65c5bSWillem de Bruijn 		msg.msg_iovlen++;
53507b65c5bSWillem de Bruijn 	}
53607b65c5bSWillem de Bruijn 
537b16ac920SSowmini Varadhan 	if (domain == PF_RDS) {
538b16ac920SSowmini Varadhan 		msg.msg_name = &cfg_dst_addr;
539b16ac920SSowmini Varadhan 		msg.msg_namelen =  (cfg_dst_addr.ss_family == AF_INET ?
540b16ac920SSowmini Varadhan 				    sizeof(struct sockaddr_in) :
541b16ac920SSowmini Varadhan 				    sizeof(struct sockaddr_in6));
542b16ac920SSowmini Varadhan 	}
543b16ac920SSowmini Varadhan 
54407b65c5bSWillem de Bruijn 	iov[2].iov_base = payload;
54507b65c5bSWillem de Bruijn 	iov[2].iov_len = cfg_payload_len;
54607b65c5bSWillem de Bruijn 	msg.msg_iovlen++;
54707b65c5bSWillem de Bruijn 	msg.msg_iov = &iov[3 - msg.msg_iovlen];
54807b65c5bSWillem de Bruijn 
54907b65c5bSWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_runtime_ms;
55007b65c5bSWillem de Bruijn 	do {
55107b65c5bSWillem de Bruijn 		if (cfg_cork)
55207b65c5bSWillem de Bruijn 			do_sendmsg_corked(fd, &msg);
55307b65c5bSWillem de Bruijn 		else
554dfb8434bSSowmini Varadhan 			do_sendmsg(fd, &msg, cfg_zerocopy, domain);
55507b65c5bSWillem de Bruijn 
556af2b7e5bSZijian Zhang 		if (cfg_zerocopy && sends_since_notify >= cfg_notification_limit)
557af2b7e5bSZijian Zhang 			do_recv_completions(fd, domain);
558af2b7e5bSZijian Zhang 
55907b65c5bSWillem de Bruijn 		while (!do_poll(fd, POLLOUT)) {
56007b65c5bSWillem de Bruijn 			if (cfg_zerocopy)
5616f3899e6SSowmini Varadhan 				do_recv_completions(fd, domain);
56207b65c5bSWillem de Bruijn 		}
56307b65c5bSWillem de Bruijn 
56407b65c5bSWillem de Bruijn 	} while (gettimeofday_ms() < tstop);
56507b65c5bSWillem de Bruijn 
56607b65c5bSWillem de Bruijn 	if (cfg_zerocopy)
5676f3899e6SSowmini Varadhan 		do_recv_remaining_completions(fd, domain);
56807b65c5bSWillem de Bruijn 
56907b65c5bSWillem de Bruijn 	if (close(fd))
57007b65c5bSWillem de Bruijn 		error(1, errno, "close");
57107b65c5bSWillem de Bruijn 
57207b65c5bSWillem de Bruijn 	fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n",
57307b65c5bSWillem de Bruijn 		packets, bytes >> 20, completions,
57407b65c5bSWillem de Bruijn 		zerocopied == 1 ? 'y' : 'n');
57507b65c5bSWillem de Bruijn }
57607b65c5bSWillem de Bruijn 
do_setup_rx(int domain,int type,int protocol)57707b65c5bSWillem de Bruijn static int do_setup_rx(int domain, int type, int protocol)
57807b65c5bSWillem de Bruijn {
57907b65c5bSWillem de Bruijn 	int fd;
58007b65c5bSWillem de Bruijn 
58107b65c5bSWillem de Bruijn 	/* If tx over PF_PACKET, rx over PF_INET(6)/SOCK_RAW,
58207b65c5bSWillem de Bruijn 	 * to recv the only copy of the packet, not a clone
58307b65c5bSWillem de Bruijn 	 */
58407b65c5bSWillem de Bruijn 	if (domain == PF_PACKET)
58507b65c5bSWillem de Bruijn 		error(1, 0, "Use PF_INET/SOCK_RAW to read");
58607b65c5bSWillem de Bruijn 
58707b65c5bSWillem de Bruijn 	if (type == SOCK_RAW && protocol == IPPROTO_RAW)
58807b65c5bSWillem de Bruijn 		error(1, 0, "IPPROTO_RAW: not supported on Rx");
58907b65c5bSWillem de Bruijn 
59007b65c5bSWillem de Bruijn 	fd = socket(domain, type, protocol);
59107b65c5bSWillem de Bruijn 	if (fd == -1)
59207b65c5bSWillem de Bruijn 		error(1, errno, "socket r");
59307b65c5bSWillem de Bruijn 
59407b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 1 << 21);
59507b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, 1 << 16);
59607b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
59707b65c5bSWillem de Bruijn 
59807b65c5bSWillem de Bruijn 	if (bind(fd, (void *) &cfg_dst_addr, cfg_alen))
59907b65c5bSWillem de Bruijn 		error(1, errno, "bind");
60007b65c5bSWillem de Bruijn 
60107b65c5bSWillem de Bruijn 	if (type == SOCK_STREAM) {
60207b65c5bSWillem de Bruijn 		if (listen(fd, 1))
60307b65c5bSWillem de Bruijn 			error(1, errno, "listen");
60407b65c5bSWillem de Bruijn 		fd = do_accept(fd);
60507b65c5bSWillem de Bruijn 	}
60607b65c5bSWillem de Bruijn 
60707b65c5bSWillem de Bruijn 	return fd;
60807b65c5bSWillem de Bruijn }
60907b65c5bSWillem de Bruijn 
61007b65c5bSWillem de Bruijn /* Flush all outstanding bytes for the tcp receive queue */
do_flush_tcp(int fd)61107b65c5bSWillem de Bruijn static void do_flush_tcp(int fd)
61207b65c5bSWillem de Bruijn {
61307b65c5bSWillem de Bruijn 	int ret;
61407b65c5bSWillem de Bruijn 
61507b65c5bSWillem de Bruijn 	/* MSG_TRUNC flushes up to len bytes */
61607b65c5bSWillem de Bruijn 	ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
61707b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
61807b65c5bSWillem de Bruijn 		return;
61907b65c5bSWillem de Bruijn 	if (ret == -1)
62007b65c5bSWillem de Bruijn 		error(1, errno, "flush");
62107b65c5bSWillem de Bruijn 	if (!ret)
62207b65c5bSWillem de Bruijn 		return;
62307b65c5bSWillem de Bruijn 
62407b65c5bSWillem de Bruijn 	packets++;
62507b65c5bSWillem de Bruijn 	bytes += ret;
62607b65c5bSWillem de Bruijn }
62707b65c5bSWillem de Bruijn 
62807b65c5bSWillem de Bruijn /* Flush all outstanding datagrams. Verify first few bytes of each. */
do_flush_datagram(int fd,int type)62907b65c5bSWillem de Bruijn static void do_flush_datagram(int fd, int type)
63007b65c5bSWillem de Bruijn {
63107b65c5bSWillem de Bruijn 	int ret, off = 0;
63207b65c5bSWillem de Bruijn 	char buf[64];
63307b65c5bSWillem de Bruijn 
63407b65c5bSWillem de Bruijn 	/* MSG_TRUNC will return full datagram length */
63507b65c5bSWillem de Bruijn 	ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_TRUNC);
63607b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
63707b65c5bSWillem de Bruijn 		return;
63807b65c5bSWillem de Bruijn 
63907b65c5bSWillem de Bruijn 	/* raw ipv4 return with header, raw ipv6 without */
64007b65c5bSWillem de Bruijn 	if (cfg_family == PF_INET && type == SOCK_RAW) {
64107b65c5bSWillem de Bruijn 		off += sizeof(struct iphdr);
64207b65c5bSWillem de Bruijn 		ret -= sizeof(struct iphdr);
64307b65c5bSWillem de Bruijn 	}
64407b65c5bSWillem de Bruijn 
64507b65c5bSWillem de Bruijn 	if (ret == -1)
64607b65c5bSWillem de Bruijn 		error(1, errno, "recv");
64707b65c5bSWillem de Bruijn 	if (ret != cfg_payload_len)
64807b65c5bSWillem de Bruijn 		error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len);
64907b65c5bSWillem de Bruijn 	if (ret > sizeof(buf) - off)
65007b65c5bSWillem de Bruijn 		ret = sizeof(buf) - off;
65107b65c5bSWillem de Bruijn 	if (memcmp(buf + off, payload, ret))
65207b65c5bSWillem de Bruijn 		error(1, 0, "recv: data mismatch");
65307b65c5bSWillem de Bruijn 
65407b65c5bSWillem de Bruijn 	packets++;
65507b65c5bSWillem de Bruijn 	bytes += cfg_payload_len;
65607b65c5bSWillem de Bruijn }
65707b65c5bSWillem de Bruijn 
do_rx(int domain,int type,int protocol)65807b65c5bSWillem de Bruijn static void do_rx(int domain, int type, int protocol)
65907b65c5bSWillem de Bruijn {
660db63e489SWillem de Bruijn 	const int cfg_receiver_wait_ms = 400;
66107b65c5bSWillem de Bruijn 	uint64_t tstop;
66207b65c5bSWillem de Bruijn 	int fd;
66307b65c5bSWillem de Bruijn 
66407b65c5bSWillem de Bruijn 	fd = do_setup_rx(domain, type, protocol);
66507b65c5bSWillem de Bruijn 
666db63e489SWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_runtime_ms + cfg_receiver_wait_ms;
66707b65c5bSWillem de Bruijn 	do {
66807b65c5bSWillem de Bruijn 		if (type == SOCK_STREAM)
66907b65c5bSWillem de Bruijn 			do_flush_tcp(fd);
67007b65c5bSWillem de Bruijn 		else
67107b65c5bSWillem de Bruijn 			do_flush_datagram(fd, type);
67207b65c5bSWillem de Bruijn 
67307b65c5bSWillem de Bruijn 		do_poll(fd, POLLIN);
67407b65c5bSWillem de Bruijn 
67507b65c5bSWillem de Bruijn 	} while (gettimeofday_ms() < tstop);
67607b65c5bSWillem de Bruijn 
67707b65c5bSWillem de Bruijn 	if (close(fd))
67807b65c5bSWillem de Bruijn 		error(1, errno, "close");
67907b65c5bSWillem de Bruijn 
68007b65c5bSWillem de Bruijn 	fprintf(stderr, "rx=%lu (%lu MB)\n", packets, bytes >> 20);
68107b65c5bSWillem de Bruijn }
68207b65c5bSWillem de Bruijn 
do_test(int domain,int type,int protocol)68307b65c5bSWillem de Bruijn static void do_test(int domain, int type, int protocol)
68407b65c5bSWillem de Bruijn {
68507b65c5bSWillem de Bruijn 	int i;
68607b65c5bSWillem de Bruijn 
68707b65c5bSWillem de Bruijn 	if (cfg_cork && (domain == PF_PACKET || type != SOCK_DGRAM))
68807b65c5bSWillem de Bruijn 		error(1, 0, "can only cork udp sockets");
68907b65c5bSWillem de Bruijn 
69007b65c5bSWillem de Bruijn 	do_setcpu(cfg_cpu);
69107b65c5bSWillem de Bruijn 
69207b65c5bSWillem de Bruijn 	for (i = 0; i < IP_MAXPACKET; i++)
69307b65c5bSWillem de Bruijn 		payload[i] = 'a' + (i % 26);
69407b65c5bSWillem de Bruijn 
69507b65c5bSWillem de Bruijn 	if (cfg_rx)
69607b65c5bSWillem de Bruijn 		do_rx(domain, type, protocol);
69707b65c5bSWillem de Bruijn 	else
69807b65c5bSWillem de Bruijn 		do_tx(domain, type, protocol);
69907b65c5bSWillem de Bruijn }
70007b65c5bSWillem de Bruijn 
usage(const char * filepath)70107b65c5bSWillem de Bruijn static void usage(const char *filepath)
70207b65c5bSWillem de Bruijn {
70307b65c5bSWillem de Bruijn 	error(1, 0, "Usage: %s [options] <test>", filepath);
70407b65c5bSWillem de Bruijn }
70507b65c5bSWillem de Bruijn 
parse_opts(int argc,char ** argv)70607b65c5bSWillem de Bruijn static void parse_opts(int argc, char **argv)
70707b65c5bSWillem de Bruijn {
70807b65c5bSWillem de Bruijn 	const int max_payload_len = sizeof(payload) -
70907b65c5bSWillem de Bruijn 				    sizeof(struct ipv6hdr) -
71007b65c5bSWillem de Bruijn 				    sizeof(struct tcphdr) -
71107b65c5bSWillem de Bruijn 				    40 /* max tcp options */;
71207b65c5bSWillem de Bruijn 	int c;
713d36f45e5SSowmini Varadhan 	char *daddr = NULL, *saddr = NULL;
714b16ac920SSowmini Varadhan 	char *cfg_test;
71507b65c5bSWillem de Bruijn 
71607b65c5bSWillem de Bruijn 	cfg_payload_len = max_payload_len;
71707b65c5bSWillem de Bruijn 
718af2b7e5bSZijian Zhang 	while ((c = getopt(argc, argv, "46c:C:D:i:l:mp:rs:S:t:vz")) != -1) {
71907b65c5bSWillem de Bruijn 		switch (c) {
72007b65c5bSWillem de Bruijn 		case '4':
72107b65c5bSWillem de Bruijn 			if (cfg_family != PF_UNSPEC)
72207b65c5bSWillem de Bruijn 				error(1, 0, "Pass one of -4 or -6");
72307b65c5bSWillem de Bruijn 			cfg_family = PF_INET;
72407b65c5bSWillem de Bruijn 			cfg_alen = sizeof(struct sockaddr_in);
72507b65c5bSWillem de Bruijn 			break;
72607b65c5bSWillem de Bruijn 		case '6':
72707b65c5bSWillem de Bruijn 			if (cfg_family != PF_UNSPEC)
72807b65c5bSWillem de Bruijn 				error(1, 0, "Pass one of -4 or -6");
72907b65c5bSWillem de Bruijn 			cfg_family = PF_INET6;
73007b65c5bSWillem de Bruijn 			cfg_alen = sizeof(struct sockaddr_in6);
73107b65c5bSWillem de Bruijn 			break;
73207b65c5bSWillem de Bruijn 		case 'c':
73307b65c5bSWillem de Bruijn 			cfg_cork = strtol(optarg, NULL, 0);
73407b65c5bSWillem de Bruijn 			break;
73507b65c5bSWillem de Bruijn 		case 'C':
73607b65c5bSWillem de Bruijn 			cfg_cpu = strtol(optarg, NULL, 0);
73707b65c5bSWillem de Bruijn 			break;
73807b65c5bSWillem de Bruijn 		case 'D':
739d36f45e5SSowmini Varadhan 			daddr = optarg;
74007b65c5bSWillem de Bruijn 			break;
74107b65c5bSWillem de Bruijn 		case 'i':
74207b65c5bSWillem de Bruijn 			cfg_ifindex = if_nametoindex(optarg);
74307b65c5bSWillem de Bruijn 			if (cfg_ifindex == 0)
74407b65c5bSWillem de Bruijn 				error(1, errno, "invalid iface: %s", optarg);
74507b65c5bSWillem de Bruijn 			break;
746af2b7e5bSZijian Zhang 		case 'l':
747af2b7e5bSZijian Zhang 			cfg_notification_limit = strtoul(optarg, NULL, 0);
748af2b7e5bSZijian Zhang 			break;
74907b65c5bSWillem de Bruijn 		case 'm':
75007b65c5bSWillem de Bruijn 			cfg_cork_mixed = true;
75107b65c5bSWillem de Bruijn 			break;
75207b65c5bSWillem de Bruijn 		case 'p':
753d36f45e5SSowmini Varadhan 			cfg_port = strtoul(optarg, NULL, 0);
75407b65c5bSWillem de Bruijn 			break;
75507b65c5bSWillem de Bruijn 		case 'r':
75607b65c5bSWillem de Bruijn 			cfg_rx = true;
75707b65c5bSWillem de Bruijn 			break;
75807b65c5bSWillem de Bruijn 		case 's':
75907b65c5bSWillem de Bruijn 			cfg_payload_len = strtoul(optarg, NULL, 0);
76007b65c5bSWillem de Bruijn 			break;
76107b65c5bSWillem de Bruijn 		case 'S':
762d36f45e5SSowmini Varadhan 			saddr = optarg;
76307b65c5bSWillem de Bruijn 			break;
76407b65c5bSWillem de Bruijn 		case 't':
76507b65c5bSWillem de Bruijn 			cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000;
76607b65c5bSWillem de Bruijn 			break;
76707b65c5bSWillem de Bruijn 		case 'v':
76807b65c5bSWillem de Bruijn 			cfg_verbose++;
76907b65c5bSWillem de Bruijn 			break;
77007b65c5bSWillem de Bruijn 		case 'z':
77107b65c5bSWillem de Bruijn 			cfg_zerocopy = true;
77207b65c5bSWillem de Bruijn 			break;
77307b65c5bSWillem de Bruijn 		}
77407b65c5bSWillem de Bruijn 	}
775b16ac920SSowmini Varadhan 
776b16ac920SSowmini Varadhan 	cfg_test = argv[argc - 1];
777b16ac920SSowmini Varadhan 	if (strcmp(cfg_test, "rds") == 0) {
778b16ac920SSowmini Varadhan 		if (!daddr)
779b16ac920SSowmini Varadhan 			error(1, 0, "-D <server addr> required for PF_RDS\n");
780b16ac920SSowmini Varadhan 		if (!cfg_rx && !saddr)
781b16ac920SSowmini Varadhan 			error(1, 0, "-S <client addr> required for PF_RDS\n");
782b16ac920SSowmini Varadhan 	}
783d36f45e5SSowmini Varadhan 	setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
784d36f45e5SSowmini Varadhan 	setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
78507b65c5bSWillem de Bruijn 
78607b65c5bSWillem de Bruijn 	if (cfg_payload_len > max_payload_len)
78707b65c5bSWillem de Bruijn 		error(1, 0, "-s: payload exceeds max (%d)", max_payload_len);
78807b65c5bSWillem de Bruijn 	if (cfg_cork_mixed && (!cfg_zerocopy || !cfg_cork))
78907b65c5bSWillem de Bruijn 		error(1, 0, "-m: cork_mixed requires corking and zerocopy");
79007b65c5bSWillem de Bruijn 
79107b65c5bSWillem de Bruijn 	if (optind != argc - 1)
79207b65c5bSWillem de Bruijn 		usage(argv[0]);
79307b65c5bSWillem de Bruijn }
79407b65c5bSWillem de Bruijn 
main(int argc,char ** argv)79507b65c5bSWillem de Bruijn int main(int argc, char **argv)
79607b65c5bSWillem de Bruijn {
79707b65c5bSWillem de Bruijn 	const char *cfg_test;
79807b65c5bSWillem de Bruijn 
79907b65c5bSWillem de Bruijn 	parse_opts(argc, argv);
80007b65c5bSWillem de Bruijn 
80107b65c5bSWillem de Bruijn 	cfg_test = argv[argc - 1];
80207b65c5bSWillem de Bruijn 
80307b65c5bSWillem de Bruijn 	if (!strcmp(cfg_test, "packet"))
80407b65c5bSWillem de Bruijn 		do_test(PF_PACKET, SOCK_RAW, 0);
80507b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "packet_dgram"))
80607b65c5bSWillem de Bruijn 		do_test(PF_PACKET, SOCK_DGRAM, 0);
80707b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "raw"))
80807b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_RAW, IPPROTO_EGP);
80907b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "raw_hdrincl"))
81007b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_RAW, IPPROTO_RAW);
81107b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "tcp"))
81207b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_STREAM, 0);
81307b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "udp"))
81407b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_DGRAM, 0);
815b16ac920SSowmini Varadhan 	else if (!strcmp(cfg_test, "rds"))
816b16ac920SSowmini Varadhan 		do_test(PF_RDS, SOCK_SEQPACKET, 0);
81707b65c5bSWillem de Bruijn 	else
81807b65c5bSWillem de Bruijn 		error(1, 0, "unknown cfg_test %s", cfg_test);
81907b65c5bSWillem de Bruijn 
82007b65c5bSWillem de Bruijn 	return 0;
82107b65c5bSWillem de Bruijn }
822