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

Generated by: LCOV version 1.14