2018-01-19 19:15:50 +03:00
/* SPDX-License-Identifier: GPL-2.0
* Copyright ( c ) 2017 Jesper Dangaard Brouer , Red Hat , Inc .
2017-08-29 17:38:11 +03:00
*/
static const char * __doc__ =
" XDP monitor tool, based on tracepoints \n "
;
static const char * __doc_err_only__ =
" NOTICE: Only tracking XDP redirect errors \n "
" Enable TX success stats via '--stats' \n "
" (which comes with a per packet processing overhead) \n "
;
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# include <stdint.h>
# include <string.h>
# include <ctype.h>
# include <unistd.h>
# include <locale.h>
2017-10-06 11:41:51 +03:00
# include <sys/resource.h>
2017-08-29 17:38:11 +03:00
# include <getopt.h>
# include <net/if.h>
# include <time.h>
2018-05-15 08:35:02 +03:00
# include <bpf/bpf.h>
2017-08-29 17:38:11 +03:00
# include "bpf_load.h"
# include "bpf_util.h"
static int verbose = 1 ;
static bool debug = false ;
static const struct option long_options [ ] = {
{ " help " , no_argument , NULL , ' h ' } ,
{ " debug " , no_argument , NULL , ' D ' } ,
{ " stats " , no_argument , NULL , ' S ' } ,
{ " sec " , required_argument , NULL , ' s ' } ,
{ 0 , 0 , NULL , 0 }
} ;
2018-01-19 19:15:50 +03:00
/* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */
# define EXIT_FAIL_MEM 5
2017-08-29 17:38:11 +03:00
static void usage ( char * argv [ ] )
{
int i ;
printf ( " \n DOCUMENTATION: \n %s \n " , __doc__ ) ;
printf ( " \n " ) ;
printf ( " Usage: %s (options-see-below) \n " ,
argv [ 0 ] ) ;
printf ( " Listing options: \n " ) ;
for ( i = 0 ; long_options [ i ] . name ! = 0 ; i + + ) {
printf ( " --%-15s " , long_options [ i ] . name ) ;
if ( long_options [ i ] . flag ! = NULL )
printf ( " flag (internal value:%d) " ,
* long_options [ i ] . flag ) ;
else
2018-05-14 11:29:15 +03:00
printf ( " short-option: -%c " ,
2017-08-29 17:38:11 +03:00
long_options [ i ] . val ) ;
printf ( " \n " ) ;
}
printf ( " \n " ) ;
}
# define NANOSEC_PER_SEC 1000000000 /* 10^9 */
2017-10-02 00:07:34 +03:00
static __u64 gettime ( void )
2017-08-29 17:38:11 +03:00
{
struct timespec t ;
int res ;
res = clock_gettime ( CLOCK_MONOTONIC , & t ) ;
if ( res < 0 ) {
fprintf ( stderr , " Error with gettimeofday! (%i) \n " , res ) ;
exit ( EXIT_FAILURE ) ;
}
return ( __u64 ) t . tv_sec * NANOSEC_PER_SEC + t . tv_nsec ;
}
enum {
REDIR_SUCCESS = 0 ,
REDIR_ERROR = 1 ,
} ;
# define REDIR_RES_MAX 2
static const char * redir_names [ REDIR_RES_MAX ] = {
[ REDIR_SUCCESS ] = " Success " ,
[ REDIR_ERROR ] = " Error " ,
} ;
static const char * err2str ( int err )
{
if ( err < REDIR_RES_MAX )
return redir_names [ err ] ;
return NULL ;
}
2017-10-06 11:41:46 +03:00
/* enum xdp_action */
# define XDP_UNKNOWN XDP_REDIRECT + 1
# define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
static const char * xdp_action_names [ XDP_ACTION_MAX ] = {
[ XDP_ABORTED ] = " XDP_ABORTED " ,
[ XDP_DROP ] = " XDP_DROP " ,
[ XDP_PASS ] = " XDP_PASS " ,
[ XDP_TX ] = " XDP_TX " ,
[ XDP_REDIRECT ] = " XDP_REDIRECT " ,
[ XDP_UNKNOWN ] = " XDP_UNKNOWN " ,
} ;
static const char * action2str ( int action )
{
if ( action < XDP_ACTION_MAX )
return xdp_action_names [ action ] ;
return NULL ;
}
2017-08-29 17:38:11 +03:00
2018-01-19 19:15:50 +03:00
/* Common stats data record shared with _kern.c */
struct datarec {
__u64 processed ;
__u64 dropped ;
__u64 info ;
2018-05-24 17:46:22 +03:00
__u64 err ;
2018-01-19 19:15:50 +03:00
} ;
# define MAX_CPUS 64
/* Userspace structs for collection of stats from maps */
2017-08-29 17:38:11 +03:00
struct record {
__u64 timestamp ;
2018-01-19 19:15:50 +03:00
struct datarec total ;
struct datarec * cpu ;
} ;
struct u64rec {
__u64 processed ;
} ;
struct record_u64 {
/* record for _kern side __u64 values */
__u64 timestamp ;
struct u64rec total ;
struct u64rec * cpu ;
2017-08-29 17:38:11 +03:00
} ;
struct stats_record {
2018-01-19 19:15:50 +03:00
struct record_u64 xdp_redirect [ REDIR_RES_MAX ] ;
struct record_u64 xdp_exception [ XDP_ACTION_MAX ] ;
struct record xdp_cpumap_kthread ;
struct record xdp_cpumap_enqueue [ MAX_CPUS ] ;
2018-05-24 17:46:02 +03:00
struct record xdp_devmap_xmit ;
2017-08-29 17:38:11 +03:00
} ;
2018-01-19 19:15:50 +03:00
static bool map_collect_record ( int fd , __u32 key , struct record * rec )
2017-08-29 17:38:11 +03:00
{
2018-01-19 19:15:50 +03:00
/* For percpu maps, userspace gets a value per possible CPU */
unsigned int nr_cpus = bpf_num_possible_cpus ( ) ;
struct datarec values [ nr_cpus ] ;
__u64 sum_processed = 0 ;
__u64 sum_dropped = 0 ;
__u64 sum_info = 0 ;
2018-05-24 17:46:22 +03:00
__u64 sum_err = 0 ;
2018-01-19 19:15:50 +03:00
int i ;
if ( ( bpf_map_lookup_elem ( fd , & key , values ) ) ! = 0 ) {
fprintf ( stderr ,
" ERR: bpf_map_lookup_elem failed key:0x%X \n " , key ) ;
return false ;
}
/* Get time as close as possible to reading map contents */
rec - > timestamp = gettime ( ) ;
2017-08-29 17:38:11 +03:00
2018-01-19 19:15:50 +03:00
/* Record and sum values from each CPU */
for ( i = 0 ; i < nr_cpus ; i + + ) {
rec - > cpu [ i ] . processed = values [ i ] . processed ;
sum_processed + = values [ i ] . processed ;
rec - > cpu [ i ] . dropped = values [ i ] . dropped ;
sum_dropped + = values [ i ] . dropped ;
rec - > cpu [ i ] . info = values [ i ] . info ;
sum_info + = values [ i ] . info ;
2018-05-24 17:46:22 +03:00
rec - > cpu [ i ] . err = values [ i ] . err ;
sum_err + = values [ i ] . err ;
2018-01-19 19:15:50 +03:00
}
rec - > total . processed = sum_processed ;
rec - > total . dropped = sum_dropped ;
rec - > total . info = sum_info ;
2018-05-24 17:46:22 +03:00
rec - > total . err = sum_err ;
2018-01-19 19:15:50 +03:00
return true ;
}
static bool map_collect_record_u64 ( int fd , __u32 key , struct record_u64 * rec )
{
/* For percpu maps, userspace gets a value per possible CPU */
unsigned int nr_cpus = bpf_num_possible_cpus ( ) ;
struct u64rec values [ nr_cpus ] ;
__u64 sum_total = 0 ;
int i ;
if ( ( bpf_map_lookup_elem ( fd , & key , values ) ) ! = 0 ) {
fprintf ( stderr ,
" ERR: bpf_map_lookup_elem failed key:0x%X \n " , key ) ;
return false ;
}
/* Get time as close as possible to reading map contents */
rec - > timestamp = gettime ( ) ;
/* Record and sum values from each CPU */
for ( i = 0 ; i < nr_cpus ; i + + ) {
rec - > cpu [ i ] . processed = values [ i ] . processed ;
sum_total + = values [ i ] . processed ;
}
rec - > total . processed = sum_total ;
return true ;
2017-10-06 11:41:46 +03:00
}
static double calc_period ( struct record * r , struct record * p )
{
double period_ = 0 ;
__u64 period = 0 ;
period = r - > timestamp - p - > timestamp ;
if ( period > 0 )
period_ = ( ( double ) period / NANOSEC_PER_SEC ) ;
return period_ ;
}
2018-01-19 19:15:50 +03:00
static double calc_period_u64 ( struct record_u64 * r , struct record_u64 * p )
{
double period_ = 0 ;
__u64 period = 0 ;
period = r - > timestamp - p - > timestamp ;
if ( period > 0 )
period_ = ( ( double ) period / NANOSEC_PER_SEC ) ;
return period_ ;
}
static double calc_pps ( struct datarec * r , struct datarec * p , double period )
{
__u64 packets = 0 ;
double pps = 0 ;
if ( period > 0 ) {
packets = r - > processed - p - > processed ;
pps = packets / period ;
}
return pps ;
}
static double calc_pps_u64 ( struct u64rec * r , struct u64rec * p , double period )
{
__u64 packets = 0 ;
double pps = 0 ;
if ( period > 0 ) {
packets = r - > processed - p - > processed ;
pps = packets / period ;
}
return pps ;
}
static double calc_drop ( struct datarec * r , struct datarec * p , double period )
{
__u64 packets = 0 ;
double pps = 0 ;
if ( period > 0 ) {
packets = r - > dropped - p - > dropped ;
pps = packets / period ;
}
return pps ;
}
static double calc_info ( struct datarec * r , struct datarec * p , double period )
2017-10-06 11:41:46 +03:00
{
__u64 packets = 0 ;
double pps = 0 ;
if ( period > 0 ) {
2018-01-19 19:15:50 +03:00
packets = r - > info - p - > info ;
2017-10-06 11:41:46 +03:00
pps = packets / period ;
}
return pps ;
2017-08-29 17:38:11 +03:00
}
2018-05-24 17:46:22 +03:00
static double calc_err ( struct datarec * r , struct datarec * p , double period )
{
__u64 packets = 0 ;
double pps = 0 ;
if ( period > 0 ) {
packets = r - > err - p - > err ;
pps = packets / period ;
}
return pps ;
}
2018-01-19 19:15:50 +03:00
static void stats_print ( struct stats_record * stats_rec ,
struct stats_record * stats_prev ,
2017-08-29 17:38:11 +03:00
bool err_only )
{
2018-01-19 19:15:50 +03:00
unsigned int nr_cpus = bpf_num_possible_cpus ( ) ;
int rec_i = 0 , i , to_cpu ;
double t = 0 , pps = 0 ;
2017-08-29 17:38:11 +03:00
2018-01-19 19:15:50 +03:00
/* Header */
printf ( " %-15s %-7s %-12s %-12s %-9s \n " ,
" XDP-event " , " CPU:to " , " pps " , " drop-pps " , " extra-info " ) ;
2017-10-06 11:41:46 +03:00
/* tracepoint: xdp:xdp_redirect_* */
2017-08-29 17:38:11 +03:00
if ( err_only )
2018-01-19 19:15:50 +03:00
rec_i = REDIR_ERROR ;
for ( ; rec_i < REDIR_RES_MAX ; rec_i + + ) {
struct record_u64 * rec , * prev ;
char * fmt1 = " %-15s %-7d %'-12.0f %'-12.0f %s \n " ;
char * fmt2 = " %-15s %-7s %'-12.0f %'-12.0f %s \n " ;
rec = & stats_rec - > xdp_redirect [ rec_i ] ;
prev = & stats_prev - > xdp_redirect [ rec_i ] ;
t = calc_period_u64 ( rec , prev ) ;
for ( i = 0 ; i < nr_cpus ; i + + ) {
struct u64rec * r = & rec - > cpu [ i ] ;
struct u64rec * p = & prev - > cpu [ i ] ;
pps = calc_pps_u64 ( r , p , t ) ;
if ( pps > 0 )
printf ( fmt1 , " XDP_REDIRECT " , i ,
rec_i ? 0.0 : pps , rec_i ? pps : 0.0 ,
err2str ( rec_i ) ) ;
2017-08-29 17:38:11 +03:00
}
2018-01-19 19:15:50 +03:00
pps = calc_pps_u64 ( & rec - > total , & prev - > total , t ) ;
printf ( fmt2 , " XDP_REDIRECT " , " total " ,
rec_i ? 0.0 : pps , rec_i ? pps : 0.0 , err2str ( rec_i ) ) ;
2017-10-06 11:41:46 +03:00
}
2017-08-29 17:38:11 +03:00
2017-10-06 11:41:46 +03:00
/* tracepoint: xdp:xdp_exception */
2018-01-19 19:15:50 +03:00
for ( rec_i = 0 ; rec_i < XDP_ACTION_MAX ; rec_i + + ) {
struct record_u64 * rec , * prev ;
char * fmt1 = " %-15s %-7d %'-12.0f %'-12.0f %s \n " ;
char * fmt2 = " %-15s %-7s %'-12.0f %'-12.0f %s \n " ;
rec = & stats_rec - > xdp_exception [ rec_i ] ;
prev = & stats_prev - > xdp_exception [ rec_i ] ;
t = calc_period_u64 ( rec , prev ) ;
for ( i = 0 ; i < nr_cpus ; i + + ) {
struct u64rec * r = & rec - > cpu [ i ] ;
struct u64rec * p = & prev - > cpu [ i ] ;
pps = calc_pps_u64 ( r , p , t ) ;
if ( pps > 0 )
printf ( fmt1 , " Exception " , i ,
2018-04-17 17:08:06 +03:00
0.0 , pps , action2str ( rec_i ) ) ;
2017-10-06 11:41:46 +03:00
}
2018-01-19 19:15:50 +03:00
pps = calc_pps_u64 ( & rec - > total , & prev - > total , t ) ;
2017-10-06 11:41:46 +03:00
if ( pps > 0 )
2018-01-19 19:15:50 +03:00
printf ( fmt2 , " Exception " , " total " ,
0.0 , pps , action2str ( rec_i ) ) ;
2017-08-29 17:38:11 +03:00
}
2018-01-19 19:15:50 +03:00
/* cpumap enqueue stats */
for ( to_cpu = 0 ; to_cpu < MAX_CPUS ; to_cpu + + ) {
char * fmt1 = " %-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s \n " ;
char * fmt2 = " %-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s \n " ;
struct record * rec , * prev ;
char * info_str = " " ;
double drop , info ;
rec = & stats_rec - > xdp_cpumap_enqueue [ to_cpu ] ;
prev = & stats_prev - > xdp_cpumap_enqueue [ to_cpu ] ;
t = calc_period ( rec , prev ) ;
for ( i = 0 ; i < nr_cpus ; i + + ) {
struct datarec * r = & rec - > cpu [ i ] ;
struct datarec * p = & prev - > cpu [ i ] ;
pps = calc_pps ( r , p , t ) ;
drop = calc_drop ( r , p , t ) ;
info = calc_info ( r , p , t ) ;
if ( info > 0 ) {
info_str = " bulk-average " ;
info = pps / info ; /* calc average bulk size */
}
if ( pps > 0 )
printf ( fmt1 , " cpumap-enqueue " ,
i , to_cpu , pps , drop , info , info_str ) ;
}
pps = calc_pps ( & rec - > total , & prev - > total , t ) ;
if ( pps > 0 ) {
drop = calc_drop ( & rec - > total , & prev - > total , t ) ;
info = calc_info ( & rec - > total , & prev - > total , t ) ;
if ( info > 0 ) {
info_str = " bulk-average " ;
info = pps / info ; /* calc average bulk size */
}
printf ( fmt2 , " cpumap-enqueue " ,
" sum " , to_cpu , pps , drop , info , info_str ) ;
}
2017-08-29 17:38:11 +03:00
}
2018-01-19 19:15:50 +03:00
/* cpumap kthread stats */
{
char * fmt1 = " %-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s \n " ;
char * fmt2 = " %-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s \n " ;
struct record * rec , * prev ;
double drop , info ;
char * i_str = " " ;
rec = & stats_rec - > xdp_cpumap_kthread ;
prev = & stats_prev - > xdp_cpumap_kthread ;
t = calc_period ( rec , prev ) ;
for ( i = 0 ; i < nr_cpus ; i + + ) {
struct datarec * r = & rec - > cpu [ i ] ;
struct datarec * p = & prev - > cpu [ i ] ;
pps = calc_pps ( r , p , t ) ;
drop = calc_drop ( r , p , t ) ;
info = calc_info ( r , p , t ) ;
if ( info > 0 )
i_str = " sched " ;
2018-05-24 17:46:02 +03:00
if ( pps > 0 | | drop > 0 )
2018-01-19 19:15:50 +03:00
printf ( fmt1 , " cpumap-kthread " ,
i , pps , drop , info , i_str ) ;
}
pps = calc_pps ( & rec - > total , & prev - > total , t ) ;
drop = calc_drop ( & rec - > total , & prev - > total , t ) ;
info = calc_info ( & rec - > total , & prev - > total , t ) ;
if ( info > 0 )
i_str = " sched-sum " ;
printf ( fmt2 , " cpumap-kthread " , " total " , pps , drop , info , i_str ) ;
2017-08-29 17:38:11 +03:00
}
2018-01-19 19:15:50 +03:00
2018-05-24 17:46:02 +03:00
/* devmap ndo_xdp_xmit stats */
{
2018-05-24 17:46:22 +03:00
char * fmt1 = " %-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s \n " ;
char * fmt2 = " %-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s \n " ;
2018-05-24 17:46:02 +03:00
struct record * rec , * prev ;
2018-05-24 17:46:22 +03:00
double drop , info , err ;
2018-05-24 17:46:02 +03:00
char * i_str = " " ;
2018-05-24 17:46:22 +03:00
char * err_str = " " ;
2018-05-24 17:46:02 +03:00
rec = & stats_rec - > xdp_devmap_xmit ;
prev = & stats_prev - > xdp_devmap_xmit ;
t = calc_period ( rec , prev ) ;
for ( i = 0 ; i < nr_cpus ; i + + ) {
struct datarec * r = & rec - > cpu [ i ] ;
struct datarec * p = & prev - > cpu [ i ] ;
pps = calc_pps ( r , p , t ) ;
drop = calc_drop ( r , p , t ) ;
info = calc_info ( r , p , t ) ;
2018-05-24 17:46:22 +03:00
err = calc_err ( r , p , t ) ;
2018-05-24 17:46:02 +03:00
if ( info > 0 ) {
i_str = " bulk-average " ;
info = ( pps + drop ) / info ; /* calc avg bulk */
}
2018-05-24 17:46:22 +03:00
if ( err > 0 )
err_str = " drv-err " ;
2018-05-24 17:46:02 +03:00
if ( pps > 0 | | drop > 0 )
printf ( fmt1 , " devmap-xmit " ,
2018-05-24 17:46:22 +03:00
i , pps , drop , info , i_str , err_str ) ;
2018-05-24 17:46:02 +03:00
}
pps = calc_pps ( & rec - > total , & prev - > total , t ) ;
drop = calc_drop ( & rec - > total , & prev - > total , t ) ;
info = calc_info ( & rec - > total , & prev - > total , t ) ;
2018-05-24 17:46:22 +03:00
err = calc_err ( & rec - > total , & prev - > total , t ) ;
2018-05-24 17:46:02 +03:00
if ( info > 0 ) {
i_str = " bulk-average " ;
info = ( pps + drop ) / info ; /* calc avg bulk */
}
2018-05-24 17:46:22 +03:00
if ( err > 0 )
err_str = " drv-err " ;
printf ( fmt2 , " devmap-xmit " , " total " , pps , drop ,
info , i_str , err_str ) ;
2018-05-24 17:46:02 +03:00
}
2018-01-19 19:15:50 +03:00
printf ( " \n " ) ;
2017-08-29 17:38:11 +03:00
}
2017-10-06 11:41:46 +03:00
static bool stats_collect ( struct stats_record * rec )
2017-08-29 17:38:11 +03:00
{
2017-10-06 11:41:46 +03:00
int fd ;
2017-08-29 17:38:11 +03:00
int i ;
/* TODO: Detect if someone unloaded the perf event_fd's, as
* this can happen by someone running perf - record - e
*/
2017-10-06 11:41:46 +03:00
fd = map_data [ 0 ] . fd ; /* map0: redirect_err_cnt */
2018-01-19 19:15:50 +03:00
for ( i = 0 ; i < REDIR_RES_MAX ; i + + )
map_collect_record_u64 ( fd , i , & rec - > xdp_redirect [ i ] ) ;
2017-10-06 11:41:46 +03:00
fd = map_data [ 1 ] . fd ; /* map1: exception_cnt */
for ( i = 0 ; i < XDP_ACTION_MAX ; i + + ) {
2018-01-19 19:15:50 +03:00
map_collect_record_u64 ( fd , i , & rec - > xdp_exception [ i ] ) ;
2017-10-06 11:41:46 +03:00
}
2018-01-19 19:15:50 +03:00
fd = map_data [ 2 ] . fd ; /* map2: cpumap_enqueue_cnt */
for ( i = 0 ; i < MAX_CPUS ; i + + )
map_collect_record ( fd , i , & rec - > xdp_cpumap_enqueue [ i ] ) ;
fd = map_data [ 3 ] . fd ; /* map3: cpumap_kthread_cnt */
map_collect_record ( fd , 0 , & rec - > xdp_cpumap_kthread ) ;
2018-05-24 17:46:02 +03:00
fd = map_data [ 4 ] . fd ; /* map4: devmap_xmit_cnt */
map_collect_record ( fd , 0 , & rec - > xdp_devmap_xmit ) ;
2017-08-29 17:38:11 +03:00
return true ;
}
2018-01-19 19:15:50 +03:00
static void * alloc_rec_per_cpu ( int record_size )
{
unsigned int nr_cpus = bpf_num_possible_cpus ( ) ;
void * array ;
2020-06-12 21:53:27 +03:00
array = calloc ( nr_cpus , record_size ) ;
2018-01-19 19:15:50 +03:00
if ( ! array ) {
fprintf ( stderr , " Mem alloc error (nr_cpus:%u) \n " , nr_cpus ) ;
exit ( EXIT_FAIL_MEM ) ;
}
return array ;
}
static struct stats_record * alloc_stats_record ( void )
{
struct stats_record * rec ;
int rec_sz ;
int i ;
/* Alloc main stats_record structure */
2020-06-12 21:53:27 +03:00
rec = calloc ( 1 , sizeof ( * rec ) ) ;
2018-01-19 19:15:50 +03:00
if ( ! rec ) {
fprintf ( stderr , " Mem alloc error \n " ) ;
exit ( EXIT_FAIL_MEM ) ;
}
/* Alloc stats stored per CPU for each record */
rec_sz = sizeof ( struct u64rec ) ;
for ( i = 0 ; i < REDIR_RES_MAX ; i + + )
rec - > xdp_redirect [ i ] . cpu = alloc_rec_per_cpu ( rec_sz ) ;
for ( i = 0 ; i < XDP_ACTION_MAX ; i + + )
rec - > xdp_exception [ i ] . cpu = alloc_rec_per_cpu ( rec_sz ) ;
rec_sz = sizeof ( struct datarec ) ;
rec - > xdp_cpumap_kthread . cpu = alloc_rec_per_cpu ( rec_sz ) ;
2018-05-24 17:46:02 +03:00
rec - > xdp_devmap_xmit . cpu = alloc_rec_per_cpu ( rec_sz ) ;
2018-01-19 19:15:50 +03:00
for ( i = 0 ; i < MAX_CPUS ; i + + )
rec - > xdp_cpumap_enqueue [ i ] . cpu = alloc_rec_per_cpu ( rec_sz ) ;
return rec ;
}
static void free_stats_record ( struct stats_record * r )
{
int i ;
for ( i = 0 ; i < REDIR_RES_MAX ; i + + )
free ( r - > xdp_redirect [ i ] . cpu ) ;
for ( i = 0 ; i < XDP_ACTION_MAX ; i + + )
free ( r - > xdp_exception [ i ] . cpu ) ;
free ( r - > xdp_cpumap_kthread . cpu ) ;
2018-05-24 17:46:02 +03:00
free ( r - > xdp_devmap_xmit . cpu ) ;
2018-01-19 19:15:50 +03:00
for ( i = 0 ; i < MAX_CPUS ; i + + )
free ( r - > xdp_cpumap_enqueue [ i ] . cpu ) ;
free ( r ) ;
}
/* Pointer swap trick */
static inline void swap ( struct stats_record * * a , struct stats_record * * b )
{
struct stats_record * tmp ;
tmp = * a ;
* a = * b ;
* b = tmp ;
}
2017-08-29 17:38:11 +03:00
static void stats_poll ( int interval , bool err_only )
{
2018-01-19 19:15:50 +03:00
struct stats_record * rec , * prev ;
2017-08-29 17:38:11 +03:00
2018-01-19 19:15:50 +03:00
rec = alloc_stats_record ( ) ;
prev = alloc_stats_record ( ) ;
stats_collect ( rec ) ;
if ( err_only )
printf ( " \n %s \n " , __doc_err_only__ ) ;
2017-08-29 17:38:11 +03:00
/* Trick to pretty printf with thousands separators use %' */
setlocale ( LC_NUMERIC , " en_US " ) ;
/* Header */
if ( verbose )
printf ( " \n %s " , __doc__ ) ;
/* TODO Need more advanced stats on error types */
2017-10-06 11:41:46 +03:00
if ( verbose ) {
printf ( " - Stats map0: %s \n " , map_data [ 0 ] . name ) ;
printf ( " - Stats map1: %s \n " , map_data [ 1 ] . name ) ;
printf ( " \n " ) ;
}
2017-08-29 17:38:11 +03:00
fflush ( stdout ) ;
while ( 1 ) {
2018-01-19 19:15:50 +03:00
swap ( & prev , & rec ) ;
stats_collect ( rec ) ;
stats_print ( rec , prev , err_only ) ;
2017-08-29 17:38:11 +03:00
fflush ( stdout ) ;
sleep ( interval ) ;
}
2018-01-19 19:15:50 +03:00
free_stats_record ( rec ) ;
free_stats_record ( prev ) ;
2017-08-29 17:38:11 +03:00
}
2017-10-02 00:07:34 +03:00
static void print_bpf_prog_info ( void )
2017-08-29 17:38:11 +03:00
{
int i ;
/* Prog info */
printf ( " Loaded BPF prog have %d bpf program(s) \n " , prog_cnt ) ;
for ( i = 0 ; i < prog_cnt ; i + + ) {
printf ( " - prog_fd[%d] = fd(%d) \n " , i , prog_fd [ i ] ) ;
}
/* Maps info */
printf ( " Loaded BPF prog have %d map(s) \n " , map_data_count ) ;
for ( i = 0 ; i < map_data_count ; i + + ) {
char * name = map_data [ i ] . name ;
int fd = map_data [ i ] . fd ;
printf ( " - map_data[%d] = fd(%d) name:%s \n " , i , fd , name ) ;
}
/* Event info */
printf ( " Searching for (max:%d) event file descriptor(s) \n " , prog_cnt ) ;
for ( i = 0 ; i < prog_cnt ; i + + ) {
if ( event_fd [ i ] ! = - 1 )
printf ( " - event_fd[%d] = fd(%d) \n " , i , event_fd [ i ] ) ;
}
}
int main ( int argc , char * * argv )
{
2017-10-06 11:41:51 +03:00
struct rlimit r = { RLIM_INFINITY , RLIM_INFINITY } ;
2017-08-29 17:38:11 +03:00
int longindex = 0 , opt ;
int ret = EXIT_SUCCESS ;
char bpf_obj_file [ 256 ] ;
/* Default settings: */
bool errors_only = true ;
int interval = 2 ;
snprintf ( bpf_obj_file , sizeof ( bpf_obj_file ) , " %s_kern.o " , argv [ 0 ] ) ;
/* Parse commands line args */
2018-05-14 11:29:15 +03:00
while ( ( opt = getopt_long ( argc , argv , " hDSs: " ,
2017-08-29 17:38:11 +03:00
long_options , & longindex ) ) ! = - 1 ) {
switch ( opt ) {
case ' D ' :
debug = true ;
break ;
case ' S ' :
errors_only = false ;
break ;
case ' s ' :
interval = atoi ( optarg ) ;
break ;
case ' h ' :
default :
usage ( argv ) ;
return EXIT_FAILURE ;
}
}
2017-10-06 11:41:51 +03:00
if ( setrlimit ( RLIMIT_MEMLOCK , & r ) ) {
perror ( " setrlimit(RLIMIT_MEMLOCK) " ) ;
return EXIT_FAILURE ;
}
2017-08-29 17:38:11 +03:00
if ( load_bpf_file ( bpf_obj_file ) ) {
printf ( " ERROR - bpf_log_buf: %s " , bpf_log_buf ) ;
2017-10-06 11:41:51 +03:00
return EXIT_FAILURE ;
2017-08-29 17:38:11 +03:00
}
if ( ! prog_fd [ 0 ] ) {
printf ( " ERROR - load_bpf_file: %s \n " , strerror ( errno ) ) ;
2017-10-06 11:41:51 +03:00
return EXIT_FAILURE ;
2017-08-29 17:38:11 +03:00
}
if ( debug ) {
print_bpf_prog_info ( ) ;
}
/* Unload/stop tracepoint event by closing fd's */
if ( errors_only ) {
/* The prog_fd[i] and event_fd[i] depend on the
* order the functions was defined in _kern . c
*/
close ( event_fd [ 2 ] ) ; /* tracepoint/xdp/xdp_redirect */
close ( prog_fd [ 2 ] ) ; /* func: trace_xdp_redirect */
close ( event_fd [ 3 ] ) ; /* tracepoint/xdp/xdp_redirect_map */
close ( prog_fd [ 3 ] ) ; /* func: trace_xdp_redirect_map */
}
stats_poll ( interval , errors_only ) ;
return ret ;
}