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
);