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 = ð
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