18c2ecf20Sopenharmony_ci/* Evaluate MSG_ZEROCOPY 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * Send traffic between two processes over one of the supported 48c2ecf20Sopenharmony_ci * protocols and modes: 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * PF_INET/PF_INET6 78c2ecf20Sopenharmony_ci * - SOCK_STREAM 88c2ecf20Sopenharmony_ci * - SOCK_DGRAM 98c2ecf20Sopenharmony_ci * - SOCK_DGRAM with UDP_CORK 108c2ecf20Sopenharmony_ci * - SOCK_RAW 118c2ecf20Sopenharmony_ci * - SOCK_RAW with IP_HDRINCL 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * PF_PACKET 148c2ecf20Sopenharmony_ci * - SOCK_DGRAM 158c2ecf20Sopenharmony_ci * - SOCK_RAW 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * PF_RDS 188c2ecf20Sopenharmony_ci * - SOCK_SEQPACKET 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Start this program on two connected hosts, one in send mode and 218c2ecf20Sopenharmony_ci * the other with option '-r' to put it in receiver mode. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * If zerocopy mode ('-z') is enabled, the sender will verify that 248c2ecf20Sopenharmony_ci * the kernel queues completions on the error queue for all zerocopy 258c2ecf20Sopenharmony_ci * transfers. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define _GNU_SOURCE 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <arpa/inet.h> 318c2ecf20Sopenharmony_ci#include <error.h> 328c2ecf20Sopenharmony_ci#include <errno.h> 338c2ecf20Sopenharmony_ci#include <limits.h> 348c2ecf20Sopenharmony_ci#include <linux/errqueue.h> 358c2ecf20Sopenharmony_ci#include <linux/if_packet.h> 368c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 378c2ecf20Sopenharmony_ci#include <linux/socket.h> 388c2ecf20Sopenharmony_ci#include <linux/sockios.h> 398c2ecf20Sopenharmony_ci#include <net/ethernet.h> 408c2ecf20Sopenharmony_ci#include <net/if.h> 418c2ecf20Sopenharmony_ci#include <netinet/ip.h> 428c2ecf20Sopenharmony_ci#include <netinet/ip6.h> 438c2ecf20Sopenharmony_ci#include <netinet/tcp.h> 448c2ecf20Sopenharmony_ci#include <netinet/udp.h> 458c2ecf20Sopenharmony_ci#include <poll.h> 468c2ecf20Sopenharmony_ci#include <sched.h> 478c2ecf20Sopenharmony_ci#include <stdbool.h> 488c2ecf20Sopenharmony_ci#include <stdio.h> 498c2ecf20Sopenharmony_ci#include <stdint.h> 508c2ecf20Sopenharmony_ci#include <stdlib.h> 518c2ecf20Sopenharmony_ci#include <string.h> 528c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 538c2ecf20Sopenharmony_ci#include <sys/socket.h> 548c2ecf20Sopenharmony_ci#include <sys/stat.h> 558c2ecf20Sopenharmony_ci#include <sys/time.h> 568c2ecf20Sopenharmony_ci#include <sys/types.h> 578c2ecf20Sopenharmony_ci#include <sys/wait.h> 588c2ecf20Sopenharmony_ci#include <unistd.h> 598c2ecf20Sopenharmony_ci#include <linux/rds.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#ifndef SO_EE_ORIGIN_ZEROCOPY 628c2ecf20Sopenharmony_ci#define SO_EE_ORIGIN_ZEROCOPY 5 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#ifndef SO_ZEROCOPY 668c2ecf20Sopenharmony_ci#define SO_ZEROCOPY 60 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#ifndef SO_EE_CODE_ZEROCOPY_COPIED 708c2ecf20Sopenharmony_ci#define SO_EE_CODE_ZEROCOPY_COPIED 1 718c2ecf20Sopenharmony_ci#endif 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#ifndef MSG_ZEROCOPY 748c2ecf20Sopenharmony_ci#define MSG_ZEROCOPY 0x4000000 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int cfg_cork; 788c2ecf20Sopenharmony_cistatic bool cfg_cork_mixed; 798c2ecf20Sopenharmony_cistatic int cfg_cpu = -1; /* default: pin to last cpu */ 808c2ecf20Sopenharmony_cistatic int cfg_family = PF_UNSPEC; 818c2ecf20Sopenharmony_cistatic int cfg_ifindex = 1; 828c2ecf20Sopenharmony_cistatic int cfg_payload_len; 838c2ecf20Sopenharmony_cistatic int cfg_port = 8000; 848c2ecf20Sopenharmony_cistatic bool cfg_rx; 858c2ecf20Sopenharmony_cistatic int cfg_runtime_ms = 4200; 868c2ecf20Sopenharmony_cistatic int cfg_verbose; 878c2ecf20Sopenharmony_cistatic int cfg_waittime_ms = 500; 888c2ecf20Sopenharmony_cistatic bool cfg_zerocopy; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic socklen_t cfg_alen; 918c2ecf20Sopenharmony_cistatic struct sockaddr_storage cfg_dst_addr; 928c2ecf20Sopenharmony_cistatic struct sockaddr_storage cfg_src_addr; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic char payload[IP_MAXPACKET]; 958c2ecf20Sopenharmony_cistatic long packets, bytes, completions, expected_completions; 968c2ecf20Sopenharmony_cistatic int zerocopied = -1; 978c2ecf20Sopenharmony_cistatic uint32_t next_completion; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic unsigned long gettimeofday_ms(void) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct timeval tv; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci gettimeofday(&tv, NULL); 1048c2ecf20Sopenharmony_ci return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic uint16_t get_ip_csum(const uint16_t *start, int num_words) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned long sum = 0; 1108c2ecf20Sopenharmony_ci int i; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci for (i = 0; i < num_words; i++) 1138c2ecf20Sopenharmony_ci sum += start[i]; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci while (sum >> 16) 1168c2ecf20Sopenharmony_ci sum = (sum & 0xFFFF) + (sum >> 16); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return ~sum; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int do_setcpu(int cpu) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci cpu_set_t mask; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci CPU_ZERO(&mask); 1268c2ecf20Sopenharmony_ci CPU_SET(cpu, &mask); 1278c2ecf20Sopenharmony_ci if (sched_setaffinity(0, sizeof(mask), &mask)) 1288c2ecf20Sopenharmony_ci fprintf(stderr, "cpu: unable to pin, may increase variance.\n"); 1298c2ecf20Sopenharmony_ci else if (cfg_verbose) 1308c2ecf20Sopenharmony_ci fprintf(stderr, "cpu: %u\n", cpu); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void do_setsockopt(int fd, int level, int optname, int val) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci if (setsockopt(fd, level, optname, &val, sizeof(val))) 1388c2ecf20Sopenharmony_ci error(1, errno, "setsockopt %d.%d: %d", level, optname, val); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int do_poll(int fd, int events) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct pollfd pfd; 1448c2ecf20Sopenharmony_ci int ret; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pfd.events = events; 1478c2ecf20Sopenharmony_ci pfd.revents = 0; 1488c2ecf20Sopenharmony_ci pfd.fd = fd; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = poll(&pfd, 1, cfg_waittime_ms); 1518c2ecf20Sopenharmony_ci if (ret == -1) 1528c2ecf20Sopenharmony_ci error(1, errno, "poll"); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return ret && (pfd.revents & events); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int do_accept(int fd) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int fda = fd; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci fd = accept(fda, NULL, NULL); 1628c2ecf20Sopenharmony_ci if (fd == -1) 1638c2ecf20Sopenharmony_ci error(1, errno, "accept"); 1648c2ecf20Sopenharmony_ci if (close(fda)) 1658c2ecf20Sopenharmony_ci error(1, errno, "close listen sock"); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return fd; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct cmsghdr *cm; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!msg->msg_control) 1758c2ecf20Sopenharmony_ci error(1, errno, "NULL cookie"); 1768c2ecf20Sopenharmony_ci cm = (void *)msg->msg_control; 1778c2ecf20Sopenharmony_ci cm->cmsg_len = CMSG_LEN(sizeof(cookie)); 1788c2ecf20Sopenharmony_ci cm->cmsg_level = SOL_RDS; 1798c2ecf20Sopenharmony_ci cm->cmsg_type = RDS_CMSG_ZCOPY_COOKIE; 1808c2ecf20Sopenharmony_ci memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie)); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int ret, len, i, flags; 1868c2ecf20Sopenharmony_ci static uint32_t cookie; 1878c2ecf20Sopenharmony_ci char ckbuf[CMSG_SPACE(sizeof(cookie))]; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci len = 0; 1908c2ecf20Sopenharmony_ci for (i = 0; i < msg->msg_iovlen; i++) 1918c2ecf20Sopenharmony_ci len += msg->msg_iov[i].iov_len; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci flags = MSG_DONTWAIT; 1948c2ecf20Sopenharmony_ci if (do_zerocopy) { 1958c2ecf20Sopenharmony_ci flags |= MSG_ZEROCOPY; 1968c2ecf20Sopenharmony_ci if (domain == PF_RDS) { 1978c2ecf20Sopenharmony_ci memset(&msg->msg_control, 0, sizeof(msg->msg_control)); 1988c2ecf20Sopenharmony_ci msg->msg_controllen = CMSG_SPACE(sizeof(cookie)); 1998c2ecf20Sopenharmony_ci msg->msg_control = (struct cmsghdr *)ckbuf; 2008c2ecf20Sopenharmony_ci add_zcopy_cookie(msg, ++cookie); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = sendmsg(fd, msg, flags); 2058c2ecf20Sopenharmony_ci if (ret == -1 && errno == EAGAIN) 2068c2ecf20Sopenharmony_ci return false; 2078c2ecf20Sopenharmony_ci if (ret == -1) 2088c2ecf20Sopenharmony_ci error(1, errno, "send"); 2098c2ecf20Sopenharmony_ci if (cfg_verbose && ret != len) 2108c2ecf20Sopenharmony_ci fprintf(stderr, "send: ret=%u != %u\n", ret, len); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (len) { 2138c2ecf20Sopenharmony_ci packets++; 2148c2ecf20Sopenharmony_ci bytes += ret; 2158c2ecf20Sopenharmony_ci if (do_zerocopy && ret) 2168c2ecf20Sopenharmony_ci expected_completions++; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci if (do_zerocopy && domain == PF_RDS) { 2198c2ecf20Sopenharmony_ci msg->msg_control = NULL; 2208c2ecf20Sopenharmony_ci msg->msg_controllen = 0; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return true; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void do_sendmsg_corked(int fd, struct msghdr *msg) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci bool do_zerocopy = cfg_zerocopy; 2298c2ecf20Sopenharmony_ci int i, payload_len, extra_len; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* split up the packet. for non-multiple, make first buffer longer */ 2328c2ecf20Sopenharmony_ci payload_len = cfg_payload_len / cfg_cork; 2338c2ecf20Sopenharmony_ci extra_len = cfg_payload_len - (cfg_cork * payload_len); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 1); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (i = 0; i < cfg_cork; i++) { 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* in mixed-frags mode, alternate zerocopy and copy frags 2408c2ecf20Sopenharmony_ci * start with non-zerocopy, to ensure attach later works 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (cfg_cork_mixed) 2438c2ecf20Sopenharmony_ci do_zerocopy = (i & 1); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci msg->msg_iov[0].iov_len = payload_len + extra_len; 2468c2ecf20Sopenharmony_ci extra_len = 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci do_sendmsg(fd, msg, do_zerocopy, 2498c2ecf20Sopenharmony_ci (cfg_dst_addr.ss_family == AF_INET ? 2508c2ecf20Sopenharmony_ci PF_INET : PF_INET6)); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int setup_iph(struct iphdr *iph, uint16_t payload_len) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct sockaddr_in *daddr = (void *) &cfg_dst_addr; 2598c2ecf20Sopenharmony_ci struct sockaddr_in *saddr = (void *) &cfg_src_addr; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci memset(iph, 0, sizeof(*iph)); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci iph->version = 4; 2648c2ecf20Sopenharmony_ci iph->tos = 0; 2658c2ecf20Sopenharmony_ci iph->ihl = 5; 2668c2ecf20Sopenharmony_ci iph->ttl = 2; 2678c2ecf20Sopenharmony_ci iph->saddr = saddr->sin_addr.s_addr; 2688c2ecf20Sopenharmony_ci iph->daddr = daddr->sin_addr.s_addr; 2698c2ecf20Sopenharmony_ci iph->protocol = IPPROTO_EGP; 2708c2ecf20Sopenharmony_ci iph->tot_len = htons(sizeof(*iph) + payload_len); 2718c2ecf20Sopenharmony_ci iph->check = get_ip_csum((void *) iph, iph->ihl << 1); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return sizeof(*iph); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct sockaddr_in6 *daddr = (void *) &cfg_dst_addr; 2798c2ecf20Sopenharmony_ci struct sockaddr_in6 *saddr = (void *) &cfg_src_addr; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci memset(ip6h, 0, sizeof(*ip6h)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ip6h->version = 6; 2848c2ecf20Sopenharmony_ci ip6h->payload_len = htons(payload_len); 2858c2ecf20Sopenharmony_ci ip6h->nexthdr = IPPROTO_EGP; 2868c2ecf20Sopenharmony_ci ip6h->hop_limit = 2; 2878c2ecf20Sopenharmony_ci ip6h->saddr = saddr->sin6_addr; 2888c2ecf20Sopenharmony_ci ip6h->daddr = daddr->sin6_addr; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return sizeof(*ip6h); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void setup_sockaddr(int domain, const char *str_addr, 2958c2ecf20Sopenharmony_ci struct sockaddr_storage *sockaddr) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct sockaddr_in6 *addr6 = (void *) sockaddr; 2988c2ecf20Sopenharmony_ci struct sockaddr_in *addr4 = (void *) sockaddr; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci switch (domain) { 3018c2ecf20Sopenharmony_ci case PF_INET: 3028c2ecf20Sopenharmony_ci memset(addr4, 0, sizeof(*addr4)); 3038c2ecf20Sopenharmony_ci addr4->sin_family = AF_INET; 3048c2ecf20Sopenharmony_ci addr4->sin_port = htons(cfg_port); 3058c2ecf20Sopenharmony_ci if (str_addr && 3068c2ecf20Sopenharmony_ci inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1) 3078c2ecf20Sopenharmony_ci error(1, 0, "ipv4 parse error: %s", str_addr); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci case PF_INET6: 3108c2ecf20Sopenharmony_ci memset(addr6, 0, sizeof(*addr6)); 3118c2ecf20Sopenharmony_ci addr6->sin6_family = AF_INET6; 3128c2ecf20Sopenharmony_ci addr6->sin6_port = htons(cfg_port); 3138c2ecf20Sopenharmony_ci if (str_addr && 3148c2ecf20Sopenharmony_ci inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1) 3158c2ecf20Sopenharmony_ci error(1, 0, "ipv6 parse error: %s", str_addr); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci error(1, 0, "illegal domain"); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int do_setup_tx(int domain, int type, int protocol) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci int fd; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci fd = socket(domain, type, protocol); 3278c2ecf20Sopenharmony_ci if (fd == -1) 3288c2ecf20Sopenharmony_ci error(1, errno, "socket t"); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci do_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 1 << 21); 3318c2ecf20Sopenharmony_ci if (cfg_zerocopy) 3328c2ecf20Sopenharmony_ci do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (domain != PF_PACKET && domain != PF_RDS) 3358c2ecf20Sopenharmony_ci if (connect(fd, (void *) &cfg_dst_addr, cfg_alen)) 3368c2ecf20Sopenharmony_ci error(1, errno, "connect"); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (domain == PF_RDS) { 3398c2ecf20Sopenharmony_ci if (bind(fd, (void *) &cfg_src_addr, cfg_alen)) 3408c2ecf20Sopenharmony_ci error(1, errno, "bind"); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return fd; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic uint32_t do_process_zerocopy_cookies(struct rds_zcopy_cookies *ck) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci int i; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (ck->num > RDS_MAX_ZCOOKIES) 3518c2ecf20Sopenharmony_ci error(1, 0, "Returned %d cookies, max expected %d\n", 3528c2ecf20Sopenharmony_ci ck->num, RDS_MAX_ZCOOKIES); 3538c2ecf20Sopenharmony_ci for (i = 0; i < ck->num; i++) 3548c2ecf20Sopenharmony_ci if (cfg_verbose >= 2) 3558c2ecf20Sopenharmony_ci fprintf(stderr, "%d\n", ck->cookies[i]); 3568c2ecf20Sopenharmony_ci return ck->num; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic bool do_recvmsg_completion(int fd) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci char cmsgbuf[CMSG_SPACE(sizeof(struct rds_zcopy_cookies))]; 3628c2ecf20Sopenharmony_ci struct rds_zcopy_cookies *ck; 3638c2ecf20Sopenharmony_ci struct cmsghdr *cmsg; 3648c2ecf20Sopenharmony_ci struct msghdr msg; 3658c2ecf20Sopenharmony_ci bool ret = false; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 3688c2ecf20Sopenharmony_ci msg.msg_control = cmsgbuf; 3698c2ecf20Sopenharmony_ci msg.msg_controllen = sizeof(cmsgbuf); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (recvmsg(fd, &msg, MSG_DONTWAIT)) 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (msg.msg_flags & MSG_CTRUNC) 3758c2ecf20Sopenharmony_ci error(1, errno, "recvmsg notification: truncated"); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 3788c2ecf20Sopenharmony_ci if (cmsg->cmsg_level == SOL_RDS && 3798c2ecf20Sopenharmony_ci cmsg->cmsg_type == RDS_CMSG_ZCOPY_COMPLETION) { 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ck = (struct rds_zcopy_cookies *)CMSG_DATA(cmsg); 3828c2ecf20Sopenharmony_ci completions += do_process_zerocopy_cookies(ck); 3838c2ecf20Sopenharmony_ci ret = true; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci error(0, 0, "ignoring cmsg at level %d type %d\n", 3878c2ecf20Sopenharmony_ci cmsg->cmsg_level, cmsg->cmsg_type); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic bool do_recv_completion(int fd, int domain) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct sock_extended_err *serr; 3958c2ecf20Sopenharmony_ci struct msghdr msg = {}; 3968c2ecf20Sopenharmony_ci struct cmsghdr *cm; 3978c2ecf20Sopenharmony_ci uint32_t hi, lo, range; 3988c2ecf20Sopenharmony_ci int ret, zerocopy; 3998c2ecf20Sopenharmony_ci char control[100]; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (domain == PF_RDS) 4028c2ecf20Sopenharmony_ci return do_recvmsg_completion(fd); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci msg.msg_control = control; 4058c2ecf20Sopenharmony_ci msg.msg_controllen = sizeof(control); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ret = recvmsg(fd, &msg, MSG_ERRQUEUE); 4088c2ecf20Sopenharmony_ci if (ret == -1 && errno == EAGAIN) 4098c2ecf20Sopenharmony_ci return false; 4108c2ecf20Sopenharmony_ci if (ret == -1) 4118c2ecf20Sopenharmony_ci error(1, errno, "recvmsg notification"); 4128c2ecf20Sopenharmony_ci if (msg.msg_flags & MSG_CTRUNC) 4138c2ecf20Sopenharmony_ci error(1, errno, "recvmsg notification: truncated"); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci cm = CMSG_FIRSTHDR(&msg); 4168c2ecf20Sopenharmony_ci if (!cm) 4178c2ecf20Sopenharmony_ci error(1, 0, "cmsg: no cmsg"); 4188c2ecf20Sopenharmony_ci if (!((cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) || 4198c2ecf20Sopenharmony_ci (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_RECVERR) || 4208c2ecf20Sopenharmony_ci (cm->cmsg_level == SOL_PACKET && cm->cmsg_type == PACKET_TX_TIMESTAMP))) 4218c2ecf20Sopenharmony_ci error(1, 0, "serr: wrong type: %d.%d", 4228c2ecf20Sopenharmony_ci cm->cmsg_level, cm->cmsg_type); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci serr = (void *) CMSG_DATA(cm); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) 4278c2ecf20Sopenharmony_ci error(1, 0, "serr: wrong origin: %u", serr->ee_origin); 4288c2ecf20Sopenharmony_ci if (serr->ee_errno != 0) 4298c2ecf20Sopenharmony_ci error(1, 0, "serr: wrong error code: %u", serr->ee_errno); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci hi = serr->ee_data; 4328c2ecf20Sopenharmony_ci lo = serr->ee_info; 4338c2ecf20Sopenharmony_ci range = hi - lo + 1; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Detect notification gaps. These should not happen often, if at all. 4368c2ecf20Sopenharmony_ci * Gaps can occur due to drops, reordering and retransmissions. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci if (lo != next_completion) 4398c2ecf20Sopenharmony_ci fprintf(stderr, "gap: %u..%u does not append to %u\n", 4408c2ecf20Sopenharmony_ci lo, hi, next_completion); 4418c2ecf20Sopenharmony_ci next_completion = hi + 1; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED); 4448c2ecf20Sopenharmony_ci if (zerocopied == -1) 4458c2ecf20Sopenharmony_ci zerocopied = zerocopy; 4468c2ecf20Sopenharmony_ci else if (zerocopied != zerocopy) { 4478c2ecf20Sopenharmony_ci fprintf(stderr, "serr: inconsistent\n"); 4488c2ecf20Sopenharmony_ci zerocopied = zerocopy; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (cfg_verbose >= 2) 4528c2ecf20Sopenharmony_ci fprintf(stderr, "completed: %u (h=%u l=%u)\n", 4538c2ecf20Sopenharmony_ci range, hi, lo); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci completions += range; 4568c2ecf20Sopenharmony_ci return true; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* Read all outstanding messages on the errqueue */ 4608c2ecf20Sopenharmony_cistatic void do_recv_completions(int fd, int domain) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci while (do_recv_completion(fd, domain)) {} 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/* Wait for all remaining completions on the errqueue */ 4668c2ecf20Sopenharmony_cistatic void do_recv_remaining_completions(int fd, int domain) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci int64_t tstop = gettimeofday_ms() + cfg_waittime_ms; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci while (completions < expected_completions && 4718c2ecf20Sopenharmony_ci gettimeofday_ms() < tstop) { 4728c2ecf20Sopenharmony_ci if (do_poll(fd, domain == PF_RDS ? POLLIN : POLLERR)) 4738c2ecf20Sopenharmony_ci do_recv_completions(fd, domain); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (completions < expected_completions) 4778c2ecf20Sopenharmony_ci fprintf(stderr, "missing notifications: %lu < %lu\n", 4788c2ecf20Sopenharmony_ci completions, expected_completions); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void do_tx(int domain, int type, int protocol) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct iovec iov[3] = { {0} }; 4848c2ecf20Sopenharmony_ci struct sockaddr_ll laddr; 4858c2ecf20Sopenharmony_ci struct msghdr msg = {0}; 4868c2ecf20Sopenharmony_ci struct ethhdr eth; 4878c2ecf20Sopenharmony_ci union { 4888c2ecf20Sopenharmony_ci struct ipv6hdr ip6h; 4898c2ecf20Sopenharmony_ci struct iphdr iph; 4908c2ecf20Sopenharmony_ci } nh; 4918c2ecf20Sopenharmony_ci uint64_t tstop; 4928c2ecf20Sopenharmony_ci int fd; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci fd = do_setup_tx(domain, type, protocol); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (domain == PF_PACKET) { 4978c2ecf20Sopenharmony_ci uint16_t proto = cfg_family == PF_INET ? ETH_P_IP : ETH_P_IPV6; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* sock_raw passes ll header as data */ 5008c2ecf20Sopenharmony_ci if (type == SOCK_RAW) { 5018c2ecf20Sopenharmony_ci memset(eth.h_dest, 0x06, ETH_ALEN); 5028c2ecf20Sopenharmony_ci memset(eth.h_source, 0x02, ETH_ALEN); 5038c2ecf20Sopenharmony_ci eth.h_proto = htons(proto); 5048c2ecf20Sopenharmony_ci iov[0].iov_base = ð 5058c2ecf20Sopenharmony_ci iov[0].iov_len = sizeof(eth); 5068c2ecf20Sopenharmony_ci msg.msg_iovlen++; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* both sock_raw and sock_dgram expect name */ 5108c2ecf20Sopenharmony_ci memset(&laddr, 0, sizeof(laddr)); 5118c2ecf20Sopenharmony_ci laddr.sll_family = AF_PACKET; 5128c2ecf20Sopenharmony_ci laddr.sll_ifindex = cfg_ifindex; 5138c2ecf20Sopenharmony_ci laddr.sll_protocol = htons(proto); 5148c2ecf20Sopenharmony_ci laddr.sll_halen = ETH_ALEN; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci memset(laddr.sll_addr, 0x06, ETH_ALEN); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci msg.msg_name = &laddr; 5198c2ecf20Sopenharmony_ci msg.msg_namelen = sizeof(laddr); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* packet and raw sockets with hdrincl must pass network header */ 5238c2ecf20Sopenharmony_ci if (domain == PF_PACKET || protocol == IPPROTO_RAW) { 5248c2ecf20Sopenharmony_ci if (cfg_family == PF_INET) 5258c2ecf20Sopenharmony_ci iov[1].iov_len = setup_iph(&nh.iph, cfg_payload_len); 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci iov[1].iov_len = setup_ip6h(&nh.ip6h, cfg_payload_len); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci iov[1].iov_base = (void *) &nh; 5308c2ecf20Sopenharmony_ci msg.msg_iovlen++; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (domain == PF_RDS) { 5348c2ecf20Sopenharmony_ci msg.msg_name = &cfg_dst_addr; 5358c2ecf20Sopenharmony_ci msg.msg_namelen = (cfg_dst_addr.ss_family == AF_INET ? 5368c2ecf20Sopenharmony_ci sizeof(struct sockaddr_in) : 5378c2ecf20Sopenharmony_ci sizeof(struct sockaddr_in6)); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci iov[2].iov_base = payload; 5418c2ecf20Sopenharmony_ci iov[2].iov_len = cfg_payload_len; 5428c2ecf20Sopenharmony_ci msg.msg_iovlen++; 5438c2ecf20Sopenharmony_ci msg.msg_iov = &iov[3 - msg.msg_iovlen]; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci tstop = gettimeofday_ms() + cfg_runtime_ms; 5468c2ecf20Sopenharmony_ci do { 5478c2ecf20Sopenharmony_ci if (cfg_cork) 5488c2ecf20Sopenharmony_ci do_sendmsg_corked(fd, &msg); 5498c2ecf20Sopenharmony_ci else 5508c2ecf20Sopenharmony_ci do_sendmsg(fd, &msg, cfg_zerocopy, domain); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci while (!do_poll(fd, POLLOUT)) { 5538c2ecf20Sopenharmony_ci if (cfg_zerocopy) 5548c2ecf20Sopenharmony_ci do_recv_completions(fd, domain); 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci } while (gettimeofday_ms() < tstop); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (cfg_zerocopy) 5608c2ecf20Sopenharmony_ci do_recv_remaining_completions(fd, domain); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (close(fd)) 5638c2ecf20Sopenharmony_ci error(1, errno, "close"); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n", 5668c2ecf20Sopenharmony_ci packets, bytes >> 20, completions, 5678c2ecf20Sopenharmony_ci zerocopied == 1 ? 'y' : 'n'); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int do_setup_rx(int domain, int type, int protocol) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci int fd; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* If tx over PF_PACKET, rx over PF_INET(6)/SOCK_RAW, 5758c2ecf20Sopenharmony_ci * to recv the only copy of the packet, not a clone 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ci if (domain == PF_PACKET) 5788c2ecf20Sopenharmony_ci error(1, 0, "Use PF_INET/SOCK_RAW to read"); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (type == SOCK_RAW && protocol == IPPROTO_RAW) 5818c2ecf20Sopenharmony_ci error(1, 0, "IPPROTO_RAW: not supported on Rx"); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci fd = socket(domain, type, protocol); 5848c2ecf20Sopenharmony_ci if (fd == -1) 5858c2ecf20Sopenharmony_ci error(1, errno, "socket r"); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci do_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 1 << 21); 5888c2ecf20Sopenharmony_ci do_setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, 1 << 16); 5898c2ecf20Sopenharmony_ci do_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (bind(fd, (void *) &cfg_dst_addr, cfg_alen)) 5928c2ecf20Sopenharmony_ci error(1, errno, "bind"); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (type == SOCK_STREAM) { 5958c2ecf20Sopenharmony_ci if (listen(fd, 1)) 5968c2ecf20Sopenharmony_ci error(1, errno, "listen"); 5978c2ecf20Sopenharmony_ci fd = do_accept(fd); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return fd; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* Flush all outstanding bytes for the tcp receive queue */ 6048c2ecf20Sopenharmony_cistatic void do_flush_tcp(int fd) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int ret; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* MSG_TRUNC flushes up to len bytes */ 6098c2ecf20Sopenharmony_ci ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT); 6108c2ecf20Sopenharmony_ci if (ret == -1 && errno == EAGAIN) 6118c2ecf20Sopenharmony_ci return; 6128c2ecf20Sopenharmony_ci if (ret == -1) 6138c2ecf20Sopenharmony_ci error(1, errno, "flush"); 6148c2ecf20Sopenharmony_ci if (!ret) 6158c2ecf20Sopenharmony_ci return; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci packets++; 6188c2ecf20Sopenharmony_ci bytes += ret; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/* Flush all outstanding datagrams. Verify first few bytes of each. */ 6228c2ecf20Sopenharmony_cistatic void do_flush_datagram(int fd, int type) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci int ret, off = 0; 6258c2ecf20Sopenharmony_ci char buf[64]; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* MSG_TRUNC will return full datagram length */ 6288c2ecf20Sopenharmony_ci ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_TRUNC); 6298c2ecf20Sopenharmony_ci if (ret == -1 && errno == EAGAIN) 6308c2ecf20Sopenharmony_ci return; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* raw ipv4 return with header, raw ipv6 without */ 6338c2ecf20Sopenharmony_ci if (cfg_family == PF_INET && type == SOCK_RAW) { 6348c2ecf20Sopenharmony_ci off += sizeof(struct iphdr); 6358c2ecf20Sopenharmony_ci ret -= sizeof(struct iphdr); 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (ret == -1) 6398c2ecf20Sopenharmony_ci error(1, errno, "recv"); 6408c2ecf20Sopenharmony_ci if (ret != cfg_payload_len) 6418c2ecf20Sopenharmony_ci error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len); 6428c2ecf20Sopenharmony_ci if (ret > sizeof(buf) - off) 6438c2ecf20Sopenharmony_ci ret = sizeof(buf) - off; 6448c2ecf20Sopenharmony_ci if (memcmp(buf + off, payload, ret)) 6458c2ecf20Sopenharmony_ci error(1, 0, "recv: data mismatch"); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci packets++; 6488c2ecf20Sopenharmony_ci bytes += cfg_payload_len; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic void do_rx(int domain, int type, int protocol) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci const int cfg_receiver_wait_ms = 400; 6548c2ecf20Sopenharmony_ci uint64_t tstop; 6558c2ecf20Sopenharmony_ci int fd; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci fd = do_setup_rx(domain, type, protocol); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci tstop = gettimeofday_ms() + cfg_runtime_ms + cfg_receiver_wait_ms; 6608c2ecf20Sopenharmony_ci do { 6618c2ecf20Sopenharmony_ci if (type == SOCK_STREAM) 6628c2ecf20Sopenharmony_ci do_flush_tcp(fd); 6638c2ecf20Sopenharmony_ci else 6648c2ecf20Sopenharmony_ci do_flush_datagram(fd, type); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci do_poll(fd, POLLIN); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci } while (gettimeofday_ms() < tstop); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (close(fd)) 6718c2ecf20Sopenharmony_ci error(1, errno, "close"); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci fprintf(stderr, "rx=%lu (%lu MB)\n", packets, bytes >> 20); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic void do_test(int domain, int type, int protocol) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci int i; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (cfg_cork && (domain == PF_PACKET || type != SOCK_DGRAM)) 6818c2ecf20Sopenharmony_ci error(1, 0, "can only cork udp sockets"); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci do_setcpu(cfg_cpu); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci for (i = 0; i < IP_MAXPACKET; i++) 6868c2ecf20Sopenharmony_ci payload[i] = 'a' + (i % 26); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (cfg_rx) 6898c2ecf20Sopenharmony_ci do_rx(domain, type, protocol); 6908c2ecf20Sopenharmony_ci else 6918c2ecf20Sopenharmony_ci do_tx(domain, type, protocol); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic void usage(const char *filepath) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci error(1, 0, "Usage: %s [options] <test>", filepath); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void parse_opts(int argc, char **argv) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci const int max_payload_len = sizeof(payload) - 7028c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr) - 7038c2ecf20Sopenharmony_ci sizeof(struct tcphdr) - 7048c2ecf20Sopenharmony_ci 40 /* max tcp options */; 7058c2ecf20Sopenharmony_ci int c; 7068c2ecf20Sopenharmony_ci char *daddr = NULL, *saddr = NULL; 7078c2ecf20Sopenharmony_ci char *cfg_test; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci cfg_payload_len = max_payload_len; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci while ((c = getopt(argc, argv, "46c:C:D:i:mp:rs:S:t:vz")) != -1) { 7128c2ecf20Sopenharmony_ci switch (c) { 7138c2ecf20Sopenharmony_ci case '4': 7148c2ecf20Sopenharmony_ci if (cfg_family != PF_UNSPEC) 7158c2ecf20Sopenharmony_ci error(1, 0, "Pass one of -4 or -6"); 7168c2ecf20Sopenharmony_ci cfg_family = PF_INET; 7178c2ecf20Sopenharmony_ci cfg_alen = sizeof(struct sockaddr_in); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case '6': 7208c2ecf20Sopenharmony_ci if (cfg_family != PF_UNSPEC) 7218c2ecf20Sopenharmony_ci error(1, 0, "Pass one of -4 or -6"); 7228c2ecf20Sopenharmony_ci cfg_family = PF_INET6; 7238c2ecf20Sopenharmony_ci cfg_alen = sizeof(struct sockaddr_in6); 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci case 'c': 7268c2ecf20Sopenharmony_ci cfg_cork = strtol(optarg, NULL, 0); 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci case 'C': 7298c2ecf20Sopenharmony_ci cfg_cpu = strtol(optarg, NULL, 0); 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci case 'D': 7328c2ecf20Sopenharmony_ci daddr = optarg; 7338c2ecf20Sopenharmony_ci break; 7348c2ecf20Sopenharmony_ci case 'i': 7358c2ecf20Sopenharmony_ci cfg_ifindex = if_nametoindex(optarg); 7368c2ecf20Sopenharmony_ci if (cfg_ifindex == 0) 7378c2ecf20Sopenharmony_ci error(1, errno, "invalid iface: %s", optarg); 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case 'm': 7408c2ecf20Sopenharmony_ci cfg_cork_mixed = true; 7418c2ecf20Sopenharmony_ci break; 7428c2ecf20Sopenharmony_ci case 'p': 7438c2ecf20Sopenharmony_ci cfg_port = strtoul(optarg, NULL, 0); 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci case 'r': 7468c2ecf20Sopenharmony_ci cfg_rx = true; 7478c2ecf20Sopenharmony_ci break; 7488c2ecf20Sopenharmony_ci case 's': 7498c2ecf20Sopenharmony_ci cfg_payload_len = strtoul(optarg, NULL, 0); 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci case 'S': 7528c2ecf20Sopenharmony_ci saddr = optarg; 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci case 't': 7558c2ecf20Sopenharmony_ci cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000; 7568c2ecf20Sopenharmony_ci break; 7578c2ecf20Sopenharmony_ci case 'v': 7588c2ecf20Sopenharmony_ci cfg_verbose++; 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci case 'z': 7618c2ecf20Sopenharmony_ci cfg_zerocopy = true; 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci cfg_test = argv[argc - 1]; 7678c2ecf20Sopenharmony_ci if (strcmp(cfg_test, "rds") == 0) { 7688c2ecf20Sopenharmony_ci if (!daddr) 7698c2ecf20Sopenharmony_ci error(1, 0, "-D <server addr> required for PF_RDS\n"); 7708c2ecf20Sopenharmony_ci if (!cfg_rx && !saddr) 7718c2ecf20Sopenharmony_ci error(1, 0, "-S <client addr> required for PF_RDS\n"); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci setup_sockaddr(cfg_family, daddr, &cfg_dst_addr); 7748c2ecf20Sopenharmony_ci setup_sockaddr(cfg_family, saddr, &cfg_src_addr); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (cfg_payload_len > max_payload_len) 7778c2ecf20Sopenharmony_ci error(1, 0, "-s: payload exceeds max (%d)", max_payload_len); 7788c2ecf20Sopenharmony_ci if (cfg_cork_mixed && (!cfg_zerocopy || !cfg_cork)) 7798c2ecf20Sopenharmony_ci error(1, 0, "-m: cork_mixed requires corking and zerocopy"); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (optind != argc - 1) 7828c2ecf20Sopenharmony_ci usage(argv[0]); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ciint main(int argc, char **argv) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci const char *cfg_test; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci parse_opts(argc, argv); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci cfg_test = argv[argc - 1]; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (!strcmp(cfg_test, "packet")) 7948c2ecf20Sopenharmony_ci do_test(PF_PACKET, SOCK_RAW, 0); 7958c2ecf20Sopenharmony_ci else if (!strcmp(cfg_test, "packet_dgram")) 7968c2ecf20Sopenharmony_ci do_test(PF_PACKET, SOCK_DGRAM, 0); 7978c2ecf20Sopenharmony_ci else if (!strcmp(cfg_test, "raw")) 7988c2ecf20Sopenharmony_ci do_test(cfg_family, SOCK_RAW, IPPROTO_EGP); 7998c2ecf20Sopenharmony_ci else if (!strcmp(cfg_test, "raw_hdrincl")) 8008c2ecf20Sopenharmony_ci do_test(cfg_family, SOCK_RAW, IPPROTO_RAW); 8018c2ecf20Sopenharmony_ci else if (!strcmp(cfg_test, "tcp")) 8028c2ecf20Sopenharmony_ci do_test(cfg_family, SOCK_STREAM, 0); 8038c2ecf20Sopenharmony_ci else if (!strcmp(cfg_test, "udp")) 8048c2ecf20Sopenharmony_ci do_test(cfg_family, SOCK_DGRAM, 0); 8058c2ecf20Sopenharmony_ci else if (!strcmp(cfg_test, "rds")) 8068c2ecf20Sopenharmony_ci do_test(PF_RDS, SOCK_SEQPACKET, 0); 8078c2ecf20Sopenharmony_ci else 8088c2ecf20Sopenharmony_ci error(1, 0, "unknown cfg_test %s", cfg_test); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 812