xref: /linux/tools/testing/selftests/net/msg_zerocopy.c (revision 6f3899e602b0f4ec3d62ff385bb805e7109defa4)
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;
8807b65c5bSWillem de Bruijn static bool cfg_zerocopy;
8907b65c5bSWillem de Bruijn 
9007b65c5bSWillem de Bruijn static socklen_t cfg_alen;
9107b65c5bSWillem de Bruijn static struct sockaddr_storage cfg_dst_addr;
9207b65c5bSWillem de Bruijn static struct sockaddr_storage cfg_src_addr;
9307b65c5bSWillem de Bruijn 
9407b65c5bSWillem de Bruijn static char payload[IP_MAXPACKET];
9507b65c5bSWillem de Bruijn static long packets, bytes, completions, expected_completions;
9607b65c5bSWillem de Bruijn static int  zerocopied = -1;
9707b65c5bSWillem de Bruijn static uint32_t next_completion;
9807b65c5bSWillem de Bruijn 
9907b65c5bSWillem de Bruijn static unsigned long gettimeofday_ms(void)
10007b65c5bSWillem de Bruijn {
10107b65c5bSWillem de Bruijn 	struct timeval tv;
10207b65c5bSWillem de Bruijn 
10307b65c5bSWillem de Bruijn 	gettimeofday(&tv, NULL);
10407b65c5bSWillem de Bruijn 	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
10507b65c5bSWillem de Bruijn }
10607b65c5bSWillem de Bruijn 
10707b65c5bSWillem de Bruijn static uint16_t get_ip_csum(const uint16_t *start, int num_words)
10807b65c5bSWillem de Bruijn {
10907b65c5bSWillem de Bruijn 	unsigned long sum = 0;
11007b65c5bSWillem de Bruijn 	int i;
11107b65c5bSWillem de Bruijn 
11207b65c5bSWillem de Bruijn 	for (i = 0; i < num_words; i++)
11307b65c5bSWillem de Bruijn 		sum += start[i];
11407b65c5bSWillem de Bruijn 
11507b65c5bSWillem de Bruijn 	while (sum >> 16)
11607b65c5bSWillem de Bruijn 		sum = (sum & 0xFFFF) + (sum >> 16);
11707b65c5bSWillem de Bruijn 
11807b65c5bSWillem de Bruijn 	return ~sum;
11907b65c5bSWillem de Bruijn }
12007b65c5bSWillem de Bruijn 
12107b65c5bSWillem de Bruijn static int do_setcpu(int cpu)
12207b65c5bSWillem de Bruijn {
12307b65c5bSWillem de Bruijn 	cpu_set_t mask;
12407b65c5bSWillem de Bruijn 
12507b65c5bSWillem de Bruijn 	CPU_ZERO(&mask);
12607b65c5bSWillem de Bruijn 	CPU_SET(cpu, &mask);
12707b65c5bSWillem de Bruijn 	if (sched_setaffinity(0, sizeof(mask), &mask))
12807b65c5bSWillem de Bruijn 		error(1, 0, "setaffinity %d", cpu);
12907b65c5bSWillem de Bruijn 
13007b65c5bSWillem de Bruijn 	if (cfg_verbose)
13107b65c5bSWillem de Bruijn 		fprintf(stderr, "cpu: %u\n", cpu);
13207b65c5bSWillem de Bruijn 
13307b65c5bSWillem de Bruijn 	return 0;
13407b65c5bSWillem de Bruijn }
13507b65c5bSWillem de Bruijn 
13607b65c5bSWillem de Bruijn static void do_setsockopt(int fd, int level, int optname, int val)
13707b65c5bSWillem de Bruijn {
13807b65c5bSWillem de Bruijn 	if (setsockopt(fd, level, optname, &val, sizeof(val)))
13907b65c5bSWillem de Bruijn 		error(1, errno, "setsockopt %d.%d: %d", level, optname, val);
14007b65c5bSWillem de Bruijn }
14107b65c5bSWillem de Bruijn 
14207b65c5bSWillem de Bruijn static int do_poll(int fd, int events)
14307b65c5bSWillem de Bruijn {
14407b65c5bSWillem de Bruijn 	struct pollfd pfd;
14507b65c5bSWillem de Bruijn 	int ret;
14607b65c5bSWillem de Bruijn 
14707b65c5bSWillem de Bruijn 	pfd.events = events;
14807b65c5bSWillem de Bruijn 	pfd.revents = 0;
14907b65c5bSWillem de Bruijn 	pfd.fd = fd;
15007b65c5bSWillem de Bruijn 
15107b65c5bSWillem de Bruijn 	ret = poll(&pfd, 1, cfg_waittime_ms);
15207b65c5bSWillem de Bruijn 	if (ret == -1)
15307b65c5bSWillem de Bruijn 		error(1, errno, "poll");
15407b65c5bSWillem de Bruijn 
15507b65c5bSWillem de Bruijn 	return ret && (pfd.revents & events);
15607b65c5bSWillem de Bruijn }
15707b65c5bSWillem de Bruijn 
15807b65c5bSWillem de Bruijn static int do_accept(int fd)
15907b65c5bSWillem de Bruijn {
16007b65c5bSWillem de Bruijn 	int fda = fd;
16107b65c5bSWillem de Bruijn 
16207b65c5bSWillem de Bruijn 	fd = accept(fda, NULL, NULL);
16307b65c5bSWillem de Bruijn 	if (fd == -1)
16407b65c5bSWillem de Bruijn 		error(1, errno, "accept");
16507b65c5bSWillem de Bruijn 	if (close(fda))
16607b65c5bSWillem de Bruijn 		error(1, errno, "close listen sock");
16707b65c5bSWillem de Bruijn 
16807b65c5bSWillem de Bruijn 	return fd;
16907b65c5bSWillem de Bruijn }
17007b65c5bSWillem de Bruijn 
171dfb8434bSSowmini Varadhan static void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie)
172dfb8434bSSowmini Varadhan {
173dfb8434bSSowmini Varadhan 	struct cmsghdr *cm;
174dfb8434bSSowmini Varadhan 
175dfb8434bSSowmini Varadhan 	if (!msg->msg_control)
176dfb8434bSSowmini Varadhan 		error(1, errno, "NULL cookie");
177dfb8434bSSowmini Varadhan 	cm = (void *)msg->msg_control;
178dfb8434bSSowmini Varadhan 	cm->cmsg_len = CMSG_LEN(sizeof(cookie));
179dfb8434bSSowmini Varadhan 	cm->cmsg_level = SOL_RDS;
180dfb8434bSSowmini Varadhan 	cm->cmsg_type = RDS_CMSG_ZCOPY_COOKIE;
181dfb8434bSSowmini Varadhan 	memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie));
182dfb8434bSSowmini Varadhan }
183dfb8434bSSowmini Varadhan 
184dfb8434bSSowmini Varadhan static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
18507b65c5bSWillem de Bruijn {
18607b65c5bSWillem de Bruijn 	int ret, len, i, flags;
187dfb8434bSSowmini Varadhan 	static uint32_t cookie;
188dfb8434bSSowmini Varadhan 	char ckbuf[CMSG_SPACE(sizeof(cookie))];
18907b65c5bSWillem de Bruijn 
19007b65c5bSWillem de Bruijn 	len = 0;
19107b65c5bSWillem de Bruijn 	for (i = 0; i < msg->msg_iovlen; i++)
19207b65c5bSWillem de Bruijn 		len += msg->msg_iov[i].iov_len;
19307b65c5bSWillem de Bruijn 
19407b65c5bSWillem de Bruijn 	flags = MSG_DONTWAIT;
195dfb8434bSSowmini Varadhan 	if (do_zerocopy) {
19607b65c5bSWillem de Bruijn 		flags |= MSG_ZEROCOPY;
197dfb8434bSSowmini Varadhan 		if (domain == PF_RDS) {
198dfb8434bSSowmini Varadhan 			memset(&msg->msg_control, 0, sizeof(msg->msg_control));
199dfb8434bSSowmini Varadhan 			msg->msg_controllen = CMSG_SPACE(sizeof(cookie));
200dfb8434bSSowmini Varadhan 			msg->msg_control = (struct cmsghdr *)ckbuf;
201dfb8434bSSowmini Varadhan 			add_zcopy_cookie(msg, ++cookie);
202dfb8434bSSowmini Varadhan 		}
203dfb8434bSSowmini Varadhan 	}
20407b65c5bSWillem de Bruijn 
20507b65c5bSWillem de Bruijn 	ret = sendmsg(fd, msg, flags);
20607b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
20707b65c5bSWillem de Bruijn 		return false;
20807b65c5bSWillem de Bruijn 	if (ret == -1)
20907b65c5bSWillem de Bruijn 		error(1, errno, "send");
21007b65c5bSWillem de Bruijn 	if (cfg_verbose && ret != len)
21107b65c5bSWillem de Bruijn 		fprintf(stderr, "send: ret=%u != %u\n", ret, len);
21207b65c5bSWillem de Bruijn 
21307b65c5bSWillem de Bruijn 	if (len) {
21407b65c5bSWillem de Bruijn 		packets++;
21507b65c5bSWillem de Bruijn 		bytes += ret;
21607b65c5bSWillem de Bruijn 		if (do_zerocopy && ret)
21707b65c5bSWillem de Bruijn 			expected_completions++;
21807b65c5bSWillem de Bruijn 	}
219dfb8434bSSowmini Varadhan 	if (do_zerocopy && domain == PF_RDS) {
220dfb8434bSSowmini Varadhan 		msg->msg_control = NULL;
221dfb8434bSSowmini Varadhan 		msg->msg_controllen = 0;
222dfb8434bSSowmini Varadhan 	}
22307b65c5bSWillem de Bruijn 
22407b65c5bSWillem de Bruijn 	return true;
22507b65c5bSWillem de Bruijn }
22607b65c5bSWillem de Bruijn 
22707b65c5bSWillem de Bruijn static void do_sendmsg_corked(int fd, struct msghdr *msg)
22807b65c5bSWillem de Bruijn {
22907b65c5bSWillem de Bruijn 	bool do_zerocopy = cfg_zerocopy;
23007b65c5bSWillem de Bruijn 	int i, payload_len, extra_len;
23107b65c5bSWillem de Bruijn 
23207b65c5bSWillem de Bruijn 	/* split up the packet. for non-multiple, make first buffer longer */
23307b65c5bSWillem de Bruijn 	payload_len = cfg_payload_len / cfg_cork;
23407b65c5bSWillem de Bruijn 	extra_len = cfg_payload_len - (cfg_cork * payload_len);
23507b65c5bSWillem de Bruijn 
23607b65c5bSWillem de Bruijn 	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 1);
23707b65c5bSWillem de Bruijn 
23807b65c5bSWillem de Bruijn 	for (i = 0; i < cfg_cork; i++) {
23907b65c5bSWillem de Bruijn 
24007b65c5bSWillem de Bruijn 		/* in mixed-frags mode, alternate zerocopy and copy frags
24107b65c5bSWillem de Bruijn 		 * start with non-zerocopy, to ensure attach later works
24207b65c5bSWillem de Bruijn 		 */
24307b65c5bSWillem de Bruijn 		if (cfg_cork_mixed)
24407b65c5bSWillem de Bruijn 			do_zerocopy = (i & 1);
24507b65c5bSWillem de Bruijn 
24607b65c5bSWillem de Bruijn 		msg->msg_iov[0].iov_len = payload_len + extra_len;
24707b65c5bSWillem de Bruijn 		extra_len = 0;
24807b65c5bSWillem de Bruijn 
249dfb8434bSSowmini Varadhan 		do_sendmsg(fd, msg, do_zerocopy,
250dfb8434bSSowmini Varadhan 			   (cfg_dst_addr.ss_family == AF_INET ?
251dfb8434bSSowmini Varadhan 			    PF_INET : PF_INET6));
25207b65c5bSWillem de Bruijn 	}
25307b65c5bSWillem de Bruijn 
25407b65c5bSWillem de Bruijn 	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0);
25507b65c5bSWillem de Bruijn }
25607b65c5bSWillem de Bruijn 
25707b65c5bSWillem de Bruijn static int setup_iph(struct iphdr *iph, uint16_t payload_len)
25807b65c5bSWillem de Bruijn {
25907b65c5bSWillem de Bruijn 	struct sockaddr_in *daddr = (void *) &cfg_dst_addr;
26007b65c5bSWillem de Bruijn 	struct sockaddr_in *saddr = (void *) &cfg_src_addr;
26107b65c5bSWillem de Bruijn 
26207b65c5bSWillem de Bruijn 	memset(iph, 0, sizeof(*iph));
26307b65c5bSWillem de Bruijn 
26407b65c5bSWillem de Bruijn 	iph->version	= 4;
26507b65c5bSWillem de Bruijn 	iph->tos	= 0;
26607b65c5bSWillem de Bruijn 	iph->ihl	= 5;
26707b65c5bSWillem de Bruijn 	iph->ttl	= 2;
26807b65c5bSWillem de Bruijn 	iph->saddr	= saddr->sin_addr.s_addr;
26907b65c5bSWillem de Bruijn 	iph->daddr	= daddr->sin_addr.s_addr;
27007b65c5bSWillem de Bruijn 	iph->protocol	= IPPROTO_EGP;
27107b65c5bSWillem de Bruijn 	iph->tot_len	= htons(sizeof(*iph) + payload_len);
27207b65c5bSWillem de Bruijn 	iph->check	= get_ip_csum((void *) iph, iph->ihl << 1);
27307b65c5bSWillem de Bruijn 
27407b65c5bSWillem de Bruijn 	return sizeof(*iph);
27507b65c5bSWillem de Bruijn }
27607b65c5bSWillem de Bruijn 
27707b65c5bSWillem de Bruijn static int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len)
27807b65c5bSWillem de Bruijn {
27907b65c5bSWillem de Bruijn 	struct sockaddr_in6 *daddr = (void *) &cfg_dst_addr;
28007b65c5bSWillem de Bruijn 	struct sockaddr_in6 *saddr = (void *) &cfg_src_addr;
28107b65c5bSWillem de Bruijn 
28207b65c5bSWillem de Bruijn 	memset(ip6h, 0, sizeof(*ip6h));
28307b65c5bSWillem de Bruijn 
28407b65c5bSWillem de Bruijn 	ip6h->version		= 6;
28507b65c5bSWillem de Bruijn 	ip6h->payload_len	= htons(payload_len);
28607b65c5bSWillem de Bruijn 	ip6h->nexthdr		= IPPROTO_EGP;
28707b65c5bSWillem de Bruijn 	ip6h->hop_limit		= 2;
28807b65c5bSWillem de Bruijn 	ip6h->saddr		= saddr->sin6_addr;
28907b65c5bSWillem de Bruijn 	ip6h->daddr		= daddr->sin6_addr;
29007b65c5bSWillem de Bruijn 
29107b65c5bSWillem de Bruijn 	return sizeof(*ip6h);
29207b65c5bSWillem de Bruijn }
29307b65c5bSWillem de Bruijn 
294d36f45e5SSowmini Varadhan 
295d36f45e5SSowmini Varadhan static void setup_sockaddr(int domain, const char *str_addr,
296d36f45e5SSowmini Varadhan 			   struct sockaddr_storage *sockaddr)
29707b65c5bSWillem de Bruijn {
29807b65c5bSWillem de Bruijn 	struct sockaddr_in6 *addr6 = (void *) sockaddr;
29907b65c5bSWillem de Bruijn 	struct sockaddr_in *addr4 = (void *) sockaddr;
30007b65c5bSWillem de Bruijn 
30107b65c5bSWillem de Bruijn 	switch (domain) {
30207b65c5bSWillem de Bruijn 	case PF_INET:
303d36f45e5SSowmini Varadhan 		memset(addr4, 0, sizeof(*addr4));
30407b65c5bSWillem de Bruijn 		addr4->sin_family = AF_INET;
30507b65c5bSWillem de Bruijn 		addr4->sin_port = htons(cfg_port);
306d36f45e5SSowmini Varadhan 		if (str_addr &&
307d36f45e5SSowmini Varadhan 		    inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
30807b65c5bSWillem de Bruijn 			error(1, 0, "ipv4 parse error: %s", str_addr);
30907b65c5bSWillem de Bruijn 		break;
31007b65c5bSWillem de Bruijn 	case PF_INET6:
311d36f45e5SSowmini Varadhan 		memset(addr6, 0, sizeof(*addr6));
31207b65c5bSWillem de Bruijn 		addr6->sin6_family = AF_INET6;
31307b65c5bSWillem de Bruijn 		addr6->sin6_port = htons(cfg_port);
314d36f45e5SSowmini Varadhan 		if (str_addr &&
315d36f45e5SSowmini Varadhan 		    inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
31607b65c5bSWillem de Bruijn 			error(1, 0, "ipv6 parse error: %s", str_addr);
31707b65c5bSWillem de Bruijn 		break;
31807b65c5bSWillem de Bruijn 	default:
31907b65c5bSWillem de Bruijn 		error(1, 0, "illegal domain");
32007b65c5bSWillem de Bruijn 	}
32107b65c5bSWillem de Bruijn }
32207b65c5bSWillem de Bruijn 
32307b65c5bSWillem de Bruijn static int do_setup_tx(int domain, int type, int protocol)
32407b65c5bSWillem de Bruijn {
32507b65c5bSWillem de Bruijn 	int fd;
32607b65c5bSWillem de Bruijn 
32707b65c5bSWillem de Bruijn 	fd = socket(domain, type, protocol);
32807b65c5bSWillem de Bruijn 	if (fd == -1)
32907b65c5bSWillem de Bruijn 		error(1, errno, "socket t");
33007b65c5bSWillem de Bruijn 
33107b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 1 << 21);
33207b65c5bSWillem de Bruijn 	if (cfg_zerocopy)
33307b65c5bSWillem de Bruijn 		do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1);
33407b65c5bSWillem de Bruijn 
335b16ac920SSowmini Varadhan 	if (domain != PF_PACKET && domain != PF_RDS)
33607b65c5bSWillem de Bruijn 		if (connect(fd, (void *) &cfg_dst_addr, cfg_alen))
33707b65c5bSWillem de Bruijn 			error(1, errno, "connect");
33807b65c5bSWillem de Bruijn 
339b16ac920SSowmini Varadhan 	if (domain == PF_RDS) {
340b16ac920SSowmini Varadhan 		if (bind(fd, (void *) &cfg_src_addr, cfg_alen))
341b16ac920SSowmini Varadhan 			error(1, errno, "bind");
342b16ac920SSowmini Varadhan 	}
343b16ac920SSowmini Varadhan 
34407b65c5bSWillem de Bruijn 	return fd;
34507b65c5bSWillem de Bruijn }
34607b65c5bSWillem de Bruijn 
347*6f3899e6SSowmini Varadhan static uint32_t do_process_zerocopy_cookies(struct rds_zcopy_cookies *ck)
348*6f3899e6SSowmini Varadhan {
349*6f3899e6SSowmini Varadhan 	int i;
350*6f3899e6SSowmini Varadhan 
351*6f3899e6SSowmini Varadhan 	if (ck->num > RDS_MAX_ZCOOKIES)
352*6f3899e6SSowmini Varadhan 		error(1, 0, "Returned %d cookies, max expected %d\n",
353*6f3899e6SSowmini Varadhan 		      ck->num, RDS_MAX_ZCOOKIES);
354*6f3899e6SSowmini Varadhan 	for (i = 0; i < ck->num; i++)
355*6f3899e6SSowmini Varadhan 		if (cfg_verbose >= 2)
356*6f3899e6SSowmini Varadhan 			fprintf(stderr, "%d\n", ck->cookies[i]);
357*6f3899e6SSowmini Varadhan 	return ck->num;
358*6f3899e6SSowmini Varadhan }
359*6f3899e6SSowmini Varadhan 
360*6f3899e6SSowmini Varadhan static bool do_recvmsg_completion(int fd)
361*6f3899e6SSowmini Varadhan {
362*6f3899e6SSowmini Varadhan 	char cmsgbuf[CMSG_SPACE(sizeof(struct rds_zcopy_cookies))];
363*6f3899e6SSowmini Varadhan 	struct rds_zcopy_cookies *ck;
364*6f3899e6SSowmini Varadhan 	struct cmsghdr *cmsg;
365*6f3899e6SSowmini Varadhan 	struct msghdr msg;
366*6f3899e6SSowmini Varadhan 	bool ret = false;
367*6f3899e6SSowmini Varadhan 
368*6f3899e6SSowmini Varadhan 	memset(&msg, 0, sizeof(msg));
369*6f3899e6SSowmini Varadhan 	msg.msg_control = cmsgbuf;
370*6f3899e6SSowmini Varadhan 	msg.msg_controllen = sizeof(cmsgbuf);
371*6f3899e6SSowmini Varadhan 
372*6f3899e6SSowmini Varadhan 	if (recvmsg(fd, &msg, MSG_DONTWAIT))
373*6f3899e6SSowmini Varadhan 		return ret;
374*6f3899e6SSowmini Varadhan 
375*6f3899e6SSowmini Varadhan 	if (msg.msg_flags & MSG_CTRUNC)
376*6f3899e6SSowmini Varadhan 		error(1, errno, "recvmsg notification: truncated");
377*6f3899e6SSowmini Varadhan 
378*6f3899e6SSowmini Varadhan 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
379*6f3899e6SSowmini Varadhan 		if (cmsg->cmsg_level == SOL_RDS &&
380*6f3899e6SSowmini Varadhan 		    cmsg->cmsg_type == RDS_CMSG_ZCOPY_COMPLETION) {
381*6f3899e6SSowmini Varadhan 
382*6f3899e6SSowmini Varadhan 			ck = (struct rds_zcopy_cookies *)CMSG_DATA(cmsg);
383*6f3899e6SSowmini Varadhan 			completions += do_process_zerocopy_cookies(ck);
384*6f3899e6SSowmini Varadhan 			ret = true;
385*6f3899e6SSowmini Varadhan 			break;
386*6f3899e6SSowmini Varadhan 		}
387*6f3899e6SSowmini Varadhan 		error(0, 0, "ignoring cmsg at level %d type %d\n",
388*6f3899e6SSowmini Varadhan 			    cmsg->cmsg_level, cmsg->cmsg_type);
389*6f3899e6SSowmini Varadhan 	}
390*6f3899e6SSowmini Varadhan 	return ret;
391*6f3899e6SSowmini Varadhan }
392*6f3899e6SSowmini Varadhan 
393*6f3899e6SSowmini Varadhan static bool do_recv_completion(int fd, int domain)
39407b65c5bSWillem de Bruijn {
39507b65c5bSWillem de Bruijn 	struct sock_extended_err *serr;
39607b65c5bSWillem de Bruijn 	struct msghdr msg = {};
39707b65c5bSWillem de Bruijn 	struct cmsghdr *cm;
39807b65c5bSWillem de Bruijn 	uint32_t hi, lo, range;
39907b65c5bSWillem de Bruijn 	int ret, zerocopy;
40007b65c5bSWillem de Bruijn 	char control[100];
40107b65c5bSWillem de Bruijn 
402*6f3899e6SSowmini Varadhan 	if (domain == PF_RDS)
403*6f3899e6SSowmini Varadhan 		return do_recvmsg_completion(fd);
404*6f3899e6SSowmini Varadhan 
40507b65c5bSWillem de Bruijn 	msg.msg_control = control;
40607b65c5bSWillem de Bruijn 	msg.msg_controllen = sizeof(control);
40707b65c5bSWillem de Bruijn 
40807b65c5bSWillem de Bruijn 	ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
40907b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
41007b65c5bSWillem de Bruijn 		return false;
41107b65c5bSWillem de Bruijn 	if (ret == -1)
41207b65c5bSWillem de Bruijn 		error(1, errno, "recvmsg notification");
41307b65c5bSWillem de Bruijn 	if (msg.msg_flags & MSG_CTRUNC)
41407b65c5bSWillem de Bruijn 		error(1, errno, "recvmsg notification: truncated");
41507b65c5bSWillem de Bruijn 
41607b65c5bSWillem de Bruijn 	cm = CMSG_FIRSTHDR(&msg);
41707b65c5bSWillem de Bruijn 	if (!cm)
41807b65c5bSWillem de Bruijn 		error(1, 0, "cmsg: no cmsg");
41907b65c5bSWillem de Bruijn 	if (!((cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) ||
42007b65c5bSWillem de Bruijn 	      (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_RECVERR) ||
42107b65c5bSWillem de Bruijn 	      (cm->cmsg_level == SOL_PACKET && cm->cmsg_type == PACKET_TX_TIMESTAMP)))
42207b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong type: %d.%d",
42307b65c5bSWillem de Bruijn 		      cm->cmsg_level, cm->cmsg_type);
42407b65c5bSWillem de Bruijn 
42507b65c5bSWillem de Bruijn 	serr = (void *) CMSG_DATA(cm);
426dfb8434bSSowmini Varadhan 
42707b65c5bSWillem de Bruijn 	if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
42807b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong origin: %u", serr->ee_origin);
42907b65c5bSWillem de Bruijn 	if (serr->ee_errno != 0)
43007b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong error code: %u", serr->ee_errno);
43107b65c5bSWillem de Bruijn 
43207b65c5bSWillem de Bruijn 	hi = serr->ee_data;
43307b65c5bSWillem de Bruijn 	lo = serr->ee_info;
43407b65c5bSWillem de Bruijn 	range = hi - lo + 1;
43507b65c5bSWillem de Bruijn 
43607b65c5bSWillem de Bruijn 	/* Detect notification gaps. These should not happen often, if at all.
43707b65c5bSWillem de Bruijn 	 * Gaps can occur due to drops, reordering and retransmissions.
43807b65c5bSWillem de Bruijn 	 */
43907b65c5bSWillem de Bruijn 	if (lo != next_completion)
44007b65c5bSWillem de Bruijn 		fprintf(stderr, "gap: %u..%u does not append to %u\n",
44107b65c5bSWillem de Bruijn 			lo, hi, next_completion);
44207b65c5bSWillem de Bruijn 	next_completion = hi + 1;
44307b65c5bSWillem de Bruijn 
44407b65c5bSWillem de Bruijn 	zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED);
44507b65c5bSWillem de Bruijn 	if (zerocopied == -1)
44607b65c5bSWillem de Bruijn 		zerocopied = zerocopy;
44707b65c5bSWillem de Bruijn 	else if (zerocopied != zerocopy) {
44807b65c5bSWillem de Bruijn 		fprintf(stderr, "serr: inconsistent\n");
44907b65c5bSWillem de Bruijn 		zerocopied = zerocopy;
45007b65c5bSWillem de Bruijn 	}
45107b65c5bSWillem de Bruijn 
45207b65c5bSWillem de Bruijn 	if (cfg_verbose >= 2)
45307b65c5bSWillem de Bruijn 		fprintf(stderr, "completed: %u (h=%u l=%u)\n",
45407b65c5bSWillem de Bruijn 			range, hi, lo);
45507b65c5bSWillem de Bruijn 
45607b65c5bSWillem de Bruijn 	completions += range;
45707b65c5bSWillem de Bruijn 	return true;
45807b65c5bSWillem de Bruijn }
45907b65c5bSWillem de Bruijn 
46007b65c5bSWillem de Bruijn /* Read all outstanding messages on the errqueue */
461*6f3899e6SSowmini Varadhan static void do_recv_completions(int fd, int domain)
46207b65c5bSWillem de Bruijn {
463*6f3899e6SSowmini Varadhan 	while (do_recv_completion(fd, domain)) {}
46407b65c5bSWillem de Bruijn }
46507b65c5bSWillem de Bruijn 
46607b65c5bSWillem de Bruijn /* Wait for all remaining completions on the errqueue */
467*6f3899e6SSowmini Varadhan static void do_recv_remaining_completions(int fd, int domain)
46807b65c5bSWillem de Bruijn {
46907b65c5bSWillem de Bruijn 	int64_t tstop = gettimeofday_ms() + cfg_waittime_ms;
47007b65c5bSWillem de Bruijn 
47107b65c5bSWillem de Bruijn 	while (completions < expected_completions &&
47207b65c5bSWillem de Bruijn 	       gettimeofday_ms() < tstop) {
473*6f3899e6SSowmini Varadhan 		if (do_poll(fd, domain == PF_RDS ? POLLIN : POLLERR))
474*6f3899e6SSowmini Varadhan 			do_recv_completions(fd, domain);
47507b65c5bSWillem de Bruijn 	}
47607b65c5bSWillem de Bruijn 
47707b65c5bSWillem de Bruijn 	if (completions < expected_completions)
478bbd9644eSWillem de Bruijn 		fprintf(stderr, "missing notifications: %lu < %lu\n",
47907b65c5bSWillem de Bruijn 			completions, expected_completions);
48007b65c5bSWillem de Bruijn }
48107b65c5bSWillem de Bruijn 
48207b65c5bSWillem de Bruijn static void do_tx(int domain, int type, int protocol)
48307b65c5bSWillem de Bruijn {
48407b65c5bSWillem de Bruijn 	struct iovec iov[3] = { {0} };
48507b65c5bSWillem de Bruijn 	struct sockaddr_ll laddr;
48607b65c5bSWillem de Bruijn 	struct msghdr msg = {0};
48707b65c5bSWillem de Bruijn 	struct ethhdr eth;
48807b65c5bSWillem de Bruijn 	union {
48907b65c5bSWillem de Bruijn 		struct ipv6hdr ip6h;
49007b65c5bSWillem de Bruijn 		struct iphdr iph;
49107b65c5bSWillem de Bruijn 	} nh;
49207b65c5bSWillem de Bruijn 	uint64_t tstop;
49307b65c5bSWillem de Bruijn 	int fd;
49407b65c5bSWillem de Bruijn 
49507b65c5bSWillem de Bruijn 	fd = do_setup_tx(domain, type, protocol);
49607b65c5bSWillem de Bruijn 
49707b65c5bSWillem de Bruijn 	if (domain == PF_PACKET) {
49807b65c5bSWillem de Bruijn 		uint16_t proto = cfg_family == PF_INET ? ETH_P_IP : ETH_P_IPV6;
49907b65c5bSWillem de Bruijn 
50007b65c5bSWillem de Bruijn 		/* sock_raw passes ll header as data */
50107b65c5bSWillem de Bruijn 		if (type == SOCK_RAW) {
50207b65c5bSWillem de Bruijn 			memset(eth.h_dest, 0x06, ETH_ALEN);
50307b65c5bSWillem de Bruijn 			memset(eth.h_source, 0x02, ETH_ALEN);
50407b65c5bSWillem de Bruijn 			eth.h_proto = htons(proto);
50507b65c5bSWillem de Bruijn 			iov[0].iov_base = &eth;
50607b65c5bSWillem de Bruijn 			iov[0].iov_len = sizeof(eth);
50707b65c5bSWillem de Bruijn 			msg.msg_iovlen++;
50807b65c5bSWillem de Bruijn 		}
50907b65c5bSWillem de Bruijn 
51007b65c5bSWillem de Bruijn 		/* both sock_raw and sock_dgram expect name */
51107b65c5bSWillem de Bruijn 		memset(&laddr, 0, sizeof(laddr));
51207b65c5bSWillem de Bruijn 		laddr.sll_family	= AF_PACKET;
51307b65c5bSWillem de Bruijn 		laddr.sll_ifindex	= cfg_ifindex;
51407b65c5bSWillem de Bruijn 		laddr.sll_protocol	= htons(proto);
51507b65c5bSWillem de Bruijn 		laddr.sll_halen		= ETH_ALEN;
51607b65c5bSWillem de Bruijn 
51707b65c5bSWillem de Bruijn 		memset(laddr.sll_addr, 0x06, ETH_ALEN);
51807b65c5bSWillem de Bruijn 
51907b65c5bSWillem de Bruijn 		msg.msg_name		= &laddr;
52007b65c5bSWillem de Bruijn 		msg.msg_namelen		= sizeof(laddr);
52107b65c5bSWillem de Bruijn 	}
52207b65c5bSWillem de Bruijn 
52307b65c5bSWillem de Bruijn 	/* packet and raw sockets with hdrincl must pass network header */
52407b65c5bSWillem de Bruijn 	if (domain == PF_PACKET || protocol == IPPROTO_RAW) {
52507b65c5bSWillem de Bruijn 		if (cfg_family == PF_INET)
52607b65c5bSWillem de Bruijn 			iov[1].iov_len = setup_iph(&nh.iph, cfg_payload_len);
52707b65c5bSWillem de Bruijn 		else
52807b65c5bSWillem de Bruijn 			iov[1].iov_len = setup_ip6h(&nh.ip6h, cfg_payload_len);
52907b65c5bSWillem de Bruijn 
53007b65c5bSWillem de Bruijn 		iov[1].iov_base = (void *) &nh;
53107b65c5bSWillem de Bruijn 		msg.msg_iovlen++;
53207b65c5bSWillem de Bruijn 	}
53307b65c5bSWillem de Bruijn 
534b16ac920SSowmini Varadhan 	if (domain == PF_RDS) {
535b16ac920SSowmini Varadhan 		msg.msg_name = &cfg_dst_addr;
536b16ac920SSowmini Varadhan 		msg.msg_namelen =  (cfg_dst_addr.ss_family == AF_INET ?
537b16ac920SSowmini Varadhan 				    sizeof(struct sockaddr_in) :
538b16ac920SSowmini Varadhan 				    sizeof(struct sockaddr_in6));
539b16ac920SSowmini Varadhan 	}
540b16ac920SSowmini Varadhan 
54107b65c5bSWillem de Bruijn 	iov[2].iov_base = payload;
54207b65c5bSWillem de Bruijn 	iov[2].iov_len = cfg_payload_len;
54307b65c5bSWillem de Bruijn 	msg.msg_iovlen++;
54407b65c5bSWillem de Bruijn 	msg.msg_iov = &iov[3 - msg.msg_iovlen];
54507b65c5bSWillem de Bruijn 
54607b65c5bSWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_runtime_ms;
54707b65c5bSWillem de Bruijn 	do {
54807b65c5bSWillem de Bruijn 		if (cfg_cork)
54907b65c5bSWillem de Bruijn 			do_sendmsg_corked(fd, &msg);
55007b65c5bSWillem de Bruijn 		else
551dfb8434bSSowmini Varadhan 			do_sendmsg(fd, &msg, cfg_zerocopy, domain);
55207b65c5bSWillem de Bruijn 
55307b65c5bSWillem de Bruijn 		while (!do_poll(fd, POLLOUT)) {
55407b65c5bSWillem de Bruijn 			if (cfg_zerocopy)
555*6f3899e6SSowmini Varadhan 				do_recv_completions(fd, domain);
55607b65c5bSWillem de Bruijn 		}
55707b65c5bSWillem de Bruijn 
55807b65c5bSWillem de Bruijn 	} while (gettimeofday_ms() < tstop);
55907b65c5bSWillem de Bruijn 
56007b65c5bSWillem de Bruijn 	if (cfg_zerocopy)
561*6f3899e6SSowmini Varadhan 		do_recv_remaining_completions(fd, domain);
56207b65c5bSWillem de Bruijn 
56307b65c5bSWillem de Bruijn 	if (close(fd))
56407b65c5bSWillem de Bruijn 		error(1, errno, "close");
56507b65c5bSWillem de Bruijn 
56607b65c5bSWillem de Bruijn 	fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n",
56707b65c5bSWillem de Bruijn 		packets, bytes >> 20, completions,
56807b65c5bSWillem de Bruijn 		zerocopied == 1 ? 'y' : 'n');
56907b65c5bSWillem de Bruijn }
57007b65c5bSWillem de Bruijn 
57107b65c5bSWillem de Bruijn static int do_setup_rx(int domain, int type, int protocol)
57207b65c5bSWillem de Bruijn {
57307b65c5bSWillem de Bruijn 	int fd;
57407b65c5bSWillem de Bruijn 
57507b65c5bSWillem de Bruijn 	/* If tx over PF_PACKET, rx over PF_INET(6)/SOCK_RAW,
57607b65c5bSWillem de Bruijn 	 * to recv the only copy of the packet, not a clone
57707b65c5bSWillem de Bruijn 	 */
57807b65c5bSWillem de Bruijn 	if (domain == PF_PACKET)
57907b65c5bSWillem de Bruijn 		error(1, 0, "Use PF_INET/SOCK_RAW to read");
58007b65c5bSWillem de Bruijn 
58107b65c5bSWillem de Bruijn 	if (type == SOCK_RAW && protocol == IPPROTO_RAW)
58207b65c5bSWillem de Bruijn 		error(1, 0, "IPPROTO_RAW: not supported on Rx");
58307b65c5bSWillem de Bruijn 
58407b65c5bSWillem de Bruijn 	fd = socket(domain, type, protocol);
58507b65c5bSWillem de Bruijn 	if (fd == -1)
58607b65c5bSWillem de Bruijn 		error(1, errno, "socket r");
58707b65c5bSWillem de Bruijn 
58807b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 1 << 21);
58907b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, 1 << 16);
59007b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
59107b65c5bSWillem de Bruijn 
59207b65c5bSWillem de Bruijn 	if (bind(fd, (void *) &cfg_dst_addr, cfg_alen))
59307b65c5bSWillem de Bruijn 		error(1, errno, "bind");
59407b65c5bSWillem de Bruijn 
59507b65c5bSWillem de Bruijn 	if (type == SOCK_STREAM) {
59607b65c5bSWillem de Bruijn 		if (listen(fd, 1))
59707b65c5bSWillem de Bruijn 			error(1, errno, "listen");
59807b65c5bSWillem de Bruijn 		fd = do_accept(fd);
59907b65c5bSWillem de Bruijn 	}
60007b65c5bSWillem de Bruijn 
60107b65c5bSWillem de Bruijn 	return fd;
60207b65c5bSWillem de Bruijn }
60307b65c5bSWillem de Bruijn 
60407b65c5bSWillem de Bruijn /* Flush all outstanding bytes for the tcp receive queue */
60507b65c5bSWillem de Bruijn static void do_flush_tcp(int fd)
60607b65c5bSWillem de Bruijn {
60707b65c5bSWillem de Bruijn 	int ret;
60807b65c5bSWillem de Bruijn 
60907b65c5bSWillem de Bruijn 	/* MSG_TRUNC flushes up to len bytes */
61007b65c5bSWillem de Bruijn 	ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
61107b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
61207b65c5bSWillem de Bruijn 		return;
61307b65c5bSWillem de Bruijn 	if (ret == -1)
61407b65c5bSWillem de Bruijn 		error(1, errno, "flush");
61507b65c5bSWillem de Bruijn 	if (!ret)
61607b65c5bSWillem de Bruijn 		return;
61707b65c5bSWillem de Bruijn 
61807b65c5bSWillem de Bruijn 	packets++;
61907b65c5bSWillem de Bruijn 	bytes += ret;
62007b65c5bSWillem de Bruijn }
62107b65c5bSWillem de Bruijn 
62207b65c5bSWillem de Bruijn /* Flush all outstanding datagrams. Verify first few bytes of each. */
62307b65c5bSWillem de Bruijn static void do_flush_datagram(int fd, int type)
62407b65c5bSWillem de Bruijn {
62507b65c5bSWillem de Bruijn 	int ret, off = 0;
62607b65c5bSWillem de Bruijn 	char buf[64];
62707b65c5bSWillem de Bruijn 
62807b65c5bSWillem de Bruijn 	/* MSG_TRUNC will return full datagram length */
62907b65c5bSWillem de Bruijn 	ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_TRUNC);
63007b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
63107b65c5bSWillem de Bruijn 		return;
63207b65c5bSWillem de Bruijn 
63307b65c5bSWillem de Bruijn 	/* raw ipv4 return with header, raw ipv6 without */
63407b65c5bSWillem de Bruijn 	if (cfg_family == PF_INET && type == SOCK_RAW) {
63507b65c5bSWillem de Bruijn 		off += sizeof(struct iphdr);
63607b65c5bSWillem de Bruijn 		ret -= sizeof(struct iphdr);
63707b65c5bSWillem de Bruijn 	}
63807b65c5bSWillem de Bruijn 
63907b65c5bSWillem de Bruijn 	if (ret == -1)
64007b65c5bSWillem de Bruijn 		error(1, errno, "recv");
64107b65c5bSWillem de Bruijn 	if (ret != cfg_payload_len)
64207b65c5bSWillem de Bruijn 		error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len);
64307b65c5bSWillem de Bruijn 	if (ret > sizeof(buf) - off)
64407b65c5bSWillem de Bruijn 		ret = sizeof(buf) - off;
64507b65c5bSWillem de Bruijn 	if (memcmp(buf + off, payload, ret))
64607b65c5bSWillem de Bruijn 		error(1, 0, "recv: data mismatch");
64707b65c5bSWillem de Bruijn 
64807b65c5bSWillem de Bruijn 	packets++;
64907b65c5bSWillem de Bruijn 	bytes += cfg_payload_len;
65007b65c5bSWillem de Bruijn }
65107b65c5bSWillem de Bruijn 
65207b65c5bSWillem de Bruijn static void do_rx(int domain, int type, int protocol)
65307b65c5bSWillem de Bruijn {
65407b65c5bSWillem de Bruijn 	uint64_t tstop;
65507b65c5bSWillem de Bruijn 	int fd;
65607b65c5bSWillem de Bruijn 
65707b65c5bSWillem de Bruijn 	fd = do_setup_rx(domain, type, protocol);
65807b65c5bSWillem de Bruijn 
65907b65c5bSWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_runtime_ms;
66007b65c5bSWillem de Bruijn 	do {
66107b65c5bSWillem de Bruijn 		if (type == SOCK_STREAM)
66207b65c5bSWillem de Bruijn 			do_flush_tcp(fd);
66307b65c5bSWillem de Bruijn 		else
66407b65c5bSWillem de Bruijn 			do_flush_datagram(fd, type);
66507b65c5bSWillem de Bruijn 
66607b65c5bSWillem de Bruijn 		do_poll(fd, POLLIN);
66707b65c5bSWillem de Bruijn 
66807b65c5bSWillem de Bruijn 	} while (gettimeofday_ms() < tstop);
66907b65c5bSWillem de Bruijn 
67007b65c5bSWillem de Bruijn 	if (close(fd))
67107b65c5bSWillem de Bruijn 		error(1, errno, "close");
67207b65c5bSWillem de Bruijn 
67307b65c5bSWillem de Bruijn 	fprintf(stderr, "rx=%lu (%lu MB)\n", packets, bytes >> 20);
67407b65c5bSWillem de Bruijn }
67507b65c5bSWillem de Bruijn 
67607b65c5bSWillem de Bruijn static void do_test(int domain, int type, int protocol)
67707b65c5bSWillem de Bruijn {
67807b65c5bSWillem de Bruijn 	int i;
67907b65c5bSWillem de Bruijn 
68007b65c5bSWillem de Bruijn 	if (cfg_cork && (domain == PF_PACKET || type != SOCK_DGRAM))
68107b65c5bSWillem de Bruijn 		error(1, 0, "can only cork udp sockets");
68207b65c5bSWillem de Bruijn 
68307b65c5bSWillem de Bruijn 	do_setcpu(cfg_cpu);
68407b65c5bSWillem de Bruijn 
68507b65c5bSWillem de Bruijn 	for (i = 0; i < IP_MAXPACKET; i++)
68607b65c5bSWillem de Bruijn 		payload[i] = 'a' + (i % 26);
68707b65c5bSWillem de Bruijn 
68807b65c5bSWillem de Bruijn 	if (cfg_rx)
68907b65c5bSWillem de Bruijn 		do_rx(domain, type, protocol);
69007b65c5bSWillem de Bruijn 	else
69107b65c5bSWillem de Bruijn 		do_tx(domain, type, protocol);
69207b65c5bSWillem de Bruijn }
69307b65c5bSWillem de Bruijn 
69407b65c5bSWillem de Bruijn static void usage(const char *filepath)
69507b65c5bSWillem de Bruijn {
69607b65c5bSWillem de Bruijn 	error(1, 0, "Usage: %s [options] <test>", filepath);
69707b65c5bSWillem de Bruijn }
69807b65c5bSWillem de Bruijn 
69907b65c5bSWillem de Bruijn static void parse_opts(int argc, char **argv)
70007b65c5bSWillem de Bruijn {
70107b65c5bSWillem de Bruijn 	const int max_payload_len = sizeof(payload) -
70207b65c5bSWillem de Bruijn 				    sizeof(struct ipv6hdr) -
70307b65c5bSWillem de Bruijn 				    sizeof(struct tcphdr) -
70407b65c5bSWillem de Bruijn 				    40 /* max tcp options */;
70507b65c5bSWillem de Bruijn 	int c;
706d36f45e5SSowmini Varadhan 	char *daddr = NULL, *saddr = NULL;
707b16ac920SSowmini Varadhan 	char *cfg_test;
70807b65c5bSWillem de Bruijn 
70907b65c5bSWillem de Bruijn 	cfg_payload_len = max_payload_len;
71007b65c5bSWillem de Bruijn 
71107b65c5bSWillem de Bruijn 	while ((c = getopt(argc, argv, "46c:C:D:i:mp:rs:S:t:vz")) != -1) {
71207b65c5bSWillem de Bruijn 		switch (c) {
71307b65c5bSWillem de Bruijn 		case '4':
71407b65c5bSWillem de Bruijn 			if (cfg_family != PF_UNSPEC)
71507b65c5bSWillem de Bruijn 				error(1, 0, "Pass one of -4 or -6");
71607b65c5bSWillem de Bruijn 			cfg_family = PF_INET;
71707b65c5bSWillem de Bruijn 			cfg_alen = sizeof(struct sockaddr_in);
71807b65c5bSWillem de Bruijn 			break;
71907b65c5bSWillem de Bruijn 		case '6':
72007b65c5bSWillem de Bruijn 			if (cfg_family != PF_UNSPEC)
72107b65c5bSWillem de Bruijn 				error(1, 0, "Pass one of -4 or -6");
72207b65c5bSWillem de Bruijn 			cfg_family = PF_INET6;
72307b65c5bSWillem de Bruijn 			cfg_alen = sizeof(struct sockaddr_in6);
72407b65c5bSWillem de Bruijn 			break;
72507b65c5bSWillem de Bruijn 		case 'c':
72607b65c5bSWillem de Bruijn 			cfg_cork = strtol(optarg, NULL, 0);
72707b65c5bSWillem de Bruijn 			break;
72807b65c5bSWillem de Bruijn 		case 'C':
72907b65c5bSWillem de Bruijn 			cfg_cpu = strtol(optarg, NULL, 0);
73007b65c5bSWillem de Bruijn 			break;
73107b65c5bSWillem de Bruijn 		case 'D':
732d36f45e5SSowmini Varadhan 			daddr = optarg;
73307b65c5bSWillem de Bruijn 			break;
73407b65c5bSWillem de Bruijn 		case 'i':
73507b65c5bSWillem de Bruijn 			cfg_ifindex = if_nametoindex(optarg);
73607b65c5bSWillem de Bruijn 			if (cfg_ifindex == 0)
73707b65c5bSWillem de Bruijn 				error(1, errno, "invalid iface: %s", optarg);
73807b65c5bSWillem de Bruijn 			break;
73907b65c5bSWillem de Bruijn 		case 'm':
74007b65c5bSWillem de Bruijn 			cfg_cork_mixed = true;
74107b65c5bSWillem de Bruijn 			break;
74207b65c5bSWillem de Bruijn 		case 'p':
743d36f45e5SSowmini Varadhan 			cfg_port = strtoul(optarg, NULL, 0);
74407b65c5bSWillem de Bruijn 			break;
74507b65c5bSWillem de Bruijn 		case 'r':
74607b65c5bSWillem de Bruijn 			cfg_rx = true;
74707b65c5bSWillem de Bruijn 			break;
74807b65c5bSWillem de Bruijn 		case 's':
74907b65c5bSWillem de Bruijn 			cfg_payload_len = strtoul(optarg, NULL, 0);
75007b65c5bSWillem de Bruijn 			break;
75107b65c5bSWillem de Bruijn 		case 'S':
752d36f45e5SSowmini Varadhan 			saddr = optarg;
75307b65c5bSWillem de Bruijn 			break;
75407b65c5bSWillem de Bruijn 		case 't':
75507b65c5bSWillem de Bruijn 			cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000;
75607b65c5bSWillem de Bruijn 			break;
75707b65c5bSWillem de Bruijn 		case 'v':
75807b65c5bSWillem de Bruijn 			cfg_verbose++;
75907b65c5bSWillem de Bruijn 			break;
76007b65c5bSWillem de Bruijn 		case 'z':
76107b65c5bSWillem de Bruijn 			cfg_zerocopy = true;
76207b65c5bSWillem de Bruijn 			break;
76307b65c5bSWillem de Bruijn 		}
76407b65c5bSWillem de Bruijn 	}
765b16ac920SSowmini Varadhan 
766b16ac920SSowmini Varadhan 	cfg_test = argv[argc - 1];
767b16ac920SSowmini Varadhan 	if (strcmp(cfg_test, "rds") == 0) {
768b16ac920SSowmini Varadhan 		if (!daddr)
769b16ac920SSowmini Varadhan 			error(1, 0, "-D <server addr> required for PF_RDS\n");
770b16ac920SSowmini Varadhan 		if (!cfg_rx && !saddr)
771b16ac920SSowmini Varadhan 			error(1, 0, "-S <client addr> required for PF_RDS\n");
772b16ac920SSowmini Varadhan 	}
773d36f45e5SSowmini Varadhan 	setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
774d36f45e5SSowmini Varadhan 	setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
77507b65c5bSWillem de Bruijn 
77607b65c5bSWillem de Bruijn 	if (cfg_payload_len > max_payload_len)
77707b65c5bSWillem de Bruijn 		error(1, 0, "-s: payload exceeds max (%d)", max_payload_len);
77807b65c5bSWillem de Bruijn 	if (cfg_cork_mixed && (!cfg_zerocopy || !cfg_cork))
77907b65c5bSWillem de Bruijn 		error(1, 0, "-m: cork_mixed requires corking and zerocopy");
78007b65c5bSWillem de Bruijn 
78107b65c5bSWillem de Bruijn 	if (optind != argc - 1)
78207b65c5bSWillem de Bruijn 		usage(argv[0]);
78307b65c5bSWillem de Bruijn }
78407b65c5bSWillem de Bruijn 
78507b65c5bSWillem de Bruijn int main(int argc, char **argv)
78607b65c5bSWillem de Bruijn {
78707b65c5bSWillem de Bruijn 	const char *cfg_test;
78807b65c5bSWillem de Bruijn 
78907b65c5bSWillem de Bruijn 	parse_opts(argc, argv);
79007b65c5bSWillem de Bruijn 
79107b65c5bSWillem de Bruijn 	cfg_test = argv[argc - 1];
79207b65c5bSWillem de Bruijn 
79307b65c5bSWillem de Bruijn 	if (!strcmp(cfg_test, "packet"))
79407b65c5bSWillem de Bruijn 		do_test(PF_PACKET, SOCK_RAW, 0);
79507b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "packet_dgram"))
79607b65c5bSWillem de Bruijn 		do_test(PF_PACKET, SOCK_DGRAM, 0);
79707b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "raw"))
79807b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_RAW, IPPROTO_EGP);
79907b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "raw_hdrincl"))
80007b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_RAW, IPPROTO_RAW);
80107b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "tcp"))
80207b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_STREAM, 0);
80307b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "udp"))
80407b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_DGRAM, 0);
805b16ac920SSowmini Varadhan 	else if (!strcmp(cfg_test, "rds"))
806b16ac920SSowmini Varadhan 		do_test(PF_RDS, SOCK_SEQPACKET, 0);
80707b65c5bSWillem de Bruijn 	else
80807b65c5bSWillem de Bruijn 		error(1, 0, "unknown cfg_test %s", cfg_test);
80907b65c5bSWillem de Bruijn 
81007b65c5bSWillem de Bruijn 	return 0;
81107b65c5bSWillem de Bruijn }
812