2018-04-26 20:42:25 +03:00
// SPDX-License-Identifier: GPL-2.0
# define _GNU_SOURCE
# include <arpa/inet.h>
# include <errno.h>
# include <error.h>
2019-06-17 22:08:35 +03:00
# include <linux/errqueue.h>
# include <linux/net_tstamp.h>
2018-04-26 20:42:25 +03:00
# include <netinet/if_ether.h>
# include <netinet/in.h>
# include <netinet/ip.h>
# include <netinet/ip6.h>
# include <netinet/udp.h>
# include <poll.h>
# include <sched.h>
# include <signal.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/socket.h>
# include <sys/time.h>
2019-06-17 22:08:35 +03:00
# include <sys/poll.h>
2018-04-26 20:42:25 +03:00
# include <sys/types.h>
# include <unistd.h>
2019-06-18 23:03:04 +03:00
# include "../kselftest.h"
2018-04-26 20:42:25 +03:00
# ifndef ETH_MAX_MTU
# define ETH_MAX_MTU 0xFFFFU
# endif
# ifndef UDP_SEGMENT
# define UDP_SEGMENT 103
# endif
# ifndef SO_ZEROCOPY
# define SO_ZEROCOPY 60
# endif
2019-06-17 22:08:35 +03:00
# ifndef SO_EE_ORIGIN_ZEROCOPY
# define SO_EE_ORIGIN_ZEROCOPY 5
# endif
2018-04-26 20:42:25 +03:00
# ifndef MSG_ZEROCOPY
# define MSG_ZEROCOPY 0x4000000
# endif
2019-06-18 23:03:04 +03:00
# ifndef ENOTSUPP
# define ENOTSUPP 524
# endif
2018-04-26 20:42:25 +03:00
# define NUM_PKT 100
static bool cfg_cache_trash ;
static int cfg_cpu = - 1 ;
static int cfg_connected = true ;
static int cfg_family = PF_UNSPEC ;
static uint16_t cfg_mss ;
static int cfg_payload_len = ( 1472 * 42 ) ;
static int cfg_port = 8000 ;
static int cfg_runtime_ms = - 1 ;
2019-06-17 22:08:35 +03:00
static bool cfg_poll ;
2018-04-26 20:42:25 +03:00
static bool cfg_segment ;
static bool cfg_sendmmsg ;
static bool cfg_tcp ;
2019-06-17 22:08:35 +03:00
static uint32_t cfg_tx_ts = SOF_TIMESTAMPING_TX_SOFTWARE ;
static bool cfg_tx_tstamp ;
static bool cfg_audit ;
static bool cfg_verbose ;
2018-04-26 20:42:25 +03:00
static bool cfg_zerocopy ;
2018-11-07 14:38:37 +03:00
static int cfg_msg_nr ;
static uint16_t cfg_gso_size ;
2019-06-17 22:08:35 +03:00
static unsigned long total_num_msgs ;
static unsigned long total_num_sends ;
static unsigned long stat_tx_ts ;
static unsigned long stat_tx_ts_errors ;
static unsigned long tstart ;
static unsigned long tend ;
static unsigned long stat_zcopies ;
2018-04-26 20:42:25 +03:00
static socklen_t cfg_alen ;
static struct sockaddr_storage cfg_dst_addr ;
static bool interrupted ;
static char buf [ NUM_PKT ] [ ETH_MAX_MTU ] ;
static void sigint_handler ( int signum )
{
if ( signum = = SIGINT )
interrupted = true ;
}
static unsigned long gettimeofday_ms ( void )
{
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
return ( tv . tv_sec * 1000 ) + ( tv . tv_usec / 1000 ) ;
}
static int set_cpu ( int cpu )
{
cpu_set_t mask ;
CPU_ZERO ( & mask ) ;
CPU_SET ( cpu , & mask ) ;
if ( sched_setaffinity ( 0 , sizeof ( mask ) , & mask ) )
error ( 1 , 0 , " setaffinity %d " , cpu ) ;
return 0 ;
}
static void setup_sockaddr ( int domain , const char * str_addr , void * sockaddr )
{
struct sockaddr_in6 * addr6 = ( void * ) sockaddr ;
struct sockaddr_in * addr4 = ( void * ) sockaddr ;
switch ( domain ) {
case PF_INET :
addr4 - > sin_family = AF_INET ;
addr4 - > sin_port = htons ( cfg_port ) ;
if ( inet_pton ( AF_INET , str_addr , & ( addr4 - > sin_addr ) ) ! = 1 )
error ( 1 , 0 , " ipv4 parse error: %s " , str_addr ) ;
break ;
case PF_INET6 :
addr6 - > sin6_family = AF_INET6 ;
addr6 - > sin6_port = htons ( cfg_port ) ;
if ( inet_pton ( AF_INET6 , str_addr , & ( addr6 - > sin6_addr ) ) ! = 1 )
error ( 1 , 0 , " ipv6 parse error: %s " , str_addr ) ;
break ;
default :
error ( 1 , 0 , " illegal domain " ) ;
}
}
2019-06-17 22:08:35 +03:00
static void flush_cmsg ( struct cmsghdr * cmsg )
2018-04-26 20:42:25 +03:00
{
2019-06-17 22:08:35 +03:00
struct sock_extended_err * err ;
struct scm_timestamping * tss ;
__u32 lo ;
__u32 hi ;
int i ;
switch ( cmsg - > cmsg_level ) {
case SOL_SOCKET :
if ( cmsg - > cmsg_type = = SO_TIMESTAMPING ) {
i = ( cfg_tx_ts = = SOF_TIMESTAMPING_TX_HARDWARE ) ? 2 : 0 ;
tss = ( struct scm_timestamping * ) CMSG_DATA ( cmsg ) ;
if ( tss - > ts [ i ] . tv_sec = = 0 )
stat_tx_ts_errors + + ;
} else {
error ( 1 , 0 , " unknown SOL_SOCKET cmsg type=%u \n " ,
cmsg - > cmsg_type ) ;
}
break ;
case SOL_IP :
case SOL_IPV6 :
switch ( cmsg - > cmsg_type ) {
case IP_RECVERR :
case IPV6_RECVERR :
{
err = ( struct sock_extended_err * ) CMSG_DATA ( cmsg ) ;
switch ( err - > ee_origin ) {
case SO_EE_ORIGIN_TIMESTAMPING :
/* Got a TX timestamp from error queue */
stat_tx_ts + + ;
break ;
case SO_EE_ORIGIN_ICMP :
case SO_EE_ORIGIN_ICMP6 :
if ( cfg_verbose )
fprintf ( stderr ,
" received ICMP error: type=%u, code=%u \n " ,
err - > ee_type , err - > ee_code ) ;
break ;
case SO_EE_ORIGIN_ZEROCOPY :
{
lo = err - > ee_info ;
hi = err - > ee_data ;
/* range of IDs acknowledged */
stat_zcopies + = hi - lo + 1 ;
break ;
}
case SO_EE_ORIGIN_LOCAL :
if ( cfg_verbose )
fprintf ( stderr ,
" received packet with local origin: %u \n " ,
err - > ee_origin ) ;
break ;
default :
error ( 0 , 1 , " received packet with origin: %u " ,
err - > ee_origin ) ;
}
break ;
}
default :
error ( 0 , 1 , " unknown IP msg type=%u \n " ,
cmsg - > cmsg_type ) ;
break ;
}
break ;
default :
error ( 0 , 1 , " unknown cmsg level=%u \n " ,
cmsg - > cmsg_level ) ;
}
}
static void flush_errqueue_recv ( int fd )
{
char control [ CMSG_SPACE ( sizeof ( struct scm_timestamping ) ) +
CMSG_SPACE ( sizeof ( struct sock_extended_err ) ) +
CMSG_SPACE ( sizeof ( struct sockaddr_in6 ) ) ] = { 0 } ;
struct msghdr msg = { 0 } ;
struct cmsghdr * cmsg ;
2018-04-26 20:42:25 +03:00
int ret ;
while ( 1 ) {
2019-06-17 22:08:35 +03:00
msg . msg_control = control ;
msg . msg_controllen = sizeof ( control ) ;
2018-04-26 20:42:25 +03:00
ret = recvmsg ( fd , & msg , MSG_ERRQUEUE ) ;
if ( ret = = - 1 & & errno = = EAGAIN )
break ;
if ( ret = = - 1 )
error ( 1 , errno , " errqueue " ) ;
2019-06-17 22:08:35 +03:00
if ( msg . msg_flags ! = MSG_ERRQUEUE )
2018-04-26 20:42:25 +03:00
error ( 1 , 0 , " errqueue: flags 0x%x \n " , msg . msg_flags ) ;
2019-06-17 22:08:35 +03:00
if ( cfg_audit ) {
for ( cmsg = CMSG_FIRSTHDR ( & msg ) ;
cmsg ;
cmsg = CMSG_NXTHDR ( & msg , cmsg ) )
flush_cmsg ( cmsg ) ;
}
2018-04-26 20:42:25 +03:00
msg . msg_flags = 0 ;
}
}
2019-06-17 22:08:35 +03:00
static void flush_errqueue ( int fd , const bool do_poll )
{
if ( do_poll ) {
struct pollfd fds = { 0 } ;
int ret ;
fds . fd = fd ;
ret = poll ( & fds , 1 , 500 ) ;
if ( ret = = 0 ) {
if ( cfg_verbose )
fprintf ( stderr , " poll timeout \n " ) ;
} else if ( ret < 0 ) {
error ( 1 , errno , " poll " ) ;
}
}
flush_errqueue_recv ( fd ) ;
}
2018-04-26 20:42:25 +03:00
static int send_tcp ( int fd , char * data )
{
int ret , done = 0 , count = 0 ;
while ( done < cfg_payload_len ) {
ret = send ( fd , data + done , cfg_payload_len - done ,
cfg_zerocopy ? MSG_ZEROCOPY : 0 ) ;
if ( ret = = - 1 )
error ( 1 , errno , " write " ) ;
done + = ret ;
count + + ;
}
return count ;
}
static int send_udp ( int fd , char * data )
{
int ret , total_len , len , count = 0 ;
total_len = cfg_payload_len ;
while ( total_len ) {
len = total_len < cfg_mss ? total_len : cfg_mss ;
ret = sendto ( fd , data , len , cfg_zerocopy ? MSG_ZEROCOPY : 0 ,
cfg_connected ? NULL : ( void * ) & cfg_dst_addr ,
cfg_connected ? 0 : cfg_alen ) ;
if ( ret = = - 1 )
error ( 1 , errno , " write " ) ;
if ( ret ! = len )
error ( 1 , errno , " write: %uB != %uB \n " , ret , len ) ;
total_len - = len ;
count + + ;
}
return count ;
}
2019-06-17 22:08:35 +03:00
static void send_ts_cmsg ( struct cmsghdr * cm )
{
uint32_t * valp ;
cm - > cmsg_level = SOL_SOCKET ;
cm - > cmsg_type = SO_TIMESTAMPING ;
cm - > cmsg_len = CMSG_LEN ( sizeof ( cfg_tx_ts ) ) ;
valp = ( void * ) CMSG_DATA ( cm ) ;
* valp = cfg_tx_ts ;
}
2018-04-26 20:42:25 +03:00
static int send_udp_sendmmsg ( int fd , char * data )
{
2019-06-17 22:08:35 +03:00
char control [ CMSG_SPACE ( sizeof ( cfg_tx_ts ) ) ] = { 0 } ;
2018-04-26 20:42:25 +03:00
const int max_nr_msg = ETH_MAX_MTU / ETH_DATA_LEN ;
struct mmsghdr mmsgs [ max_nr_msg ] ;
struct iovec iov [ max_nr_msg ] ;
unsigned int off = 0 , left ;
2019-06-17 22:08:35 +03:00
size_t msg_controllen = 0 ;
2018-04-26 20:42:25 +03:00
int i = 0 , ret ;
memset ( mmsgs , 0 , sizeof ( mmsgs ) ) ;
2019-06-17 22:08:35 +03:00
if ( cfg_tx_tstamp ) {
struct msghdr msg = { 0 } ;
struct cmsghdr * cmsg ;
msg . msg_control = control ;
msg . msg_controllen = sizeof ( control ) ;
cmsg = CMSG_FIRSTHDR ( & msg ) ;
send_ts_cmsg ( cmsg ) ;
msg_controllen + = CMSG_SPACE ( sizeof ( cfg_tx_ts ) ) ;
}
2018-04-26 20:42:25 +03:00
left = cfg_payload_len ;
while ( left ) {
if ( i = = max_nr_msg )
error ( 1 , 0 , " sendmmsg: exceeds max_nr_msg " ) ;
iov [ i ] . iov_base = data + off ;
iov [ i ] . iov_len = cfg_mss < left ? cfg_mss : left ;
mmsgs [ i ] . msg_hdr . msg_iov = iov + i ;
mmsgs [ i ] . msg_hdr . msg_iovlen = 1 ;
2019-06-17 22:08:35 +03:00
mmsgs [ i ] . msg_hdr . msg_name = ( void * ) & cfg_dst_addr ;
mmsgs [ i ] . msg_hdr . msg_namelen = cfg_alen ;
if ( msg_controllen ) {
mmsgs [ i ] . msg_hdr . msg_control = control ;
mmsgs [ i ] . msg_hdr . msg_controllen = msg_controllen ;
}
2018-04-26 20:42:25 +03:00
off + = iov [ i ] . iov_len ;
left - = iov [ i ] . iov_len ;
i + + ;
}
ret = sendmmsg ( fd , mmsgs , i , cfg_zerocopy ? MSG_ZEROCOPY : 0 ) ;
if ( ret = = - 1 )
error ( 1 , errno , " sendmmsg " ) ;
return ret ;
}
static void send_udp_segment_cmsg ( struct cmsghdr * cm )
{
uint16_t * valp ;
cm - > cmsg_level = SOL_UDP ;
cm - > cmsg_type = UDP_SEGMENT ;
2018-11-07 14:38:37 +03:00
cm - > cmsg_len = CMSG_LEN ( sizeof ( cfg_gso_size ) ) ;
2018-04-26 20:42:25 +03:00
valp = ( void * ) CMSG_DATA ( cm ) ;
2018-11-07 14:38:37 +03:00
* valp = cfg_gso_size ;
2018-04-26 20:42:25 +03:00
}
static int send_udp_segment ( int fd , char * data )
{
2019-06-17 22:08:35 +03:00
char control [ CMSG_SPACE ( sizeof ( cfg_gso_size ) ) +
CMSG_SPACE ( sizeof ( cfg_tx_ts ) ) ] = { 0 } ;
2018-04-26 20:42:25 +03:00
struct msghdr msg = { 0 } ;
struct iovec iov = { 0 } ;
2019-06-17 22:08:35 +03:00
size_t msg_controllen ;
struct cmsghdr * cmsg ;
2018-04-26 20:42:25 +03:00
int ret ;
iov . iov_base = data ;
iov . iov_len = cfg_payload_len ;
msg . msg_iov = & iov ;
msg . msg_iovlen = 1 ;
msg . msg_control = control ;
msg . msg_controllen = sizeof ( control ) ;
2019-06-17 22:08:35 +03:00
cmsg = CMSG_FIRSTHDR ( & msg ) ;
send_udp_segment_cmsg ( cmsg ) ;
msg_controllen = CMSG_SPACE ( sizeof ( cfg_mss ) ) ;
if ( cfg_tx_tstamp ) {
cmsg = CMSG_NXTHDR ( & msg , cmsg ) ;
send_ts_cmsg ( cmsg ) ;
msg_controllen + = CMSG_SPACE ( sizeof ( cfg_tx_ts ) ) ;
}
2018-04-26 20:42:25 +03:00
2019-06-17 22:08:35 +03:00
msg . msg_controllen = msg_controllen ;
2018-04-26 20:42:25 +03:00
msg . msg_name = ( void * ) & cfg_dst_addr ;
msg . msg_namelen = cfg_alen ;
ret = sendmsg ( fd , & msg , cfg_zerocopy ? MSG_ZEROCOPY : 0 ) ;
if ( ret = = - 1 )
error ( 1 , errno , " sendmsg " ) ;
if ( ret ! = iov . iov_len )
2019-10-23 07:58:07 +03:00
error ( 1 , 0 , " sendmsg: %u != %llu \n " , ret ,
( unsigned long long ) iov . iov_len ) ;
2018-04-26 20:42:25 +03:00
return 1 ;
}
static void usage ( const char * filepath )
{
2019-06-17 22:08:35 +03:00
error ( 1 , 0 , " Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize] " ,
2018-04-26 20:42:25 +03:00
filepath ) ;
}
static void parse_opts ( int argc , char * * argv )
{
2021-12-29 13:58:10 +03:00
const char * bind_addr = NULL ;
2018-04-26 20:42:25 +03:00
int max_len , hdrlen ;
int c ;
2019-06-17 22:08:35 +03:00
while ( ( c = getopt ( argc , argv , " 46acC:D:Hl:mM:p:s:PS:tTuvz " ) ) ! = - 1 ) {
2018-04-26 20:42:25 +03:00
switch ( c ) {
case ' 4 ' :
if ( cfg_family ! = PF_UNSPEC )
error ( 1 , 0 , " Pass one of -4 or -6 " ) ;
cfg_family = PF_INET ;
cfg_alen = sizeof ( struct sockaddr_in ) ;
break ;
case ' 6 ' :
if ( cfg_family ! = PF_UNSPEC )
error ( 1 , 0 , " Pass one of -4 or -6 " ) ;
cfg_family = PF_INET6 ;
cfg_alen = sizeof ( struct sockaddr_in6 ) ;
break ;
2019-06-17 22:08:35 +03:00
case ' a ' :
cfg_audit = true ;
break ;
2018-04-26 20:42:25 +03:00
case ' c ' :
cfg_cache_trash = true ;
break ;
case ' C ' :
cfg_cpu = strtol ( optarg , NULL , 0 ) ;
break ;
case ' D ' :
2021-12-29 13:58:10 +03:00
bind_addr = optarg ;
2018-04-26 20:42:25 +03:00
break ;
case ' l ' :
cfg_runtime_ms = strtoul ( optarg , NULL , 10 ) * 1000 ;
break ;
case ' m ' :
cfg_sendmmsg = true ;
break ;
2018-11-07 14:38:37 +03:00
case ' M ' :
cfg_msg_nr = strtoul ( optarg , NULL , 10 ) ;
break ;
2018-04-26 20:42:25 +03:00
case ' p ' :
cfg_port = strtoul ( optarg , NULL , 0 ) ;
break ;
2019-06-17 22:08:35 +03:00
case ' P ' :
cfg_poll = true ;
break ;
2018-04-26 20:42:25 +03:00
case ' s ' :
cfg_payload_len = strtoul ( optarg , NULL , 0 ) ;
break ;
case ' S ' :
2018-11-07 14:38:37 +03:00
cfg_gso_size = strtoul ( optarg , NULL , 0 ) ;
2018-04-26 20:42:25 +03:00
cfg_segment = true ;
break ;
2019-06-17 22:08:35 +03:00
case ' H ' :
cfg_tx_ts = SOF_TIMESTAMPING_TX_HARDWARE ;
cfg_tx_tstamp = true ;
break ;
2018-04-26 20:42:25 +03:00
case ' t ' :
cfg_tcp = true ;
break ;
2019-06-17 22:08:35 +03:00
case ' T ' :
cfg_tx_tstamp = true ;
break ;
2018-04-26 20:42:25 +03:00
case ' u ' :
cfg_connected = false ;
break ;
2019-06-17 22:08:35 +03:00
case ' v ' :
cfg_verbose = true ;
break ;
2018-04-26 20:42:25 +03:00
case ' z ' :
cfg_zerocopy = true ;
break ;
}
}
2021-12-29 13:58:10 +03:00
if ( ! bind_addr )
bind_addr = cfg_family = = PF_INET6 ? " :: " : " 0.0.0.0 " ;
setup_sockaddr ( cfg_family , bind_addr , & cfg_dst_addr ) ;
2018-04-26 20:42:25 +03:00
if ( optind ! = argc )
usage ( argv [ 0 ] ) ;
if ( cfg_family = = PF_UNSPEC )
error ( 1 , 0 , " must pass one of -4 or -6 " ) ;
if ( cfg_tcp & & ! cfg_connected )
error ( 1 , 0 , " connectionless tcp makes no sense " ) ;
if ( cfg_segment & & cfg_sendmmsg )
error ( 1 , 0 , " cannot combine segment offload and sendmmsg " ) ;
2019-06-17 22:08:35 +03:00
if ( cfg_tx_tstamp & & ! ( cfg_segment | | cfg_sendmmsg ) )
error ( 1 , 0 , " Options -T and -H require either -S or -m option " ) ;
2018-04-26 20:42:25 +03:00
if ( cfg_family = = PF_INET )
hdrlen = sizeof ( struct iphdr ) + sizeof ( struct udphdr ) ;
else
hdrlen = sizeof ( struct ip6_hdr ) + sizeof ( struct udphdr ) ;
cfg_mss = ETH_DATA_LEN - hdrlen ;
max_len = ETH_MAX_MTU - hdrlen ;
2018-11-07 14:38:37 +03:00
if ( ! cfg_gso_size )
cfg_gso_size = cfg_mss ;
2018-04-26 20:42:25 +03:00
if ( cfg_payload_len > max_len )
error ( 1 , 0 , " payload length %u exceeds max %u " ,
cfg_payload_len , max_len ) ;
}
static void set_pmtu_discover ( int fd , bool is_ipv4 )
{
int level , name , val ;
if ( is_ipv4 ) {
level = SOL_IP ;
name = IP_MTU_DISCOVER ;
val = IP_PMTUDISC_DO ;
} else {
level = SOL_IPV6 ;
name = IPV6_MTU_DISCOVER ;
val = IPV6_PMTUDISC_DO ;
}
if ( setsockopt ( fd , level , name , & val , sizeof ( val ) ) )
error ( 1 , errno , " setsockopt path mtu " ) ;
}
2019-06-17 22:08:35 +03:00
static void set_tx_timestamping ( int fd )
{
int val = SOF_TIMESTAMPING_OPT_CMSG | SOF_TIMESTAMPING_OPT_ID |
SOF_TIMESTAMPING_OPT_TSONLY ;
if ( cfg_tx_ts = = SOF_TIMESTAMPING_TX_SOFTWARE )
val | = SOF_TIMESTAMPING_SOFTWARE ;
else
val | = SOF_TIMESTAMPING_RAW_HARDWARE ;
if ( setsockopt ( fd , SOL_SOCKET , SO_TIMESTAMPING , & val , sizeof ( val ) ) )
error ( 1 , errno , " setsockopt tx timestamping " ) ;
}
static void print_audit_report ( unsigned long num_msgs , unsigned long num_sends )
{
unsigned long tdelta ;
tdelta = tend - tstart ;
if ( ! tdelta )
return ;
fprintf ( stderr , " Summary over %lu.%03lu seconds... \n " ,
tdelta / 1000 , tdelta % 1000 ) ;
fprintf ( stderr ,
" sum %s tx: %6lu MB/s %10lu calls (%lu/s) %10lu msgs (%lu/s) \n " ,
cfg_tcp ? " tcp " : " udp " ,
( ( num_msgs * cfg_payload_len ) > > 10 ) / tdelta ,
num_sends , num_sends * 1000 / tdelta ,
num_msgs , num_msgs * 1000 / tdelta ) ;
if ( cfg_tx_tstamp ) {
if ( stat_tx_ts_errors )
error ( 1 , 0 ,
" Expected clean TX Timestamps: %9lu msgs received %6lu errors " ,
stat_tx_ts , stat_tx_ts_errors ) ;
if ( stat_tx_ts ! = num_sends )
error ( 1 , 0 ,
" Unexpected number of TX Timestamps: %9lu expected %9lu received " ,
num_sends , stat_tx_ts ) ;
fprintf ( stderr ,
" Tx Timestamps: %19lu received %17lu errors \n " ,
stat_tx_ts , stat_tx_ts_errors ) ;
}
if ( cfg_zerocopy ) {
if ( stat_zcopies ! = num_sends )
error ( 1 , 0 , " Unexpected number of Zerocopy completions: %9lu expected %9lu received " ,
num_sends , stat_zcopies ) ;
fprintf ( stderr ,
" Zerocopy acks: %19lu \n " ,
stat_zcopies ) ;
}
}
static void print_report ( unsigned long num_msgs , unsigned long num_sends )
{
fprintf ( stderr ,
" %s tx: %6lu MB/s %8lu calls/s %6lu msg/s \n " ,
cfg_tcp ? " tcp " : " udp " ,
( num_msgs * cfg_payload_len ) > > 20 ,
num_sends , num_msgs ) ;
if ( cfg_audit ) {
total_num_msgs + = num_msgs ;
total_num_sends + = num_sends ;
}
}
2018-04-26 20:42:25 +03:00
int main ( int argc , char * * argv )
{
unsigned long num_msgs , num_sends ;
unsigned long tnow , treport , tstop ;
2019-06-18 23:03:04 +03:00
int fd , i , val , ret ;
2018-04-26 20:42:25 +03:00
parse_opts ( argc , argv ) ;
if ( cfg_cpu > 0 )
set_cpu ( cfg_cpu ) ;
for ( i = 0 ; i < sizeof ( buf [ 0 ] ) ; i + + )
buf [ 0 ] [ i ] = ' a ' + ( i % 26 ) ;
for ( i = 1 ; i < NUM_PKT ; i + + )
memcpy ( buf [ i ] , buf [ 0 ] , sizeof ( buf [ 0 ] ) ) ;
signal ( SIGINT , sigint_handler ) ;
fd = socket ( cfg_family , cfg_tcp ? SOCK_STREAM : SOCK_DGRAM , 0 ) ;
if ( fd = = - 1 )
error ( 1 , errno , " socket " ) ;
if ( cfg_zerocopy ) {
val = 1 ;
2019-06-18 23:03:04 +03:00
ret = setsockopt ( fd , SOL_SOCKET , SO_ZEROCOPY ,
& val , sizeof ( val ) ) ;
if ( ret ) {
if ( errno = = ENOPROTOOPT | | errno = = ENOTSUPP ) {
fprintf ( stderr , " SO_ZEROCOPY not supported " ) ;
exit ( KSFT_SKIP ) ;
}
2018-04-26 20:42:25 +03:00
error ( 1 , errno , " setsockopt zerocopy " ) ;
2019-06-18 23:03:04 +03:00
}
2018-04-26 20:42:25 +03:00
}
if ( cfg_connected & &
connect ( fd , ( void * ) & cfg_dst_addr , cfg_alen ) )
error ( 1 , errno , " connect " ) ;
if ( cfg_segment )
set_pmtu_discover ( fd , cfg_family = = PF_INET ) ;
2019-06-17 22:08:35 +03:00
if ( cfg_tx_tstamp )
set_tx_timestamping ( fd ) ;
2018-04-26 20:42:25 +03:00
num_msgs = num_sends = 0 ;
tnow = gettimeofday_ms ( ) ;
2019-06-17 22:08:35 +03:00
tstart = tnow ;
tend = tnow ;
2018-04-26 20:42:25 +03:00
tstop = tnow + cfg_runtime_ms ;
treport = tnow + 1000 ;
i = 0 ;
do {
if ( cfg_tcp )
num_sends + = send_tcp ( fd , buf [ i ] ) ;
else if ( cfg_segment )
num_sends + = send_udp_segment ( fd , buf [ i ] ) ;
else if ( cfg_sendmmsg )
num_sends + = send_udp_sendmmsg ( fd , buf [ i ] ) ;
else
num_sends + = send_udp ( fd , buf [ i ] ) ;
num_msgs + + ;
2019-06-17 22:08:35 +03:00
if ( ( cfg_zerocopy & & ( ( num_msgs & 0xF ) = = 0 ) ) | | cfg_tx_tstamp )
flush_errqueue ( fd , cfg_poll ) ;
2018-04-26 20:42:25 +03:00
2018-11-07 14:38:37 +03:00
if ( cfg_msg_nr & & num_msgs > = cfg_msg_nr )
break ;
2018-04-26 20:42:25 +03:00
tnow = gettimeofday_ms ( ) ;
2019-06-17 22:08:35 +03:00
if ( tnow > = treport ) {
print_report ( num_msgs , num_sends ) ;
2018-04-26 20:42:25 +03:00
num_msgs = num_sends = 0 ;
treport = tnow + 1000 ;
}
/* cold cache when writing buffer */
if ( cfg_cache_trash )
i = + + i < NUM_PKT ? i : 0 ;
} while ( ! interrupted & & ( cfg_runtime_ms = = - 1 | | tnow < tstop ) ) ;
2019-06-17 22:08:35 +03:00
if ( cfg_zerocopy | | cfg_tx_tstamp )
flush_errqueue ( fd , true ) ;
2018-04-26 20:42:25 +03:00
if ( close ( fd ) )
error ( 1 , errno , " close " ) ;
2019-06-17 22:08:35 +03:00
if ( cfg_audit ) {
tend = tnow ;
total_num_msgs + = num_msgs ;
total_num_sends + = num_sends ;
print_audit_report ( total_num_msgs , total_num_sends ) ;
}
2018-04-26 20:42:25 +03:00
return 0 ;
}