LCOV - code coverage report
Current view: top level - app/shared - fd_auto_net.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 256 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 57 0.0 %

          Line data    Source code
       1             : #include "fd_auto_net.h"
       2             : #include "../../disco/net/fd_linux_bond.h"
       3             : #include "../../disco/net/fd_net_tile.h"
       4             : #include "../../disco/net/mlx5/fd_mlx5.h"
       5             : #include "../../disco/netlink/fd_netlink_tile.h"
       6             : #include "../../waltz/mib/fd_netdev_netlink.h"
       7             : 
       8             : #include <stdlib.h> /* strtoul */
       9             : #include <limits.h>
      10             : #include <errno.h>
      11             : #include <string.h>
      12             : #include <unistd.h>
      13             : #include <sys/utsname.h>
      14             : #include <linux/if_arp.h>
      15             : 
      16           0 : #define PROVIDER_CNT       ( 2UL)
      17           0 : #define FEAT_CNT_MAX       (16UL)
      18           0 : #define NET_DRIVER_CNT_MAX (16UL)
      19             : 
      20             : struct fd_auto_info {
      21             :   /* System */
      22             :   ulong linux_major;
      23             :   ulong linux_minor;
      24             : 
      25             :   /* Networking */
      26             :   char driver[ NAME_SZ ];
      27             :   uint is_virtual_if:1;
      28             :   uint is_bonded_if:1;
      29             :   uint is_lacp_if:1;
      30             :   uint bonded_if_slave_count;
      31             :   int  is_using_gre;
      32             :   int  has_mlx5_rdma_port;
      33             :   int  has_uverbs;
      34             :   int  has_uverbs_module;
      35             : };
      36             : typedef struct fd_auto_info fd_auto_info_t;
      37             : 
      38             : struct fd_auto_driver_rq {
      39             :   char const * name;
      40             :   ulong        min_linux_major;
      41             :   ulong        min_linux_minor;
      42             : 
      43             :   /* 0 = no max linux version */
      44             :   ulong        max_linux_major;
      45             :   ulong        max_linux_minor;
      46             : };
      47             : typedef struct fd_auto_driver_rq fd_auto_driver_rq_t;
      48             : 
      49             : struct fd_auto_feat {
      50             :   char const *         name;
      51             :   /* supported_drivers is for unconditional driver requirements.
      52             :      For conditional driver requirements leave this empty and call
      53             :      fd_auto_check_driver() inside the check() callback
      54             :      (like xdp_rss_queue_mode_check() does). */
      55             :   fd_auto_driver_rq_t  supported_drivers[ NET_DRIVER_CNT_MAX ];
      56             : 
      57             :   /* is_feat_auto() checks if feature is auto and sets the default */
      58             :   int  (*is_feat_auto)( fd_config_t          * config );
      59             :   int  (*check       )( fd_config_t    const * config,
      60             :                         fd_auto_info_t const * info   );
      61             :   void (*apply       )( fd_config_t          * config );
      62             : };
      63             : typedef struct fd_auto_feat fd_auto_feat_t;
      64             : 
      65             : struct fd_auto_provider {
      66             :   char const *         name;
      67             :   /* Later features observe earlier features updates to config,
      68             :      so the order is important.  e.g. ZC checks xdp_mode is DRV
      69             :      so DRV needs to come before ZC in the feats ordering. */
      70             :   fd_auto_feat_t       feats            [ FEAT_CNT_MAX ];
      71             :   fd_auto_driver_rq_t  supported_drivers[ NET_DRIVER_CNT_MAX ];
      72             : 
      73             :   int  (*check)( fd_config_t    const * config,
      74             :                  fd_auto_info_t const * info   );
      75             :   void (*apply)( fd_config_t          * config );
      76             : };
      77             : typedef struct fd_auto_provider fd_auto_provider_t;
      78             : 
      79             : static int
      80             : fd_auto_check_driver( fd_auto_info_t      const * info,
      81           0 :                       fd_auto_driver_rq_t const * supported_drivers ) {
      82             :   /* Check for driver requirements. */
      83           0 :   if( !supported_drivers[0].name ) return 1;
      84             : 
      85           0 :   for( ulong i=0UL; i<NET_DRIVER_CNT_MAX; i++ ) {
      86           0 :     fd_auto_driver_rq_t const * req = &supported_drivers[ i ];
      87           0 :     if( !req->name ) return 0;
      88             : 
      89           0 :     if( 0==strcmp( info->driver, req->name ) ) {
      90             :       /* Check system linux version is within bounds of min and max
      91             :          of what this driver req requires. */
      92           0 :       if( req->max_linux_major > 0UL ) {
      93           0 :         if( info->linux_major>req->max_linux_major ||
      94           0 :           ( info->linux_major==req->max_linux_major &&
      95           0 :             info->linux_minor>req->max_linux_minor ) ) continue;
      96           0 :       }
      97             : 
      98           0 :       if( info->linux_major>req->min_linux_major ||
      99           0 :         ( info->linux_major==req->min_linux_major &&
     100           0 :           info->linux_minor>=req->min_linux_minor ) ) return 1;
     101           0 :     }
     102           0 :   }
     103             : 
     104           0 :   return 0;
     105           0 : }
     106             : 
     107             : /* XDP checks/apply */
     108             : 
     109             : static int
     110             : xdp_check( fd_config_t    const * config,
     111           0 :            fd_auto_info_t const * info FD_PARAM_UNUSED ) {
     112           0 :   if( strcmp( config->net.provider, "auto" ) ) return 0;
     113           0 :   return 1;
     114           0 : }
     115             : 
     116             : static void
     117           0 : xdp_apply( fd_config_t * config ) {
     118           0 :   fd_memcpy( config->net.provider, "xdp", 4 );
     119           0 : }
     120             : 
     121             : static int
     122           0 : is_xdp_listen_gre_auto( fd_config_t * config ) {
     123           0 :   if( 2!=config->net.xdp.listen_gre ) return 0;
     124             :   /* Set to default */
     125           0 :   config->net.xdp.listen_gre = 0;
     126           0 :   return 1;
     127           0 : }
     128             : 
     129             : static int
     130             : xdp_listen_gre_check( fd_config_t    const * config FD_PARAM_UNUSED,
     131           0 :                       fd_auto_info_t const * info ) {
     132           0 :   return info->is_using_gre;
     133           0 : }
     134             : 
     135             : static void
     136           0 : xdp_listen_gre_apply( fd_config_t * config ) {
     137           0 :   config->net.xdp.listen_gre = 1;
     138           0 : }
     139             : 
     140             : static int
     141           0 : is_xdp_rss_queue_mode_auto( fd_config_t * config ) {
     142           0 :   if( strcmp( config->net.xdp.rss_queue_mode, "auto" ) ) return 0;
     143             :   /* Set to default */
     144           0 :   fd_memcpy( config->net.xdp.rss_queue_mode, "simple", 7 );
     145           0 :   return 1;
     146           0 : }
     147             : 
     148             : static int
     149             : xdp_rss_queue_mode_check( fd_config_t    const * config,
     150           0 :                           fd_auto_info_t const * info ) {
     151           0 :   if( 1==config->net.xdp.listen_gre ) {
     152           0 :     static fd_auto_driver_rq_t const gre_rss_drivers[ NET_DRIVER_CNT_MAX ] = {
     153           0 :       { "mlx5_core", 0, 0, 0, 0 }
     154           0 :     };
     155             : 
     156           0 :     if( 0==fd_auto_check_driver( info, gre_rss_drivers ) ) return 0;
     157           0 :   }
     158           0 :   return 1;
     159           0 : }
     160             : 
     161             : static void
     162           0 : xdp_rss_queue_mode_apply( fd_config_t * config ) {
     163           0 :   fd_memcpy( config->net.xdp.rss_queue_mode, "auto", 5 );
     164           0 : }
     165             : 
     166             : static int
     167           0 : is_xdp_native_bond_auto( fd_config_t * config ) {
     168           0 :   if( 2!=config->net.xdp.native_bond ) return 0;
     169             :   /* Set to default */
     170           0 :   config->net.xdp.native_bond = 0;
     171           0 :   return 1;
     172           0 : }
     173             : 
     174             : static int
     175             : xdp_native_bond_check( fd_config_t    const * config,
     176           0 :                        fd_auto_info_t const * info ) {
     177           0 :   if( !info->is_bonded_if ) return 0;
     178           0 :   if( !info->is_lacp_if ) return 0;
     179             : 
     180           0 :   if( 0==info->bonded_if_slave_count || info->bonded_if_slave_count>FD_NET_BOND_SLAVE_MAX ) return 0;
     181             : 
     182             :   /* net_tile_count must be a multiple of the bonded interface's slave count */
     183           0 :   if( 0!=config->layout.net_tile_count%info->bonded_if_slave_count ) return 0;
     184             : 
     185           0 :   return 1;
     186           0 : }
     187             : 
     188             : static void
     189           0 : xdp_native_bond_apply( fd_config_t * config ) {
     190           0 :   config->net.xdp.native_bond = 1;
     191           0 : }
     192             : 
     193             : static int
     194           0 : is_xdp_mode_auto( fd_config_t * config ) {
     195           0 :   if( strcmp( config->net.xdp.xdp_mode, "auto" ) ) return 0;
     196             :   /* Set to default */
     197           0 :   fd_memcpy( config->net.xdp.xdp_mode, "skb", 4 );
     198           0 :   return 1;
     199           0 : }
     200             : 
     201             : static int
     202             : xdp_drv_check( fd_config_t    const * config,
     203           0 :                fd_auto_info_t const * info ) {
     204           0 :   if( info->is_virtual_if &&
     205           0 :       !config->net.xdp.native_bond ) return 0;
     206           0 :   return 1;
     207           0 : }
     208             : 
     209             : static void
     210           0 : xdp_drv_apply( fd_config_t * config ) {
     211           0 :   fd_memcpy( config->net.xdp.xdp_mode, "drv", 4 );
     212           0 : }
     213             : 
     214             : static int
     215           0 : is_xdp_prefbusy_auto( fd_config_t * config ) {
     216           0 :   if( strcmp( config->net.xdp.poll_mode, "auto" ) ) return 0;
     217             :   /* Set to default */
     218           0 :   fd_memcpy( config->net.xdp.poll_mode, "softirq", 8 );
     219           0 :   return 1;
     220           0 : }
     221             : 
     222             : static int
     223             : xdp_prefbusy_check( fd_config_t    const * config,
     224           0 :                     fd_auto_info_t const * info FD_PARAM_UNUSED ) {
     225           0 :   if( strcmp( config->net.xdp.xdp_mode, "drv") ) return 0;
     226           0 :   return 1;
     227           0 : }
     228             : 
     229             : static void
     230           0 : xdp_prefbusy_apply( fd_config_t * config ) {
     231           0 :   fd_memcpy( config->net.xdp.poll_mode, "prefbusy", 9 );
     232           0 : }
     233             : 
     234             : static int
     235           0 : is_xdp_zc_auto( fd_config_t * config ) {
     236           0 :   if( 2!=config->net.xdp.xdp_zero_copy ) return 0;
     237             :   /* Set to default */
     238           0 :   config->net.xdp.xdp_zero_copy = 0;
     239           0 :   return 1;
     240           0 : }
     241             : 
     242             : static int
     243             : xdp_zc_check( fd_config_t    const * config,
     244           0 :               fd_auto_info_t const * info FD_PARAM_UNUSED ) {
     245           0 :   if( strcmp( config->net.xdp.xdp_mode, "drv") ) return 0;
     246           0 :   return 1;
     247           0 : }
     248             : 
     249             : static void
     250           0 : xdp_zc_apply( fd_config_t * config ) {
     251           0 :   config->net.xdp.xdp_zero_copy = 1;
     252           0 : }
     253             : 
     254             : /* mlx5 tile checks/apply */
     255             : static int
     256             : mlx5_check( fd_config_t    const * config,
     257           0 :             fd_auto_info_t const * info ) {
     258           0 :   if( strcmp( config->net.provider, "auto" ) ) return 0;
     259           0 :   if( !info->has_mlx5_rdma_port ) return 0;
     260           0 :   if( !info->has_uverbs && !info->has_uverbs_module ) return 0;
     261           0 :   return 1;
     262           0 : }
     263             : 
     264             : static void
     265           0 : mlx5_apply( fd_config_t * config ) {
     266           0 :   fd_memcpy( config->net.provider, "mlx5", 5 );
     267           0 : }
     268             : 
     269             : /* Each feature's supported Linux version matrix per driver decided
     270             :    based on fd-linux-version-testbed test results. */
     271             : static const fd_auto_provider_t NET_PROVIDERS[] = {
     272             :   {
     273             :     .name = "mlx5",
     274             :     .supported_drivers = { { "mlx5_core", 5, 14, 0, 0 } },
     275             :     .check = mlx5_check,
     276             :     .apply = mlx5_apply
     277             :   },
     278             :   {
     279             :     .name  = "xdp",
     280             :     .check = xdp_check,
     281             :     .apply = xdp_apply,
     282             :     .feats = {
     283             :       {
     284             :         .name              = "Listen GRE",
     285             :         .is_feat_auto      = is_xdp_listen_gre_auto,
     286             :         .check             = xdp_listen_gre_check,
     287             :         .apply             = xdp_listen_gre_apply
     288             :       },
     289             :       {
     290             :         .name              = "RSS Queue Mode",
     291             :         .is_feat_auto      = is_xdp_rss_queue_mode_auto,
     292             :         .check             = xdp_rss_queue_mode_check,
     293             :         .apply             = xdp_rss_queue_mode_apply
     294             :       },
     295             :       {
     296             :         .name              = "Native Bond",
     297             :         .supported_drivers = {
     298             :           { "mlx5_core", 5, 15, 0, 0 },
     299             :           { "i40e",      5, 15, 0, 0 },
     300             :         },
     301             :         .is_feat_auto      = is_xdp_native_bond_auto,
     302             :         .check             = xdp_native_bond_check,
     303             :         .apply             = xdp_native_bond_apply
     304             :       },
     305             :       {
     306             :         .name              = "DRV",
     307             :         .supported_drivers = {
     308             :           { "mlx5_core", 5, 15, 6, 3 },
     309             :           { "mlx5_core", 6,  5, 0, 0 },
     310             :           { "i40e",      5, 19, 0, 0 }
     311             :         },
     312             :         .is_feat_auto      = is_xdp_mode_auto,
     313             :         .check             = xdp_drv_check,
     314             :         .apply             = xdp_drv_apply,
     315             :       },
     316             :       {
     317             :         .name              = "Prefbusy",
     318             :         .supported_drivers = {
     319             :           { "mlx5_core", 5, 15, 0, 0 },
     320             :           { "i40e",      5, 15, 0, 0 }
     321             :         },
     322             :         .is_feat_auto      = is_xdp_prefbusy_auto,
     323             :         .check             = xdp_prefbusy_check,
     324             :         .apply             = xdp_prefbusy_apply
     325             :       },
     326             :       {
     327             :         .name              = "Zero Copy",
     328             :         .supported_drivers = {
     329             :           { "mlx5_core", 6, 1, 0, 0 }
     330             :         },
     331             :         .is_feat_auto      = is_xdp_zc_auto,
     332             :         .check             = xdp_zc_check,
     333             :         .apply             = xdp_zc_apply
     334             :       }
     335             :     }
     336             :   }
     337             : };
     338             : 
     339             : static void
     340           0 : scrape_system( fd_auto_info_t * info ) {
     341           0 :   struct utsname utsname;
     342           0 :   if( FD_UNLIKELY( -1==uname( &utsname ) ) ) {
     343           0 :     FD_LOG_WARNING(( "uname() failed (%i-%s), auto config skipping kernel version detection",
     344           0 :                      errno, fd_io_strerror( errno ) ));
     345           0 :     return;
     346           0 :   }
     347             : 
     348             :   /* utsname.release is typically "major.minor.patch-distrojunk",
     349             :      e.g. "6.8.0-45-generic" or "5.14.0-362.8.1.el9_3.x86_64".  We
     350             :      only care about major.minor.  A missing ".minor" is treated as
     351             :      minor 0.  An unparseable release leaves {0,0}, which fails all
     352             :      version requirements (fail-soft). */
     353             : 
     354           0 :   char const * cur = utsname.release;
     355           0 :   char *       end = NULL;
     356             : 
     357           0 :   ulong major = strtoul( cur, &end, 10 );
     358           0 :   if( FD_UNLIKELY( end==cur ) ) {
     359           0 :     FD_LOG_WARNING(( "unrecognized kernel release `%s`, auto config skipping kernel version detection",
     360           0 :                      utsname.release ));
     361           0 :     return;
     362           0 :   }
     363             : 
     364           0 :   ulong minor = 0UL;
     365           0 :   if( FD_LIKELY( end[0]=='.' ) ) {
     366           0 :     cur   = end+1;
     367           0 :     minor = strtoul( cur, &end, 10 );
     368           0 :     if( FD_UNLIKELY( end==cur ) ) minor = 0UL; /* tolerate "6." */
     369           0 :   }
     370             : 
     371           0 :   info->linux_major = major;
     372           0 :   info->linux_minor = minor;
     373           0 : }
     374             : 
     375             : static int
     376           0 : scrape_is_using_gre( void ) {
     377           0 :   fd_netlink_t netlink[1];
     378           0 :   if( FD_UNLIKELY( !fd_netlink_init( netlink, 42U ) ) ) return 1;
     379             : 
     380           0 :   ulong  netdev_tbl_footprint = fd_netdev_tbl_footprint( NETDEV_MAX, BOND_MASTER_MAX );
     381           0 :   void * netdev_tbl_mem       = aligned_alloc( FD_NETDEV_TBL_ALIGN, netdev_tbl_footprint );
     382           0 :   if( FD_UNLIKELY( !netdev_tbl_mem ) ) {
     383           0 :     fd_netlink_fini( netlink );
     384           0 :     FD_LOG_WARNING(( "failed to allocate network interface table for GRE auto detection" ));
     385           0 :     return 1;
     386           0 :   }
     387             : 
     388           0 :   fd_netdev_tbl_join_t netdev_tbl[1];
     389           0 :   FD_TEST( fd_netdev_tbl_new( netdev_tbl_mem, NETDEV_MAX, BOND_MASTER_MAX ) );
     390           0 :   FD_TEST( fd_netdev_tbl_join( netdev_tbl, netdev_tbl_mem ) );
     391             : 
     392           0 :   int err = fd_netdev_netlink_load_table( netdev_tbl, netlink );
     393           0 :   fd_netlink_fini( netlink );
     394           0 :   if( FD_UNLIKELY( err ) ) {
     395           0 :     FD_LOG_WARNING(( "failed to load network interfaces for GRE auto detection (%i-%s)",
     396           0 :                      err, fd_io_strerror( err ) ));
     397           0 :     free( netdev_tbl_mem );
     398           0 :     return 1;
     399           0 :   }
     400             : 
     401           0 :   int is_using_gre = 0;
     402           0 :   for( ushort i=0U; i<netdev_tbl->hdr->dev_cnt; i++ ) {
     403           0 :     if( netdev_tbl->dev_tbl[ i ].dev_type==ARPHRD_IPGRE ) {
     404           0 :       is_using_gre = 1;
     405           0 :       break;
     406           0 :     }
     407           0 :   }
     408           0 :   free( netdev_tbl_mem );
     409           0 :   return is_using_gre;
     410           0 : }
     411             : 
     412             : static void
     413             : scrape_networking( fd_auto_info_t * info,
     414           0 :                    char const     * if_name ) {
     415           0 :   info->is_using_gre = scrape_is_using_gre();
     416             : 
     417             :   /* Check for virtual interface */
     418           0 :   char path[ PATH_MAX ];
     419           0 :   FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/device", if_name ) );
     420           0 :   info->is_virtual_if = ( -1==access( path, F_OK ) );
     421             : 
     422             :   /* Check for bond */
     423           0 :   info->is_bonded_if = !!fd_bonding_is_master( if_name );
     424           0 :   if( info->is_bonded_if ) {
     425           0 :     info->bonded_if_slave_count = (uint)fd_bonding_slave_cnt( if_name );
     426           0 :     info->is_lacp_if            = !!fd_bonding_is_lacp( if_name );
     427           0 :   }
     428             : 
     429           0 :   fd_net_get_driver( info->driver, sizeof(info->driver), if_name );
     430             : 
     431           0 :   if( !strcmp( info->driver, "mlx5_core" ) ) {
     432           0 :     char rdma_name[ FD_MLX5_RDMA_NAME_MAX ];
     433           0 :     uint rdma_port;
     434           0 :     info->has_mlx5_rdma_port = fd_mlx5_rdma_dev_find( rdma_name, &rdma_port, if_name );
     435           0 :     info->has_uverbs         = fd_mlx5_uverbs_avail();
     436           0 :   }
     437           0 : }
     438             : 
     439             : static fd_auto_info_t
     440           0 : fd_auto_scrape_info( fd_config_t const * config ) {
     441           0 :   fd_auto_info_t info = {0};
     442             : 
     443           0 :   scrape_system    ( &info                        );
     444           0 :   scrape_networking( &info, config->net.interface );
     445             : 
     446           0 :   if( !strcmp( config->net.provider, "auto" ) &&
     447           0 :       info.has_mlx5_rdma_port                 &&
     448           0 :       !info.has_uverbs ) {
     449           0 :     info.has_uverbs_module = !fd_mlx5_uverbs_modprobe( 1 );
     450           0 :   }
     451             : 
     452           0 :   return info;
     453           0 : }
     454             : 
     455             : /* fd_auto_net_apply resolves any "auto" fields in the networking
     456             : configuration. The resulting configuration is optimized for the system
     457             : but won't always maximally optimize, this is to reduce risk of failures. */
     458             : static void
     459             : fd_auto_net_apply( fd_config_t          * config,
     460           0 :                    fd_auto_info_t const * info ) {
     461             :   /* Providers are in order of priority with first being the highest */
     462           0 :   int const is_provider_auto = !strcmp( config->net.provider, "auto" );
     463           0 :   int is_provider_set = 0;
     464           0 :   for( ulong p=0UL; p<PROVIDER_CNT; p++ ) {
     465           0 :     fd_auto_provider_t const * provider = &NET_PROVIDERS[p];
     466             : 
     467             :     /* Check provider requirements */
     468           0 :     int const is_explicit_provider = !is_provider_auto && !strcmp( config->net.provider, provider->name );
     469           0 :     int is_chosen_provider = !is_provider_set &&
     470           0 :                              ( is_explicit_provider ||
     471           0 :                                ( is_provider_auto &&
     472           0 :                                  ( !provider->check || provider->check( config, info ) ) &&
     473           0 :                                  fd_auto_check_driver( info, provider->supported_drivers ) ) );
     474           0 :     if( is_chosen_provider ) {
     475           0 :       is_provider_set = 1;
     476           0 :       if( is_provider_auto ) {
     477           0 :         if( FD_UNLIKELY( !provider->apply ) ) {
     478           0 :           FD_LOG_ERR(( "Applying auto configure net provider %s failed since no apply function was found.", provider->name ));
     479           0 :         }
     480           0 :         provider->apply( config );
     481           0 :       }
     482           0 :     }
     483             : 
     484           0 :     for( ulong f=0UL; f<FEAT_CNT_MAX; f++ ) {
     485           0 :       fd_auto_feat_t const * feat = &provider->feats[f];
     486           0 :       if( !feat->name ) break;
     487             : 
     488             :       /* Checks feature is set to "auto", if so replaces "auto" with
     489             :          default and goes onto the next checks */
     490           0 :       if( !feat->is_feat_auto( config ) ) continue;
     491             : 
     492           0 :       if( !is_chosen_provider ) continue;
     493             : 
     494             :       /* Feature's driver/Linux version requirements */
     495           0 :       if( !fd_auto_check_driver( info, feat->supported_drivers ) ) continue;
     496             : 
     497             :       /* Feature's other requirements */
     498           0 :       if( feat->check && !feat->check( config, info ) ) continue;
     499             : 
     500           0 :       if( FD_UNLIKELY( !feat->apply ) ) {
     501           0 :         FD_LOG_ERR(( "Applying auto configure net feature %s failed since no apply function was found.", feat->name ));
     502           0 :       }
     503           0 :       feat->apply( config );
     504           0 :     }
     505           0 :   }
     506           0 : }
     507             : 
     508             : void
     509           0 : fd_auto_net( fd_config_t * config ) {
     510           0 :   fd_auto_info_t info = fd_auto_scrape_info( config );
     511           0 :   fd_auto_net_apply( config, &info );
     512             : 
     513           0 :   fd_cstr_printf( config->auto_config_log, sizeof(config->auto_config_log), NULL,
     514           0 :       "network auto configure system info: provider=%s xdp_mode=%s poll_mode=%s zero_copy=%d native_bond=%d listen_gre=%d rss_queue_mode=%s (driver=%s kernel=%lu.%lu gre=%d virtual_if=%d bonded_if=%d lacp_if=%d slaves=%u, net_tile_cnt=%u)",
     515           0 :       config->net.provider,
     516           0 :       config->net.xdp.xdp_mode,
     517           0 :       config->net.xdp.poll_mode,
     518           0 :       config->net.xdp.xdp_zero_copy,
     519           0 :       config->net.xdp.native_bond,
     520           0 :       config->net.xdp.listen_gre,
     521           0 :       config->net.xdp.rss_queue_mode,
     522           0 :       info.driver[0] ? info.driver : "unknown",
     523           0 :       info.linux_major, info.linux_minor,
     524           0 :       info.is_using_gre,
     525           0 :       info.is_virtual_if,
     526           0 :       info.is_bonded_if,
     527           0 :       info.is_lacp_if,
     528           0 :       info.bonded_if_slave_count,
     529           0 :       config->layout.net_tile_count );
     530           0 : }

Generated by: LCOV version 1.14