1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/errqueue.h>
9 #include <linux/net_tstamp.h>
10 #include <netinet/if_ether.h>
11 #include <netinet/in.h>
12 #include <netinet/ip.h>
13 #include <netinet/ip6.h>
14 #include <netinet/udp.h>
22 #include <sys/socket.h>
25 #include <sys/types.h>
28 #include "../kselftest.h"
31 #define ETH_MAX_MTU 0xFFFFU
35 #define UDP_SEGMENT 103
39 #define SO_ZEROCOPY 60
42 #ifndef SO_EE_ORIGIN_ZEROCOPY
43 #define SO_EE_ORIGIN_ZEROCOPY 5
47 #define MSG_ZEROCOPY 0x4000000
56 static bool cfg_cache_trash;
57 static int cfg_cpu = -1;
58 static int cfg_connected = true;
59 static int cfg_family = PF_UNSPEC;
60 static uint16_t cfg_mss;
61 static int cfg_payload_len = (1472 * 42);
62 static int cfg_port = 8000;
63 static int cfg_runtime_ms = -1;
65 static int cfg_poll_loop_timeout_ms = 2000;
66 static bool cfg_segment;
67 static bool cfg_sendmmsg;
69 static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE;
70 static bool cfg_tx_tstamp;
71 static bool cfg_audit;
72 static bool cfg_verbose;
73 static bool cfg_zerocopy;
74 static int cfg_msg_nr;
75 static uint16_t cfg_gso_size;
76 static unsigned long total_num_msgs;
77 static unsigned long total_num_sends;
78 static unsigned long stat_tx_ts;
79 static unsigned long stat_tx_ts_errors;
80 static unsigned long tstart;
81 static unsigned long tend;
82 static unsigned long stat_zcopies;
84 static socklen_t cfg_alen;
85 static struct sockaddr_storage cfg_dst_addr;
87 static bool interrupted;
88 static char buf[NUM_PKT][ETH_MAX_MTU];
90 static void sigint_handler(int signum)
96 static unsigned long gettimeofday_ms(void)
100 gettimeofday(&tv, NULL);
101 return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
104 static int set_cpu(int cpu)
110 if (sched_setaffinity(0, sizeof(mask), &mask))
111 error(1, 0, "setaffinity %d", cpu);
116 static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
118 struct sockaddr_in6 *addr6 = (void *) sockaddr;
119 struct sockaddr_in *addr4 = (void *) sockaddr;
123 addr4->sin_family = AF_INET;
124 addr4->sin_port = htons(cfg_port);
125 if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
126 error(1, 0, "ipv4 parse error: %s", str_addr);
129 addr6->sin6_family = AF_INET6;
130 addr6->sin6_port = htons(cfg_port);
131 if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
132 error(1, 0, "ipv6 parse error: %s", str_addr);
135 error(1, 0, "illegal domain");
139 static void flush_cmsg(struct cmsghdr *cmsg)
141 struct sock_extended_err *err;
142 struct scm_timestamping *tss;
147 switch (cmsg->cmsg_level) {
149 if (cmsg->cmsg_type == SO_TIMESTAMPING) {
150 i = (cfg_tx_ts == SOF_TIMESTAMPING_TX_HARDWARE) ? 2 : 0;
151 tss = (struct scm_timestamping *)CMSG_DATA(cmsg);
152 if (tss->ts[i].tv_sec == 0)
155 error(1, 0, "unknown SOL_SOCKET cmsg type=%u\n",
161 switch (cmsg->cmsg_type) {
165 err = (struct sock_extended_err *)CMSG_DATA(cmsg);
166 switch (err->ee_origin) {
167 case SO_EE_ORIGIN_TIMESTAMPING:
168 /* Got a TX timestamp from error queue */
171 case SO_EE_ORIGIN_ICMP:
172 case SO_EE_ORIGIN_ICMP6:
175 "received ICMP error: type=%u, code=%u\n",
176 err->ee_type, err->ee_code);
178 case SO_EE_ORIGIN_ZEROCOPY:
182 /* range of IDs acknowledged */
183 stat_zcopies += hi - lo + 1;
186 case SO_EE_ORIGIN_LOCAL:
189 "received packet with local origin: %u\n",
193 error(0, 1, "received packet with origin: %u",
199 error(0, 1, "unknown IP msg type=%u\n",
205 error(0, 1, "unknown cmsg level=%u\n",
210 static void flush_errqueue_recv(int fd)
212 char control[CMSG_SPACE(sizeof(struct scm_timestamping)) +
213 CMSG_SPACE(sizeof(struct sock_extended_err)) +
214 CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
215 struct msghdr msg = {0};
216 struct cmsghdr *cmsg;
220 msg.msg_control = control;
221 msg.msg_controllen = sizeof(control);
222 ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
223 if (ret == -1 && errno == EAGAIN)
226 error(1, errno, "errqueue");
227 if (msg.msg_flags != MSG_ERRQUEUE)
228 error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
230 for (cmsg = CMSG_FIRSTHDR(&msg);
232 cmsg = CMSG_NXTHDR(&msg, cmsg))
239 static void flush_errqueue(int fd, const bool do_poll,
240 unsigned long poll_timeout, const bool poll_err)
243 struct pollfd fds = {0};
247 ret = poll(&fds, 1, poll_timeout);
249 if ((cfg_verbose) && (poll_err))
250 fprintf(stderr, "poll timeout\n");
251 } else if (ret < 0) {
252 error(1, errno, "poll");
256 flush_errqueue_recv(fd);
259 static void flush_errqueue_retry(int fd, unsigned long num_sends)
261 unsigned long tnow, tstop;
262 bool first_try = true;
264 tnow = gettimeofday_ms();
265 tstop = tnow + cfg_poll_loop_timeout_ms;
267 flush_errqueue(fd, true, tstop - tnow, first_try);
269 tnow = gettimeofday_ms();
270 } while ((stat_zcopies != num_sends) && (tnow < tstop));
273 static int send_tcp(int fd, char *data)
275 int ret, done = 0, count = 0;
277 while (done < cfg_payload_len) {
278 ret = send(fd, data + done, cfg_payload_len - done,
279 cfg_zerocopy ? MSG_ZEROCOPY : 0);
281 error(1, errno, "write");
290 static int send_udp(int fd, char *data)
292 int ret, total_len, len, count = 0;
294 total_len = cfg_payload_len;
297 len = total_len < cfg_mss ? total_len : cfg_mss;
299 ret = sendto(fd, data, len, cfg_zerocopy ? MSG_ZEROCOPY : 0,
300 cfg_connected ? NULL : (void *)&cfg_dst_addr,
301 cfg_connected ? 0 : cfg_alen);
303 error(1, errno, "write");
305 error(1, errno, "write: %uB != %uB\n", ret, len);
314 static void send_ts_cmsg(struct cmsghdr *cm)
318 cm->cmsg_level = SOL_SOCKET;
319 cm->cmsg_type = SO_TIMESTAMPING;
320 cm->cmsg_len = CMSG_LEN(sizeof(cfg_tx_ts));
321 valp = (void *)CMSG_DATA(cm);
325 static int send_udp_sendmmsg(int fd, char *data)
327 char control[CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
328 const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN;
329 struct mmsghdr mmsgs[max_nr_msg];
330 struct iovec iov[max_nr_msg];
331 unsigned int off = 0, left;
332 size_t msg_controllen = 0;
335 memset(mmsgs, 0, sizeof(mmsgs));
338 struct msghdr msg = {0};
339 struct cmsghdr *cmsg;
341 msg.msg_control = control;
342 msg.msg_controllen = sizeof(control);
343 cmsg = CMSG_FIRSTHDR(&msg);
345 msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
348 left = cfg_payload_len;
351 error(1, 0, "sendmmsg: exceeds max_nr_msg");
353 iov[i].iov_base = data + off;
354 iov[i].iov_len = cfg_mss < left ? cfg_mss : left;
356 mmsgs[i].msg_hdr.msg_iov = iov + i;
357 mmsgs[i].msg_hdr.msg_iovlen = 1;
359 mmsgs[i].msg_hdr.msg_name = (void *)&cfg_dst_addr;
360 mmsgs[i].msg_hdr.msg_namelen = cfg_alen;
361 if (msg_controllen) {
362 mmsgs[i].msg_hdr.msg_control = control;
363 mmsgs[i].msg_hdr.msg_controllen = msg_controllen;
366 off += iov[i].iov_len;
367 left -= iov[i].iov_len;
371 ret = sendmmsg(fd, mmsgs, i, cfg_zerocopy ? MSG_ZEROCOPY : 0);
373 error(1, errno, "sendmmsg");
378 static void send_udp_segment_cmsg(struct cmsghdr *cm)
382 cm->cmsg_level = SOL_UDP;
383 cm->cmsg_type = UDP_SEGMENT;
384 cm->cmsg_len = CMSG_LEN(sizeof(cfg_gso_size));
385 valp = (void *)CMSG_DATA(cm);
386 *valp = cfg_gso_size;
389 static int send_udp_segment(int fd, char *data)
391 char control[CMSG_SPACE(sizeof(cfg_gso_size)) +
392 CMSG_SPACE(sizeof(cfg_tx_ts))] = {0};
393 struct msghdr msg = {0};
394 struct iovec iov = {0};
395 size_t msg_controllen;
396 struct cmsghdr *cmsg;
400 iov.iov_len = cfg_payload_len;
405 msg.msg_control = control;
406 msg.msg_controllen = sizeof(control);
407 cmsg = CMSG_FIRSTHDR(&msg);
408 send_udp_segment_cmsg(cmsg);
409 msg_controllen = CMSG_SPACE(sizeof(cfg_mss));
411 cmsg = CMSG_NXTHDR(&msg, cmsg);
413 msg_controllen += CMSG_SPACE(sizeof(cfg_tx_ts));
416 msg.msg_controllen = msg_controllen;
417 msg.msg_name = (void *)&cfg_dst_addr;
418 msg.msg_namelen = cfg_alen;
420 ret = sendmsg(fd, &msg, cfg_zerocopy ? MSG_ZEROCOPY : 0);
422 error(1, errno, "sendmsg");
423 if (ret != iov.iov_len)
424 error(1, 0, "sendmsg: %u != %llu\n", ret,
425 (unsigned long long)iov.iov_len);
430 static void usage(const char *filepath)
432 error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] "
433 "[-L secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
437 static void parse_opts(int argc, char **argv)
439 const char *bind_addr = NULL;
443 while ((c = getopt(argc, argv, "46acC:D:Hl:L:mM:p:s:PS:tTuvz")) != -1) {
446 if (cfg_family != PF_UNSPEC)
447 error(1, 0, "Pass one of -4 or -6");
448 cfg_family = PF_INET;
449 cfg_alen = sizeof(struct sockaddr_in);
452 if (cfg_family != PF_UNSPEC)
453 error(1, 0, "Pass one of -4 or -6");
454 cfg_family = PF_INET6;
455 cfg_alen = sizeof(struct sockaddr_in6);
461 cfg_cache_trash = true;
464 cfg_cpu = strtol(optarg, NULL, 0);
470 cfg_runtime_ms = strtoul(optarg, NULL, 10) * 1000;
473 cfg_poll_loop_timeout_ms = strtoul(optarg, NULL, 10) * 1000;
479 cfg_msg_nr = strtoul(optarg, NULL, 10);
482 cfg_port = strtoul(optarg, NULL, 0);
488 cfg_payload_len = strtoul(optarg, NULL, 0);
491 cfg_gso_size = strtoul(optarg, NULL, 0);
495 cfg_tx_ts = SOF_TIMESTAMPING_TX_HARDWARE;
496 cfg_tx_tstamp = true;
502 cfg_tx_tstamp = true;
505 cfg_connected = false;
519 bind_addr = cfg_family == PF_INET6 ? "::" : "0.0.0.0";
521 setup_sockaddr(cfg_family, bind_addr, &cfg_dst_addr);
526 if (cfg_family == PF_UNSPEC)
527 error(1, 0, "must pass one of -4 or -6");
528 if (cfg_tcp && !cfg_connected)
529 error(1, 0, "connectionless tcp makes no sense");
530 if (cfg_segment && cfg_sendmmsg)
531 error(1, 0, "cannot combine segment offload and sendmmsg");
532 if (cfg_tx_tstamp && !(cfg_segment || cfg_sendmmsg))
533 error(1, 0, "Options -T and -H require either -S or -m option");
535 if (cfg_family == PF_INET)
536 hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
538 hdrlen = sizeof(struct ip6_hdr) + sizeof(struct udphdr);
540 cfg_mss = ETH_DATA_LEN - hdrlen;
541 max_len = ETH_MAX_MTU - hdrlen;
543 cfg_gso_size = cfg_mss;
545 if (cfg_payload_len > max_len)
546 error(1, 0, "payload length %u exceeds max %u",
547 cfg_payload_len, max_len);
550 static void set_pmtu_discover(int fd, bool is_ipv4)
552 int level, name, val;
556 name = IP_MTU_DISCOVER;
557 val = IP_PMTUDISC_DO;
560 name = IPV6_MTU_DISCOVER;
561 val = IPV6_PMTUDISC_DO;
564 if (setsockopt(fd, level, name, &val, sizeof(val)))
565 error(1, errno, "setsockopt path mtu");
568 static void set_tx_timestamping(int fd)
570 int val = SOF_TIMESTAMPING_OPT_CMSG | SOF_TIMESTAMPING_OPT_ID |
571 SOF_TIMESTAMPING_OPT_TSONLY;
573 if (cfg_tx_ts == SOF_TIMESTAMPING_TX_SOFTWARE)
574 val |= SOF_TIMESTAMPING_SOFTWARE;
576 val |= SOF_TIMESTAMPING_RAW_HARDWARE;
578 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val)))
579 error(1, errno, "setsockopt tx timestamping");
582 static void print_audit_report(unsigned long num_msgs, unsigned long num_sends)
584 unsigned long tdelta;
586 tdelta = tend - tstart;
590 fprintf(stderr, "Summary over %lu.%03lu seconds...\n",
591 tdelta / 1000, tdelta % 1000);
593 "sum %s tx: %6lu MB/s %10lu calls (%lu/s) %10lu msgs (%lu/s)\n",
594 cfg_tcp ? "tcp" : "udp",
595 ((num_msgs * cfg_payload_len) >> 10) / tdelta,
596 num_sends, num_sends * 1000 / tdelta,
597 num_msgs, num_msgs * 1000 / tdelta);
600 if (stat_tx_ts_errors)
602 "Expected clean TX Timestamps: %9lu msgs received %6lu errors",
603 stat_tx_ts, stat_tx_ts_errors);
604 if (stat_tx_ts != num_sends)
606 "Unexpected number of TX Timestamps: %9lu expected %9lu received",
607 num_sends, stat_tx_ts);
609 "Tx Timestamps: %19lu received %17lu errors\n",
610 stat_tx_ts, stat_tx_ts_errors);
614 if (stat_zcopies != num_sends)
615 error(1, 0, "Unexpected number of Zerocopy completions: %9lu expected %9lu received",
616 num_sends, stat_zcopies);
618 "Zerocopy acks: %19lu\n",
623 static void print_report(unsigned long num_msgs, unsigned long num_sends)
626 "%s tx: %6lu MB/s %8lu calls/s %6lu msg/s\n",
627 cfg_tcp ? "tcp" : "udp",
628 (num_msgs * cfg_payload_len) >> 20,
629 num_sends, num_msgs);
632 total_num_msgs += num_msgs;
633 total_num_sends += num_sends;
637 int main(int argc, char **argv)
639 unsigned long num_msgs, num_sends;
640 unsigned long tnow, treport, tstop;
643 parse_opts(argc, argv);
648 for (i = 0; i < sizeof(buf[0]); i++)
649 buf[0][i] = 'a' + (i % 26);
650 for (i = 1; i < NUM_PKT; i++)
651 memcpy(buf[i], buf[0], sizeof(buf[0]));
653 signal(SIGINT, sigint_handler);
655 fd = socket(cfg_family, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
657 error(1, errno, "socket");
662 ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY,
665 if (errno == ENOPROTOOPT || errno == ENOTSUPP) {
666 fprintf(stderr, "SO_ZEROCOPY not supported");
669 error(1, errno, "setsockopt zerocopy");
674 connect(fd, (void *)&cfg_dst_addr, cfg_alen))
675 error(1, errno, "connect");
678 set_pmtu_discover(fd, cfg_family == PF_INET);
681 set_tx_timestamping(fd);
683 num_msgs = num_sends = 0;
684 tnow = gettimeofday_ms();
687 tstop = tnow + cfg_runtime_ms;
688 treport = tnow + 1000;
693 num_sends += send_tcp(fd, buf[i]);
694 else if (cfg_segment)
695 num_sends += send_udp_segment(fd, buf[i]);
696 else if (cfg_sendmmsg)
697 num_sends += send_udp_sendmmsg(fd, buf[i]);
699 num_sends += send_udp(fd, buf[i]);
701 if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
702 flush_errqueue(fd, cfg_poll, 500, true);
704 if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
707 tnow = gettimeofday_ms();
708 if (tnow >= treport) {
709 print_report(num_msgs, num_sends);
710 num_msgs = num_sends = 0;
711 treport = tnow + 1000;
714 /* cold cache when writing buffer */
716 i = ++i < NUM_PKT ? i : 0;
718 } while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
720 if (cfg_zerocopy || cfg_tx_tstamp)
721 flush_errqueue_retry(fd, num_sends);
724 error(1, errno, "close");
728 total_num_msgs += num_msgs;
729 total_num_sends += num_sends;
730 print_audit_report(total_num_msgs, total_num_sends);