LCOV - code coverage report
Current view: top level - app/shared/commands/configure - ethtool-channels.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 327 0.0 %
Date: 2026-06-19 09:21:35 Functions: 0 11 0.0 %

          Line data    Source code
       1             : #include "configure.h"
       2             : 
       3             : #include <errno.h>
       4             : #include <unistd.h>
       5             : 
       6             : #include "fd_ethtool_ioctl.h"
       7             : #include "../../../../disco/net/fd_linux_bond.h"
       8             : 
       9           0 : #define NAME "ethtool-channels"
      10             : 
      11             : static int fini_device( char const * device );
      12             : 
      13             : static int
      14           0 : enabled( fd_config_t const * config ) {
      15             : 
      16             :   /* only enable if network stack is XDP */
      17           0 :   if( 0!=strcmp( config->net.provider, "xdp" ) ) return 0;
      18             : 
      19           0 :   return 1;
      20           0 : }
      21             : 
      22             : static void
      23             : init_perm( fd_cap_chk_t *      chk,
      24           0 :            fd_config_t const * config FD_PARAM_UNUSED ) {
      25           0 :   fd_cap_chk_root( chk, NAME, "modify network device configuration with ethtool" );
      26           0 : }
      27             : 
      28             : static void
      29             : fini_perm( fd_cap_chk_t *      chk,
      30           0 :            fd_config_t const * config FD_PARAM_UNUSED ) {
      31           0 :   fd_cap_chk_root( chk, NAME, "modify network device configuration with ethtool" );
      32           0 : }
      33             : 
      34             : /* FIXME: Centrally define listen port list to avoid this configure
      35             :    stage from going out of sync with port mappings. */
      36             : static uint
      37             : get_ports( fd_config_t const * config,
      38           0 :            ushort *            ports ) {
      39           0 :   uint port_cnt = 0U;
      40             : 
      41           0 : #define ADD_PORT( p ) do { \
      42           0 :   ushort __port = ( p ); \
      43           0 :   if( FD_UNLIKELY( __port==0U ) ) break; \
      44           0 :   int __dupe = 0; \
      45           0 :   for( uint __p=0U; !__dupe && __p<port_cnt; ++__p ) __dupe = (ports[ __p ]==__port); \
      46           0 :   if( FD_UNLIKELY( __dupe ) ) break; \
      47           0 :   ports[ port_cnt ] = __port; \
      48           0 :   port_cnt++; \
      49           0 : } while(0)
      50             : 
      51           0 :   ADD_PORT( config->tiles.shred.shred_listen_port              );
      52           0 :   ADD_PORT( config->tiles.quic.quic_transaction_listen_port    );
      53           0 :   ADD_PORT( config->tiles.quic.regular_transaction_listen_port );
      54           0 :   if( config->is_firedancer ) {
      55           0 :     ADD_PORT( config->gossip.port                              );
      56           0 :     ADD_PORT( config->tiles.repair.repair_client_listen_port   );
      57           0 :     ADD_PORT( config->tiles.rserve.repair_serve_listen_port    );
      58           0 :     ADD_PORT( config->tiles.txsend.txsend_src_port             );
      59           0 :   }
      60           0 : #undef ADD_PORT
      61             : 
      62           0 :   return port_cnt;
      63           0 : }
      64             : 
      65             : /* Attempts to initialize the device in simple or dedicated mode.  If
      66             :    strict is true, FD_LOG_ERR's on failure.  Otherwise, returns 1 on
      67             :    failure. Returns 0 on success. */
      68             : static int
      69             : init_device( char const *        device,
      70             :              fd_config_t const * config,
      71             :              int                 dedicated_mode,
      72             :              int                 listen_gre,
      73             :              int                 strict,
      74           0 :              uint                device_cnt ) {
      75           0 :   FD_TEST( dedicated_mode || strict );
      76             : 
      77           0 :   uint const net_tile_cnt = config->layout.net_tile_count;
      78           0 :   if( FD_UNLIKELY( net_tile_cnt%device_cnt!=0 ) ) {
      79           0 :     FD_LOG_ERR(( "net tile count %u must be a multiple of the number of slave devices %u (incompatible settings [layout.net_tile_count] and [net.xdp.native_bond])", net_tile_cnt, device_cnt ));
      80           0 :   }
      81           0 :   uint const queue_cnt = net_tile_cnt / device_cnt;
      82             : 
      83           0 :   fd_ethtool_ioctl_t ioc __attribute__((cleanup(fd_ethtool_ioctl_fini)));
      84           0 :   if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
      85           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
      86             : 
      87             :   /* This should happen first, otherwise changing the number of channels may fail */
      88           0 :   FD_TEST( 0==fd_ethtool_ioctl_rxfh_set_default( &ioc ) );
      89             : 
      90           0 :   uint const num_channels = !dedicated_mode ? queue_cnt : 0 /* maximum allowed */;
      91           0 :   int ret = fd_ethtool_ioctl_channels_set_num( &ioc, num_channels );
      92           0 :   if( FD_UNLIKELY( 0!=ret ) ) {
      93           0 :     if( strict ) {
      94           0 :       if( FD_LIKELY( ret == EBUSY ) )
      95           0 :         FD_LOG_ERR(( "error configuring network device (%s), failed to set number of channels. "
      96           0 :                      "This is most commonly caused by an issue with the Intel ice driver on certain versions "
      97           0 :                      "of Ubuntu.  If you are using the ice driver, `sudo dmesg | grep %s` contains "
      98           0 :                      "messages about RDMA, and you do not need RDMA, try running `rmmod irdma` and/or "
      99           0 :                      "blacklisting the irdma kernel module.", device, device ));
     100           0 :       else
     101           0 :         FD_LOG_ERR(( "error configuring network device (%s), failed to set number of channels", device ));
     102           0 :     }
     103           0 :     return 1;
     104           0 :   }
     105             : 
     106             :   /* Some drivers (e.g. igb) put the RXFH table into an incorrect state
     107             :      after changing the channel count.  So in simple mode we reset it
     108             :      to the default again. */
     109           0 :   if( !dedicated_mode ) {
     110           0 :     FD_TEST( 0==fd_ethtool_ioctl_rxfh_set_default( &ioc ) );
     111             : 
     112             :     /* Configure the NIC to include UDP source and destination ports in
     113             :        the RSS hash for UDP/IPv4 flows.  Without this, most NICs default
     114             :        to hashing only on IP addresses, which causes severe RX queue
     115             :        imbalance when all traffic targets the same destination IP and
     116             :        port (e.g. gossip).  Not needed in dedicated mode since ntuple
     117             :        rules bypass RSS for Firedancer traffic. */
     118           0 :     if( FD_UNLIKELY( 0!=fd_ethtool_ioctl_rxfh_set_flow_hash_udp4( &ioc ) ) )
     119           0 :       FD_LOG_WARNING(( "failed to set UDP flow hash on device %s, RSS distribution may be poor", device ));
     120           0 :   }
     121             : 
     122           0 :   FD_TEST( 0==fd_ethtool_ioctl_ntuple_clear( &ioc ) );
     123             : 
     124           0 :   if( dedicated_mode ) {
     125             :     /* Some drivers (e.g. ixgbe) reset the RXFH table upon activation
     126             :        of the ntuple feature, so we do this first. */
     127           0 :     if( FD_UNLIKELY( 0!=fd_ethtool_ioctl_feature_set( &ioc, FD_ETHTOOL_FEATURE_NTUPLE, 1 ) ) ) {
     128           0 :       if( strict ) FD_LOG_ERR(( "error configuring network device (%s), failed to enable ntuple feature. Try `net.xdp.rss_queue_mode=\"simple\"`", device ));
     129           0 :       else         return 1;
     130           0 :     }
     131             : 
     132             :     /* Remove a queue from the rxfh table for each net tile. */
     133           0 :     uint rxfh_queue_cnt;
     134           0 :     FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_queue_cnt( &ioc, &rxfh_queue_cnt ) );
     135           0 :     if( FD_UNLIKELY( queue_cnt>=rxfh_queue_cnt ) ) {
     136           0 :       if( strict ) FD_LOG_ERR(( "error configuring network device (%s), too many net tiles %u for queue count %u.  "
     137           0 :                                 "Try `net.xdp.rss_queue_mode=\"simple\"` or reduce net tile count",
     138           0 :                                 device, net_tile_cnt, rxfh_queue_cnt ));
     139           0 :       else         return 1;
     140           0 :     }
     141           0 :     if( FD_UNLIKELY( 0!=fd_ethtool_ioctl_rxfh_set_suffix( &ioc, queue_cnt ) ) ) {
     142           0 :       if( strict ) FD_LOG_ERR(( "error configuring network device (%s), failed to isolate queues. Try `net.xdp.rss_queue_mode=\"simple\"`", device ));
     143           0 :       else         return 1;
     144           0 :     }
     145             : 
     146             :     /* Add a ntuple rule for each listening destination port.  If there
     147             :        are multiple net tiles, create a group of rules for each tile. */
     148           0 :     int ntuple_error = 0;
     149           0 :     ushort ports[ 32 ];
     150           0 :     uint port_cnt = get_ports( config, ports );
     151           0 :     uint rule_idx = 0;
     152           0 :     uint const rule_group_cnt = fd_uint_pow2_up( queue_cnt );
     153           0 :     for( uint r=0U; !ntuple_error && r<rule_group_cnt; r++ ) {
     154           0 :       for( uint p=0U; !ntuple_error && p<port_cnt; p++ ) {
     155           0 :         ntuple_error = 0!=fd_ethtool_ioctl_ntuple_set_udp_dport( &ioc, rule_idx++, ports[ p ], r, rule_group_cnt, r%queue_cnt );
     156           0 :       }
     157           0 :     }
     158           0 :     if( FD_UNLIKELY( ntuple_error ) ) {
     159           0 :       if( strict ) FD_LOG_ERR(( "error configuring network device (%s), failed to install ntuple rules.  "
     160           0 :                                 "Try `net.xdp.rss_queue_mode=\"simple\"` or `layout.net_tile_count=1`", device ));
     161           0 :       else         return 1;
     162           0 :     }
     163             : 
     164             :     /* It's rather unfortunate, but given the APIs that ethtool exposes,
     165             :        we can't do much better than sending all GRE packets to a single
     166             :        queue.  At least more recent Mellanox NICs certainly do support
     167             :        RSS based on the inner header, but it's not possible to configure
     168             :        this in a generic way. */
     169           0 :     int gre_ntuple_error = 0;
     170           0 :     if( listen_gre ) gre_ntuple_error = fd_ethtool_ioctl_ntuple_set_gre( &ioc, rule_idx++, 0U );
     171           0 :     if( FD_UNLIKELY( gre_ntuple_error ) ) {
     172           0 :       FD_LOG_ERR(( "error configuring network device (%s), failed to install ntuple rule "
     173           0 :                    "to route GRE packets to Firedancer.  If you require GRE-wrapped "
     174           0 :                    "traffic (e.g. with DoubleZero), set `net.xdp.rss_queue_mode=\"simple\"`.  "
     175           0 :                    "Otherwise, set `net.xdp.listen_gre=false`.", device ));
     176           0 :     }
     177           0 :   }
     178             : 
     179           0 :   return 0;
     180           0 : }
     181             : 
     182             : static void
     183           0 : init( fd_config_t const * config ) {
     184           0 :   int only_dedicated =
     185           0 :     (0==strcmp( config->net.xdp.rss_queue_mode, "dedicated" ));
     186           0 :   int try_dedicated = only_dedicated ||
     187           0 :     (0==strcmp( config->net.xdp.rss_queue_mode, "auto" ) );
     188             : 
     189             :   /* if using a bonded device, we need to set channels on the
     190             :      underlying devices. */
     191           0 :   int  is_bonded  = fd_bonding_is_master( config->net.interface );
     192           0 :   uint device_cnt = 1U;
     193           0 :   if( is_bonded && config->net.xdp.native_bond ) {
     194           0 :     device_cnt = fd_bonding_slave_cnt( config->net.interface );
     195           0 :   }
     196             : 
     197           0 :   int listen_gre = config->net.xdp.listen_gre;
     198             : 
     199             :   /* If the mode was auto, we will try to init in dedicated mode but will
     200             :      not fail the stage if this is not successful.  If the mode was
     201             :      dedicated, we will require success. */
     202           0 :   if( try_dedicated ) {
     203           0 :     int failed = 0;
     204           0 :     if( is_bonded ) {
     205           0 :       fd_bonding_slave_iter_t iter_[1];
     206           0 :       fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     207           0 :       for( ; !failed && !fd_bonding_slave_iter_done( iter );
     208           0 :           fd_bonding_slave_iter_next( iter ) ) {
     209           0 :         failed = init_device( fd_bonding_slave_iter_ele( iter ), config, 1, listen_gre, only_dedicated, device_cnt );
     210           0 :       }
     211           0 :     } else {
     212           0 :       failed = init_device( config->net.interface, config, 1, listen_gre, only_dedicated, device_cnt );
     213           0 :     }
     214           0 :     if( !failed ) return;
     215           0 :     FD_TEST( !only_dedicated );
     216           0 :     FD_LOG_WARNING(( "error configuring network device (%s), rss_queue_mode \"auto\" attempted"
     217           0 :                      " \"dedicated\" configuration but falling back to \"simple\".", config->net.interface ));
     218             :     /* Wipe partial dedicated configuration before simple init */
     219           0 :     if( is_bonded ) {
     220           0 :       fd_bonding_slave_iter_t iter_[1];
     221           0 :       fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     222           0 :       for( ; !fd_bonding_slave_iter_done( iter );
     223           0 :           fd_bonding_slave_iter_next( iter ) ) {
     224           0 :         fini_device( fd_bonding_slave_iter_ele( iter ) );
     225           0 :       }
     226           0 :     }
     227           0 :     else {
     228           0 :       fini_device( config->net.interface );
     229           0 :     }
     230           0 :   }
     231             : 
     232             :   /* Require success for simple mode, either configured or as fallback */
     233           0 :   if( is_bonded ) {
     234           0 :     fd_bonding_slave_iter_t iter_[1];
     235           0 :     fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     236           0 :     for( ; !fd_bonding_slave_iter_done( iter );
     237           0 :         fd_bonding_slave_iter_next( iter ) ) {
     238           0 :       init_device( fd_bonding_slave_iter_ele( iter ), config, 0, listen_gre, 1, device_cnt );
     239           0 :     }
     240           0 :   } else {
     241           0 :     init_device( config->net.interface, config, 0, listen_gre, 1, device_cnt );
     242           0 :   }
     243           0 : }
     244             : 
     245             : /* Returns whether anything is changed from the default (fini'd) state */
     246             : static int
     247           0 : check_device_is_modified( char const * device ) {
     248           0 :   fd_ethtool_ioctl_t ioc __attribute__((cleanup(fd_ethtool_ioctl_fini)));
     249           0 :   if( FD_UNLIKELY( &ioc!=fd_ethtool_ioctl_init( &ioc, device ) ) )
     250           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
     251             : 
     252           0 :   fd_ethtool_ioctl_channels_t channels;
     253           0 :   FD_TEST( 0==fd_ethtool_ioctl_channels_get_num( &ioc, &channels ) );
     254           0 :   if( channels.current!=channels.max ) return 1;
     255             : 
     256           0 :   uint rxfh_queue_cnt;
     257           0 :   FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_queue_cnt( &ioc, &rxfh_queue_cnt ) );
     258             : 
     259           0 :   uint rxfh_table[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
     260           0 :   uint rxfh_table_ele_cnt;
     261           0 :   FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table, &rxfh_table_ele_cnt ) );
     262           0 :   for( uint j=0U, q=0U; j<rxfh_table_ele_cnt; j++) {
     263           0 :     if( rxfh_table[ j ]!=q++ ) return 1;
     264           0 :     if( q>=rxfh_queue_cnt ) q = 0;
     265           0 :   }
     266             : 
     267           0 :   int ntuple_rules_empty;
     268           0 :   FD_TEST( 0==fd_ethtool_ioctl_ntuple_validate( &ioc, NULL, 0, 0, UINT_MAX, &ntuple_rules_empty ) );
     269           0 :   if( !ntuple_rules_empty ) return 1;
     270             : 
     271           0 :   return 0;
     272           0 : }
     273             : 
     274             : static int
     275             : check_device_is_configured( char const *        device,
     276             :                             fd_config_t const * config,
     277             :                             int                 dedicated_mode,
     278           0 :                             uint                device_cnt ) {
     279           0 :   uint const net_tile_cnt = config->layout.net_tile_count;
     280           0 :   if( FD_UNLIKELY( net_tile_cnt%device_cnt!=0 ) ) {
     281           0 :     FD_LOG_ERR(( "net tile count %u must be a multiple of the number of slave devices %u (incompatible settings [layout.net_tile_count] and [net.xdp.native_bond])", net_tile_cnt, device_cnt ));
     282           0 :   }
     283           0 :   uint const queue_cnt = net_tile_cnt / device_cnt;
     284             : 
     285           0 :   fd_ethtool_ioctl_t ioc __attribute__((cleanup(fd_ethtool_ioctl_fini)));
     286           0 :   if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
     287           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
     288             : 
     289           0 :   fd_ethtool_ioctl_channels_t channels;
     290           0 :   FD_TEST( 0==fd_ethtool_ioctl_channels_get_num( &ioc, &channels ) );
     291           0 :   if( channels.current!=(dedicated_mode ? channels.max : queue_cnt) ) return 0;
     292             : 
     293           0 :   uint rxfh_queue_cnt;
     294           0 :   FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_queue_cnt( &ioc, &rxfh_queue_cnt ) );
     295           0 :   rxfh_queue_cnt = fd_uint_min( rxfh_queue_cnt, channels.current );
     296             : 
     297           0 :   uint rxfh_table[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
     298           0 :   uint rxfh_table_ele_cnt;
     299           0 :   FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table, &rxfh_table_ele_cnt ) );
     300           0 :   int rxfh_error = (dedicated_mode && 0U==rxfh_table_ele_cnt);
     301           0 :   uint const start_queue = dedicated_mode ? queue_cnt : 0U;
     302           0 :   for( uint j=0U, q=start_queue; !rxfh_error && j<rxfh_table_ele_cnt; j++) {
     303           0 :     rxfh_error = (rxfh_table[ j ]!=q++);
     304           0 :     if( FD_UNLIKELY( q>=rxfh_queue_cnt ) ) q = start_queue;
     305           0 :   }
     306           0 :   if( rxfh_error ) return 0;
     307             : 
     308           0 :   if( dedicated_mode ) {
     309           0 :     int ntuple_feature_active;
     310           0 :     FD_TEST( 0==fd_ethtool_ioctl_feature_test( &ioc, FD_ETHTOOL_FEATURE_NTUPLE, &ntuple_feature_active ) );
     311           0 :     if( !ntuple_feature_active ) return 0;
     312           0 :   }
     313             : 
     314           0 :   if( !dedicated_mode ) {
     315           0 :     int ntuple_rules_empty;
     316           0 :     FD_TEST( 0==fd_ethtool_ioctl_ntuple_validate( &ioc, NULL, 0, 0, UINT_MAX, &ntuple_rules_empty ) );
     317           0 :     if( !ntuple_rules_empty ) return 0;
     318           0 :   } else {
     319           0 :     int ports_valid;
     320           0 :     ushort ports[ 32 ];
     321           0 :     uint port_cnt = get_ports( config, ports );
     322           0 :     int listen_gre = config->net.xdp.listen_gre;
     323           0 :     FD_TEST( 0==fd_ethtool_ioctl_ntuple_validate( &ioc, ports, port_cnt, queue_cnt, listen_gre ? 0U : UINT_MAX, &ports_valid ));
     324           0 :     if( !ports_valid ) return 0;
     325           0 :   }
     326             : 
     327           0 :   return 1;
     328           0 : }
     329             : 
     330             : static configure_result_t
     331             : check( fd_config_t const * config,
     332           0 :        int                 check_type FD_PARAM_UNUSED ) {
     333           0 :   int only_dedicated =
     334           0 :     (0==strcmp( config->net.xdp.rss_queue_mode, "dedicated" ));
     335           0 :   int check_dedicated = only_dedicated ||
     336           0 :     (0==strcmp( config->net.xdp.rss_queue_mode, "auto" ));
     337             : 
     338           0 :   int  is_bonded  = fd_bonding_is_master( config->net.interface );
     339           0 :   uint device_cnt = 1U;
     340           0 :   if( is_bonded && config->net.xdp.native_bond ) {
     341           0 :     device_cnt = fd_bonding_slave_cnt( config->net.interface );
     342           0 :   }
     343             : 
     344           0 :   if( check_dedicated ) {
     345           0 :     int is_configured = 1;
     346           0 :     if( is_bonded ) {
     347           0 :       fd_bonding_slave_iter_t iter_[1];
     348           0 :       fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     349           0 :       for( ; is_configured && !fd_bonding_slave_iter_done( iter );
     350           0 :           fd_bonding_slave_iter_next( iter ) ) {
     351           0 :         is_configured = check_device_is_configured( fd_bonding_slave_iter_ele( iter ), config, 1, device_cnt );
     352           0 :       }
     353           0 :     } else {
     354           0 :       is_configured = check_device_is_configured( config->net.interface, config, 1, device_cnt );
     355           0 :     }
     356           0 :     if( is_configured ) CONFIGURE_OK();
     357           0 :   }
     358             : 
     359           0 :   if( !only_dedicated ) {
     360           0 :     int is_configured = 1;
     361           0 :     if( is_bonded ) {
     362           0 :       fd_bonding_slave_iter_t iter_[1];
     363           0 :       fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     364           0 :       for( ; is_configured && !fd_bonding_slave_iter_done( iter );
     365           0 :           fd_bonding_slave_iter_next( iter ) ) {
     366           0 :         is_configured = check_device_is_configured( fd_bonding_slave_iter_ele( iter ), config, 0, device_cnt );
     367           0 :       }
     368           0 :     } else {
     369           0 :       is_configured = check_device_is_configured( config->net.interface, config, 0, device_cnt );
     370           0 :     }
     371           0 :     if( is_configured ) CONFIGURE_OK();
     372           0 :   }
     373             : 
     374           0 :   int is_modified = 0;
     375           0 :   if( is_bonded ) {
     376           0 :     fd_bonding_slave_iter_t iter_[1];
     377           0 :     fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     378           0 :     for( ; !is_modified && !fd_bonding_slave_iter_done( iter );
     379           0 :         fd_bonding_slave_iter_next( iter ) ) {
     380           0 :       is_modified = check_device_is_modified( fd_bonding_slave_iter_ele( iter ) );
     381           0 :     }
     382           0 :   } else {
     383           0 :     is_modified = check_device_is_modified( config->net.interface );
     384           0 :   }
     385           0 :   if( is_modified )
     386           0 :     PARTIALLY_CONFIGURED( "device `%s` has partial ethtool-channels network configuration", config->net.interface );
     387             : 
     388           0 :   NOT_CONFIGURED( "device `%s` missing ethtool-channels network configuration", config->net.interface );
     389           0 : }
     390             : 
     391             : static int
     392           0 : fini_device( char const * device ) {
     393           0 :   int error = 0;
     394             : 
     395           0 :   fd_ethtool_ioctl_t ioc;
     396           0 :   if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
     397           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
     398             : 
     399             :   /* It may be the case for certain devices that the default state is
     400             :      the same as the init'd state (in simple mode).  In this case the
     401             :      following fini commands will all be noops, which is fine.  But we
     402             :      need to return 0 so that the configure stage logic does not
     403             :      consider this to be an error.  We compare the state before and
     404             :      after to see if anything was changed by fini. */
     405           0 :   fd_ethtool_ioctl_channels_t channels_orig;
     406           0 :   error |= (0!=fd_ethtool_ioctl_channels_get_num( &ioc, &channels_orig ));
     407           0 :   uint rxfh_table_orig[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
     408           0 :   uint rxfh_table_orig_ele_cnt;
     409           0 :   error |= (0!=fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table_orig, &rxfh_table_orig_ele_cnt ));
     410           0 :   int ntuple_rules_empty_orig;
     411           0 :   error |= (0!=fd_ethtool_ioctl_ntuple_validate( &ioc, NULL, 0, 0, UINT_MAX, &ntuple_rules_empty_orig ));
     412           0 :   if( FD_UNLIKELY( error ) )
     413           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to determine initial state", device ));
     414             : 
     415             :   /* We leave the ntuple feature flag as-is in fini */
     416           0 :   error |= (0!=fd_ethtool_ioctl_ntuple_clear( &ioc ));
     417             : 
     418             :   /* This should happen first, otherwise changing the number of channels may fail */
     419           0 :   error |= (0!=fd_ethtool_ioctl_rxfh_set_default( &ioc ));
     420             : 
     421           0 :   error |= (0!=fd_ethtool_ioctl_channels_set_num( &ioc, 0 /* max */ ));
     422             : 
     423             :   /* Some drivers (i40e) do not always evenly redistribute the RXFH table
     424             :      when increasing the channel count, so we run this again just in case. */
     425           0 :   error |= (0!=fd_ethtool_ioctl_rxfh_set_default( &ioc ));
     426             : 
     427           0 :   if( FD_UNLIKELY( error ) )
     428           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to set to default state", device ));
     429             : 
     430           0 :   fd_ethtool_ioctl_channels_t channels_new;
     431           0 :   error |= (0!=fd_ethtool_ioctl_channels_get_num( &ioc, &channels_new ));
     432           0 :   uint rxfh_table_new[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
     433           0 :   uint rxfh_table_new_ele_cnt;
     434           0 :   error |= (0!=fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table_new, &rxfh_table_new_ele_cnt ));
     435           0 :   int ntuple_rules_empty_new;
     436           0 :   error |= (0!=fd_ethtool_ioctl_ntuple_validate( &ioc, NULL, 0, 0, UINT_MAX, &ntuple_rules_empty_new ));
     437           0 :   if( FD_UNLIKELY( error ) )
     438           0 :     FD_LOG_ERR(( "error configuring network device (%s), unable to determine final state", device ));
     439             : 
     440           0 :   fd_ethtool_ioctl_fini( &ioc );
     441             : 
     442           0 :   int modified = (0!=memcmp( &channels_orig, &channels_new, sizeof(fd_ethtool_ioctl_channels_t) )) ||
     443           0 :                  (rxfh_table_orig_ele_cnt != rxfh_table_new_ele_cnt) ||
     444           0 :                  (0!=memcmp( rxfh_table_orig, rxfh_table_new, rxfh_table_orig_ele_cnt * sizeof(uint) )) ||
     445           0 :                  (ntuple_rules_empty_orig!=ntuple_rules_empty_new);
     446           0 :   return modified;
     447           0 : }
     448             : 
     449             : static int
     450             : fini( fd_config_t const * config,
     451           0 :       int                 pre_init FD_PARAM_UNUSED ) {
     452           0 :   int done = 0;
     453           0 :   if( FD_UNLIKELY( fd_bonding_is_master( config->net.interface ) ) ) {
     454           0 :     fd_bonding_slave_iter_t iter_[1];
     455           0 :     fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
     456           0 :     for( ; !fd_bonding_slave_iter_done( iter );
     457           0 :          fd_bonding_slave_iter_next( iter ) ) {
     458           0 :       done |= fini_device( fd_bonding_slave_iter_ele( iter ) );
     459           0 :     }
     460           0 :   } else {
     461           0 :     done = fini_device( config->net.interface );
     462           0 :   }
     463           0 :   return done;
     464           0 : }
     465             : 
     466             : configure_stage_t fd_cfg_stage_ethtool_channels = {
     467             :   .name            = NAME,
     468             :   .always_recreate = 0,
     469             :   .enabled         = enabled,
     470             :   .init_perm       = init_perm,
     471             :   .fini_perm       = fini_perm,
     472             :   .init            = init,
     473             :   .fini            = fini,
     474             :   .check           = check,
     475             : };
     476             : 
     477             : #undef NAME

Generated by: LCOV version 1.14