2018-05-02 14:01:36 +03:00
// SPDX-License-Identifier: GPL-2.0
2018-05-18 15:00:21 +03:00
/* Copyright(c) 2017 - 2018 Intel Corporation. */
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
# include <asm/barrier.h>
2018-05-02 14:01:36 +03:00
# include <errno.h>
# include <getopt.h>
# include <libgen.h>
# include <linux/bpf.h>
2019-02-21 12:21:27 +03:00
# include <linux/compiler.h>
2018-05-02 14:01:36 +03:00
# include <linux/if_link.h>
# include <linux/if_xdp.h>
# include <linux/if_ether.h>
2019-02-21 12:21:27 +03:00
# include <locale.h>
# include <net/ethernet.h>
2018-05-02 14:01:36 +03:00
# include <net/if.h>
2019-02-21 12:21:27 +03:00
# include <poll.h>
# include <pthread.h>
2018-05-02 14:01:36 +03:00
# include <signal.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2019-02-21 12:21:27 +03:00
# include <sys/mman.h>
2018-05-02 14:01:36 +03:00
# include <sys/resource.h>
# include <sys/socket.h>
2019-02-21 12:21:27 +03:00
# include <sys/types.h>
2018-05-02 14:01:36 +03:00
# include <time.h>
# include <unistd.h>
2018-07-27 00:32:21 +03:00
# include "bpf/libbpf.h"
2019-02-21 12:21:27 +03:00
# include "bpf/xsk.h"
2018-05-15 08:35:02 +03:00
# include <bpf/bpf.h>
2018-05-02 14:01:36 +03:00
# ifndef SOL_XDP
# define SOL_XDP 283
# endif
# ifndef AF_XDP
# define AF_XDP 44
# endif
# ifndef PF_XDP
# define PF_XDP AF_XDP
# endif
2019-02-21 12:21:27 +03:00
# define NUM_FRAMES (4 * 1024)
# define BATCH_SIZE 64
2018-05-02 14:01:36 +03:00
# define DEBUG_HEXDUMP 0
2019-02-21 12:21:27 +03:00
# define MAX_SOCKS 8
2018-05-02 14:01:36 +03:00
2018-06-04 14:57:14 +03:00
typedef __u64 u64 ;
2018-05-02 14:01:36 +03:00
typedef __u32 u32 ;
static unsigned long prev_time ;
enum benchmark_type {
BENCH_RXDROP = 0 ,
BENCH_TXONLY = 1 ,
BENCH_L2FWD = 2 ,
} ;
static enum benchmark_type opt_bench = BENCH_RXDROP ;
2019-02-02 00:42:28 +03:00
static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST ;
2018-05-02 14:01:36 +03:00
static const char * opt_if = " " ;
static int opt_ifindex ;
static int opt_queue ;
static int opt_poll ;
static int opt_interval = 1 ;
2018-06-04 15:06:01 +03:00
static u32 opt_xdp_bind_flags ;
2019-02-02 00:42:30 +03:00
static __u32 prog_id ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
struct xsk_umem_info {
struct xsk_ring_prod fq ;
struct xsk_ring_cons cq ;
struct xsk_umem * umem ;
void * buffer ;
2018-05-02 14:01:36 +03:00
} ;
2019-02-21 12:21:27 +03:00
struct xsk_socket_info {
struct xsk_ring_cons rx ;
struct xsk_ring_prod tx ;
struct xsk_umem_info * umem ;
struct xsk_socket * xsk ;
2018-05-02 14:01:36 +03:00
unsigned long rx_npkts ;
unsigned long tx_npkts ;
unsigned long prev_rx_npkts ;
unsigned long prev_tx_npkts ;
2019-02-21 12:21:27 +03:00
u32 outstanding_tx ;
2018-05-02 14:01:36 +03:00
} ;
static int num_socks ;
2019-02-21 12:21:27 +03:00
struct xsk_socket_info * xsks [ MAX_SOCKS ] ;
2018-05-02 14:01:36 +03:00
static unsigned long get_nsecs ( void )
{
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
return ts . tv_sec * 1000000000UL + ts . tv_nsec ;
}
2019-02-21 12:21:27 +03:00
static void print_benchmark ( bool running )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
const char * bench_str = " INVALID " ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
if ( opt_bench = = BENCH_RXDROP )
bench_str = " rxdrop " ;
else if ( opt_bench = = BENCH_TXONLY )
bench_str = " txonly " ;
else if ( opt_bench = = BENCH_L2FWD )
bench_str = " l2fwd " ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
printf ( " %s:%d %s " , opt_if , opt_queue , bench_str ) ;
if ( opt_xdp_flags & XDP_FLAGS_SKB_MODE )
printf ( " xdp-skb " ) ;
else if ( opt_xdp_flags & XDP_FLAGS_DRV_MODE )
printf ( " xdp-drv " ) ;
else
printf ( " " ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
if ( opt_poll )
printf ( " poll() " ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
if ( running ) {
printf ( " running... " ) ;
fflush ( stdout ) ;
2018-05-02 14:01:36 +03:00
}
}
2019-02-21 12:21:27 +03:00
static void dump_stats ( void )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
unsigned long now = get_nsecs ( ) ;
long dt = now - prev_time ;
int i ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
prev_time = now ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
for ( i = 0 ; i < num_socks & & xsks [ i ] ; i + + ) {
char * fmt = " %-15s %'-11.0f %'-11lu \n " ;
double rx_pps , tx_pps ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
rx_pps = ( xsks [ i ] - > rx_npkts - xsks [ i ] - > prev_rx_npkts ) *
1000000000. / dt ;
tx_pps = ( xsks [ i ] - > tx_npkts - xsks [ i ] - > prev_tx_npkts ) *
1000000000. / dt ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
printf ( " \n sock%d@ " , i ) ;
print_benchmark ( false ) ;
printf ( " \n " ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
printf ( " %-15s %-11s %-11s %-11.2f \n " , " " , " pps " , " pkts " ,
dt / 1000000000. ) ;
printf ( fmt , " rx " , rx_pps , xsks [ i ] - > rx_npkts ) ;
printf ( fmt , " tx " , tx_pps , xsks [ i ] - > tx_npkts ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
xsks [ i ] - > prev_rx_npkts = xsks [ i ] - > rx_npkts ;
xsks [ i ] - > prev_tx_npkts = xsks [ i ] - > tx_npkts ;
2018-05-02 14:01:36 +03:00
}
}
2019-02-21 12:21:27 +03:00
static void * poller ( void * arg )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
( void ) arg ;
for ( ; ; ) {
sleep ( opt_interval ) ;
dump_stats ( ) ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
return NULL ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
static void remove_xdp_program ( void )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
__u32 curr_prog_id = 0 ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
if ( bpf_get_link_xdp_id ( opt_ifindex , & curr_prog_id , opt_xdp_flags ) ) {
printf ( " bpf_get_link_xdp_id failed \n " ) ;
exit ( EXIT_FAILURE ) ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
if ( prog_id = = curr_prog_id )
bpf_set_link_xdp_fd ( opt_ifindex , - 1 , opt_xdp_flags ) ;
else if ( ! curr_prog_id )
printf ( " couldn't find a prog id on a given interface \n " ) ;
else
printf ( " program on interface changed, not removing \n " ) ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
static void int_exit ( int sig )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
struct xsk_umem * umem = xsks [ 0 ] - > umem - > umem ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
( void ) sig ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
dump_stats ( ) ;
xsk_socket__delete ( xsks [ 0 ] - > xsk ) ;
( void ) xsk_umem__delete ( umem ) ;
remove_xdp_program ( ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
exit ( EXIT_SUCCESS ) ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
static void __exit_with_error ( int error , const char * file , const char * func ,
int line )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
fprintf ( stderr , " %s:%s:%i: errno: %d/ \" %s \" \n " , file , func ,
line , error , strerror ( error ) ) ;
dump_stats ( ) ;
remove_xdp_program ( ) ;
exit ( EXIT_FAILURE ) ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
# define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \
__LINE__ )
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
static const char pkt_data [ ] =
" \x3c \xfd \xfe \x9e \x7f \x71 \xec \xb1 \xd7 \x98 \x3a \xc0 \x08 \x00 \x45 \x00 "
" \x00 \x2e \x00 \x00 \x00 \x00 \x40 \x11 \x88 \x97 \x05 \x08 \x07 \x08 \xc8 \x14 "
" \x1e \x04 \x10 \x92 \x10 \x92 \x00 \x1a \x6d \xa3 \x34 \x33 \x1f \x69 \x40 \x6b "
" \x54 \x59 \xb6 \x14 \x2d \x11 \x44 \xbf \xaf \xd9 \xbe \xaa " ;
2018-05-02 14:01:36 +03:00
static void swap_mac_addresses ( void * data )
{
struct ether_header * eth = ( struct ether_header * ) data ;
struct ether_addr * src_addr = ( struct ether_addr * ) & eth - > ether_shost ;
struct ether_addr * dst_addr = ( struct ether_addr * ) & eth - > ether_dhost ;
struct ether_addr tmp ;
tmp = * src_addr ;
* src_addr = * dst_addr ;
* dst_addr = tmp ;
}
2018-06-04 14:57:14 +03:00
static void hex_dump ( void * pkt , size_t length , u64 addr )
2018-05-02 14:01:36 +03:00
{
const unsigned char * address = ( unsigned char * ) pkt ;
const unsigned char * line = address ;
size_t line_size = 32 ;
unsigned char c ;
2018-06-04 14:57:14 +03:00
char buf [ 32 ] ;
int i = 0 ;
2018-05-02 14:01:36 +03:00
2018-06-04 14:57:14 +03:00
if ( ! DEBUG_HEXDUMP )
return ;
sprintf ( buf , " addr=%llu " , addr ) ;
2018-05-02 14:01:36 +03:00
printf ( " length = %zu \n " , length ) ;
2018-06-04 14:57:14 +03:00
printf ( " %s | " , buf ) ;
2018-05-02 14:01:36 +03:00
while ( length - - > 0 ) {
printf ( " %02X " , * address + + ) ;
if ( ! ( + + i % line_size ) | | ( length = = 0 & & i % line_size ) ) {
if ( length = = 0 ) {
while ( i + + % line_size )
printf ( " __ " ) ;
}
printf ( " | " ) ; /* right close */
while ( line < address ) {
c = * line + + ;
printf ( " %c " , ( c < 33 | | c = = 255 ) ? 0x2E : c ) ;
}
printf ( " \n " ) ;
if ( length > 0 )
2018-06-04 14:57:14 +03:00
printf ( " %s | " , buf ) ;
2018-05-02 14:01:36 +03:00
}
}
printf ( " \n " ) ;
}
2019-02-21 12:21:27 +03:00
static size_t gen_eth_frame ( struct xsk_umem_info * umem , u64 addr )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
memcpy ( xsk_umem__get_data ( umem - > buffer , addr ) , pkt_data ,
sizeof ( pkt_data ) - 1 ) ;
2018-05-02 14:01:36 +03:00
return sizeof ( pkt_data ) - 1 ;
}
2019-02-21 12:21:27 +03:00
static struct xsk_umem_info * xsk_configure_umem ( void * buffer , u64 size )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
struct xsk_umem_info * umem ;
int ret ;
2018-05-02 14:01:36 +03:00
umem = calloc ( 1 , sizeof ( * umem ) ) ;
2019-02-21 12:21:27 +03:00
if ( ! umem )
exit_with_error ( errno ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
ret = xsk_umem__create ( & umem - > umem , buffer , size , & umem - > fq , & umem - > cq ,
NULL ) ;
if ( ret )
exit_with_error ( - ret ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
umem - > buffer = buffer ;
2018-05-02 14:01:36 +03:00
return umem ;
}
2019-02-21 12:21:27 +03:00
static struct xsk_socket_info * xsk_configure_socket ( struct xsk_umem_info * umem )
2018-05-02 14:01:36 +03:00
{
2019-02-21 12:21:27 +03:00
struct xsk_socket_config cfg ;
struct xsk_socket_info * xsk ;
int ret ;
u32 idx ;
int i ;
2018-05-02 14:01:36 +03:00
xsk = calloc ( 1 , sizeof ( * xsk ) ) ;
2019-02-21 12:21:27 +03:00
if ( ! xsk )
exit_with_error ( errno ) ;
xsk - > umem = umem ;
cfg . rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS ;
cfg . tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS ;
cfg . libbpf_flags = 0 ;
cfg . xdp_flags = opt_xdp_flags ;
cfg . bind_flags = opt_xdp_bind_flags ;
ret = xsk_socket__create ( & xsk - > xsk , opt_if , opt_queue , umem - > umem ,
& xsk - > rx , & xsk - > tx , & cfg ) ;
if ( ret )
exit_with_error ( - ret ) ;
ret = bpf_get_link_xdp_id ( opt_ifindex , & prog_id , opt_xdp_flags ) ;
if ( ret )
exit_with_error ( - ret ) ;
ret = xsk_ring_prod__reserve ( & xsk - > umem - > fq ,
XSK_RING_PROD__DEFAULT_NUM_DESCS ,
& idx ) ;
if ( ret ! = XSK_RING_PROD__DEFAULT_NUM_DESCS )
exit_with_error ( - ret ) ;
for ( i = 0 ;
i < XSK_RING_PROD__DEFAULT_NUM_DESCS *
XSK_UMEM__DEFAULT_FRAME_SIZE ;
i + = XSK_UMEM__DEFAULT_FRAME_SIZE )
* xsk_ring_prod__fill_addr ( & xsk - > umem - > fq , idx + + ) = i ;
xsk_ring_prod__submit ( & xsk - > umem - > fq ,
XSK_RING_PROD__DEFAULT_NUM_DESCS ) ;
2018-05-02 14:01:36 +03:00
return xsk ;
}
static struct option long_options [ ] = {
{ " rxdrop " , no_argument , 0 , ' r ' } ,
{ " txonly " , no_argument , 0 , ' t ' } ,
{ " l2fwd " , no_argument , 0 , ' l ' } ,
{ " interface " , required_argument , 0 , ' i ' } ,
{ " queue " , required_argument , 0 , ' q ' } ,
{ " poll " , no_argument , 0 , ' p ' } ,
{ " xdp-skb " , no_argument , 0 , ' S ' } ,
{ " xdp-native " , no_argument , 0 , ' N ' } ,
{ " interval " , required_argument , 0 , ' n ' } ,
2018-08-28 15:44:35 +03:00
{ " zero-copy " , no_argument , 0 , ' z ' } ,
{ " copy " , no_argument , 0 , ' c ' } ,
2018-05-02 14:01:36 +03:00
{ 0 , 0 , 0 , 0 }
} ;
static void usage ( const char * prog )
{
const char * str =
" Usage: %s [OPTIONS] \n "
" Options: \n "
" -r, --rxdrop Discard all incoming packets (default) \n "
" -t, --txonly Only send packets \n "
" -l, --l2fwd MAC swap L2 forwarding \n "
" -i, --interface=n Run on interface n \n "
" -q, --queue=n Use queue n (default 0) \n "
" -p, --poll Use poll syscall \n "
" -S, --xdp-skb=n Use XDP skb-mod \n "
" -N, --xdp-native=n Enfore XDP native mode \n "
" -n, --interval=n Specify statistics update interval (default 1 sec). \n "
2018-08-28 15:44:35 +03:00
" -z, --zero-copy Force zero-copy mode. \n "
" -c, --copy Force copy mode. \n "
2018-05-02 14:01:36 +03:00
" \n " ;
fprintf ( stderr , str , prog ) ;
exit ( EXIT_FAILURE ) ;
}
static void parse_command_line ( int argc , char * * argv )
{
int option_index , c ;
opterr = 0 ;
for ( ; ; ) {
2019-02-02 00:42:28 +03:00
c = getopt_long ( argc , argv , " Frtli:q:psSNn:cz " , long_options ,
2018-05-02 14:01:36 +03:00
& option_index ) ;
if ( c = = - 1 )
break ;
switch ( c ) {
case ' r ' :
opt_bench = BENCH_RXDROP ;
break ;
case ' t ' :
opt_bench = BENCH_TXONLY ;
break ;
case ' l ' :
opt_bench = BENCH_L2FWD ;
break ;
case ' i ' :
opt_if = optarg ;
break ;
case ' q ' :
opt_queue = atoi ( optarg ) ;
break ;
case ' p ' :
opt_poll = 1 ;
break ;
case ' S ' :
opt_xdp_flags | = XDP_FLAGS_SKB_MODE ;
2018-06-04 15:06:01 +03:00
opt_xdp_bind_flags | = XDP_COPY ;
2018-05-02 14:01:36 +03:00
break ;
case ' N ' :
opt_xdp_flags | = XDP_FLAGS_DRV_MODE ;
break ;
case ' n ' :
opt_interval = atoi ( optarg ) ;
break ;
2018-08-28 15:44:35 +03:00
case ' z ' :
opt_xdp_bind_flags | = XDP_ZEROCOPY ;
break ;
case ' c ' :
opt_xdp_bind_flags | = XDP_COPY ;
break ;
2019-02-02 00:42:28 +03:00
case ' F ' :
opt_xdp_flags & = ~ XDP_FLAGS_UPDATE_IF_NOEXIST ;
break ;
2018-05-02 14:01:36 +03:00
default :
usage ( basename ( argv [ 0 ] ) ) ;
}
}
opt_ifindex = if_nametoindex ( opt_if ) ;
if ( ! opt_ifindex ) {
fprintf ( stderr , " ERROR: interface \" %s \" does not exist \n " ,
opt_if ) ;
usage ( basename ( argv [ 0 ] ) ) ;
}
2019-02-21 12:21:27 +03:00
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
static void kick_tx ( struct xsk_socket_info * xsk )
2018-05-02 14:01:36 +03:00
{
int ret ;
2019-02-21 12:21:27 +03:00
ret = sendto ( xsk_socket__fd ( xsk - > xsk ) , NULL , 0 , MSG_DONTWAIT , NULL , 0 ) ;
2018-06-29 10:48:19 +03:00
if ( ret > = 0 | | errno = = ENOBUFS | | errno = = EAGAIN | | errno = = EBUSY )
2018-05-02 14:01:36 +03:00
return ;
2019-02-21 12:21:27 +03:00
exit_with_error ( errno ) ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
static inline void complete_tx_l2fwd ( struct xsk_socket_info * xsk )
2018-05-02 14:01:36 +03:00
{
2019-03-01 09:19:41 +03:00
u32 idx_cq = 0 , idx_fq = 0 ;
2018-05-02 14:01:36 +03:00
unsigned int rcvd ;
size_t ndescs ;
if ( ! xsk - > outstanding_tx )
return ;
2019-02-21 12:21:27 +03:00
kick_tx ( xsk ) ;
2018-05-02 14:01:36 +03:00
ndescs = ( xsk - > outstanding_tx > BATCH_SIZE ) ? BATCH_SIZE :
2019-02-21 12:21:27 +03:00
xsk - > outstanding_tx ;
2018-05-02 14:01:36 +03:00
/* re-add completed Tx buffers */
2019-02-21 12:21:27 +03:00
rcvd = xsk_ring_cons__peek ( & xsk - > umem - > cq , ndescs , & idx_cq ) ;
2018-05-02 14:01:36 +03:00
if ( rcvd > 0 ) {
2019-02-21 12:21:27 +03:00
unsigned int i ;
int ret ;
ret = xsk_ring_prod__reserve ( & xsk - > umem - > fq , rcvd , & idx_fq ) ;
while ( ret ! = rcvd ) {
if ( ret < 0 )
exit_with_error ( - ret ) ;
ret = xsk_ring_prod__reserve ( & xsk - > umem - > fq , rcvd ,
& idx_fq ) ;
}
for ( i = 0 ; i < rcvd ; i + + )
* xsk_ring_prod__fill_addr ( & xsk - > umem - > fq , idx_fq + + ) =
* xsk_ring_cons__comp_addr ( & xsk - > umem - > cq ,
idx_cq + + ) ;
xsk_ring_prod__submit ( & xsk - > umem - > fq , rcvd ) ;
xsk_ring_cons__release ( & xsk - > umem - > cq , rcvd ) ;
2018-05-02 14:01:36 +03:00
xsk - > outstanding_tx - = rcvd ;
xsk - > tx_npkts + = rcvd ;
}
}
2019-02-21 12:21:27 +03:00
static inline void complete_tx_only ( struct xsk_socket_info * xsk )
2018-05-02 14:01:36 +03:00
{
unsigned int rcvd ;
2019-02-21 12:21:27 +03:00
u32 idx ;
2018-05-02 14:01:36 +03:00
if ( ! xsk - > outstanding_tx )
return ;
2019-02-21 12:21:27 +03:00
kick_tx ( xsk ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
rcvd = xsk_ring_cons__peek ( & xsk - > umem - > cq , BATCH_SIZE , & idx ) ;
2018-05-02 14:01:36 +03:00
if ( rcvd > 0 ) {
2019-02-21 12:21:27 +03:00
xsk_ring_cons__release ( & xsk - > umem - > cq , rcvd ) ;
2018-05-02 14:01:36 +03:00
xsk - > outstanding_tx - = rcvd ;
xsk - > tx_npkts + = rcvd ;
}
}
2019-02-21 12:21:27 +03:00
static void rx_drop ( struct xsk_socket_info * xsk )
2018-05-02 14:01:36 +03:00
{
unsigned int rcvd , i ;
2019-03-01 09:19:41 +03:00
u32 idx_rx = 0 , idx_fq = 0 ;
2019-02-21 12:21:27 +03:00
int ret ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
rcvd = xsk_ring_cons__peek ( & xsk - > rx , BATCH_SIZE , & idx_rx ) ;
2018-05-02 14:01:36 +03:00
if ( ! rcvd )
return ;
2019-02-21 12:21:27 +03:00
ret = xsk_ring_prod__reserve ( & xsk - > umem - > fq , rcvd , & idx_fq ) ;
while ( ret ! = rcvd ) {
if ( ret < 0 )
exit_with_error ( - ret ) ;
ret = xsk_ring_prod__reserve ( & xsk - > umem - > fq , rcvd , & idx_fq ) ;
}
2018-05-02 14:01:36 +03:00
for ( i = 0 ; i < rcvd ; i + + ) {
2019-02-21 12:21:27 +03:00
u64 addr = xsk_ring_cons__rx_desc ( & xsk - > rx , idx_rx ) - > addr ;
u32 len = xsk_ring_cons__rx_desc ( & xsk - > rx , idx_rx + + ) - > len ;
char * pkt = xsk_umem__get_data ( xsk - > umem - > buffer , addr ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
hex_dump ( pkt , len , addr ) ;
* xsk_ring_prod__fill_addr ( & xsk - > umem - > fq , idx_fq + + ) = addr ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
xsk_ring_prod__submit ( & xsk - > umem - > fq , rcvd ) ;
xsk_ring_cons__release ( & xsk - > rx , rcvd ) ;
2018-05-02 14:01:36 +03:00
xsk - > rx_npkts + = rcvd ;
}
static void rx_drop_all ( void )
{
struct pollfd fds [ MAX_SOCKS + 1 ] ;
int i , ret , timeout , nfds = 1 ;
memset ( fds , 0 , sizeof ( fds ) ) ;
for ( i = 0 ; i < num_socks ; i + + ) {
2019-02-21 12:21:27 +03:00
fds [ i ] . fd = xsk_socket__fd ( xsks [ i ] - > xsk ) ;
2018-05-02 14:01:36 +03:00
fds [ i ] . events = POLLIN ;
timeout = 1000 ; /* 1sn */
}
for ( ; ; ) {
if ( opt_poll ) {
ret = poll ( fds , nfds , timeout ) ;
if ( ret < = 0 )
continue ;
}
for ( i = 0 ; i < num_socks ; i + + )
rx_drop ( xsks [ i ] ) ;
}
}
2019-02-21 12:21:27 +03:00
static void tx_only ( struct xsk_socket_info * xsk )
2018-05-02 14:01:36 +03:00
{
int timeout , ret , nfds = 1 ;
struct pollfd fds [ nfds + 1 ] ;
2019-02-21 12:21:27 +03:00
u32 idx , frame_nb = 0 ;
2018-05-02 14:01:36 +03:00
memset ( fds , 0 , sizeof ( fds ) ) ;
2019-02-21 12:21:27 +03:00
fds [ 0 ] . fd = xsk_socket__fd ( xsk - > xsk ) ;
2018-05-02 14:01:36 +03:00
fds [ 0 ] . events = POLLOUT ;
timeout = 1000 ; /* 1sn */
for ( ; ; ) {
if ( opt_poll ) {
ret = poll ( fds , nfds , timeout ) ;
if ( ret < = 0 )
continue ;
2019-02-21 12:21:27 +03:00
if ( ! ( fds [ 0 ] . revents & POLLOUT ) )
2018-05-02 14:01:36 +03:00
continue ;
}
2019-02-21 12:21:27 +03:00
if ( xsk_ring_prod__reserve ( & xsk - > tx , BATCH_SIZE , & idx ) = =
BATCH_SIZE ) {
unsigned int i ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
for ( i = 0 ; i < BATCH_SIZE ; i + + ) {
xsk_ring_prod__tx_desc ( & xsk - > tx , idx + i ) - > addr
= ( frame_nb + i ) < <
XSK_UMEM__DEFAULT_FRAME_SHIFT ;
xsk_ring_prod__tx_desc ( & xsk - > tx , idx + i ) - > len =
sizeof ( pkt_data ) - 1 ;
}
xsk_ring_prod__submit ( & xsk - > tx , BATCH_SIZE ) ;
2018-05-02 14:01:36 +03:00
xsk - > outstanding_tx + = BATCH_SIZE ;
2019-02-21 12:21:27 +03:00
frame_nb + = BATCH_SIZE ;
frame_nb % = NUM_FRAMES ;
2018-05-02 14:01:36 +03:00
}
complete_tx_only ( xsk ) ;
}
}
2019-02-21 12:21:27 +03:00
static void l2fwd ( struct xsk_socket_info * xsk )
2018-05-02 14:01:36 +03:00
{
for ( ; ; ) {
unsigned int rcvd , i ;
2019-03-01 09:19:41 +03:00
u32 idx_rx = 0 , idx_tx = 0 ;
2018-05-02 14:01:36 +03:00
int ret ;
for ( ; ; ) {
complete_tx_l2fwd ( xsk ) ;
2019-02-21 12:21:27 +03:00
rcvd = xsk_ring_cons__peek ( & xsk - > rx , BATCH_SIZE ,
& idx_rx ) ;
2018-05-02 14:01:36 +03:00
if ( rcvd > 0 )
break ;
}
2019-02-21 12:21:27 +03:00
ret = xsk_ring_prod__reserve ( & xsk - > tx , rcvd , & idx_tx ) ;
while ( ret ! = rcvd ) {
if ( ret < 0 )
exit_with_error ( - ret ) ;
ret = xsk_ring_prod__reserve ( & xsk - > tx , rcvd , & idx_tx ) ;
}
2018-05-02 14:01:36 +03:00
for ( i = 0 ; i < rcvd ; i + + ) {
2019-02-21 12:21:27 +03:00
u64 addr = xsk_ring_cons__rx_desc ( & xsk - > rx ,
idx_rx ) - > addr ;
u32 len = xsk_ring_cons__rx_desc ( & xsk - > rx ,
idx_rx + + ) - > len ;
char * pkt = xsk_umem__get_data ( xsk - > umem - > buffer , addr ) ;
2018-05-02 14:01:36 +03:00
swap_mac_addresses ( pkt ) ;
2019-02-21 12:21:27 +03:00
hex_dump ( pkt , len , addr ) ;
xsk_ring_prod__tx_desc ( & xsk - > tx , idx_tx ) - > addr = addr ;
xsk_ring_prod__tx_desc ( & xsk - > tx , idx_tx + + ) - > len = len ;
2018-05-02 14:01:36 +03:00
}
2019-02-21 12:21:27 +03:00
xsk_ring_prod__submit ( & xsk - > tx , rcvd ) ;
xsk_ring_cons__release ( & xsk - > rx , rcvd ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
xsk - > rx_npkts + = rcvd ;
2018-05-02 14:01:36 +03:00
xsk - > outstanding_tx + = rcvd ;
}
}
int main ( int argc , char * * argv )
{
struct rlimit r = { RLIM_INFINITY , RLIM_INFINITY } ;
2019-02-21 12:21:27 +03:00
struct xsk_umem_info * umem ;
2018-05-02 14:01:36 +03:00
pthread_t pt ;
2019-02-21 12:21:27 +03:00
void * bufs ;
int ret ;
2018-05-02 14:01:36 +03:00
parse_command_line ( argc , argv ) ;
if ( setrlimit ( RLIMIT_MEMLOCK , & r ) ) {
fprintf ( stderr , " ERROR: setrlimit(RLIMIT_MEMLOCK) \" %s \" \n " ,
strerror ( errno ) ) ;
exit ( EXIT_FAILURE ) ;
}
2019-02-21 12:21:27 +03:00
ret = posix_memalign ( & bufs , getpagesize ( ) , /* PAGE_SIZE aligned */
NUM_FRAMES * XSK_UMEM__DEFAULT_FRAME_SIZE ) ;
if ( ret )
exit_with_error ( ret ) ;
2019-02-02 00:42:30 +03:00
2019-02-21 12:21:27 +03:00
/* Create sockets... */
umem = xsk_configure_umem ( bufs ,
NUM_FRAMES * XSK_UMEM__DEFAULT_FRAME_SIZE ) ;
xsks [ num_socks + + ] = xsk_configure_socket ( umem ) ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
if ( opt_bench = = BENCH_TXONLY ) {
int i ;
2018-05-02 14:01:36 +03:00
2019-02-21 12:21:27 +03:00
for ( i = 0 ; i < NUM_FRAMES * XSK_UMEM__DEFAULT_FRAME_SIZE ;
i + = XSK_UMEM__DEFAULT_FRAME_SIZE )
( void ) gen_eth_frame ( umem , i ) ;
2018-05-02 14:01:36 +03:00
}
signal ( SIGINT , int_exit ) ;
signal ( SIGTERM , int_exit ) ;
signal ( SIGABRT , int_exit ) ;
setlocale ( LC_ALL , " " ) ;
ret = pthread_create ( & pt , NULL , poller , NULL ) ;
2019-02-21 12:21:27 +03:00
if ( ret )
exit_with_error ( ret ) ;
2018-05-02 14:01:36 +03:00
prev_time = get_nsecs ( ) ;
if ( opt_bench = = BENCH_RXDROP )
rx_drop_all ( ) ;
else if ( opt_bench = = BENCH_TXONLY )
tx_only ( xsks [ 0 ] ) ;
else
l2fwd ( xsks [ 0 ] ) ;
return 0 ;
}