LCOV - code coverage report
Current view: top level - app/shared/commands/configure - sysfs-poll.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 73 0.0 %
Date: 2026-08-06 05:23:51 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /* This stage configures the OS to support effective preferred busy
       2             :    polling, allowing for significantly improved network stack (XDP)
       3             :    reliability under high load if enabled on a well supported driver. */
       4             : 
       5             : #include "configure.h"
       6             : 
       7           0 : #define NAME "sysfs-poll"
       8             : 
       9             : #include "../../../platform/fd_file_util.h"
      10             : #include "../../../../disco/net/fd_net_tile.h"
      11             : #include "../../../../disco/net/fd_linux_bond.h"
      12             : 
      13             : #include <string.h> /* strcmp */
      14             : #include <unistd.h> /* access */
      15             : #include <errno.h>
      16             : #include <net/if.h> /* IF_NAMESIZE */
      17             : #include <linux/capability.h>
      18             : 
      19             : /* Values for NAPI_DEFER_HARD_IRQS and GRO_FLUSH_TIMEOUT
      20             :    chosen based on napibusy configuration tested and shown
      21             :    in https://lwn.net/Articles/997491/ Linux patch cover letter.
      22             :    Chosen over fullbusy since for Firedancer the values of
      23             :    fullbusy are unnecessarily high and could cause some extra
      24             :    latency to regular non-Firedancer traffic. */
      25           0 : #define NAPI_DEFER_HARD_IRQS 100U
      26           0 : #define GRO_FLUSH_TIMEOUT    200000U
      27             : 
      28             : static char const setting_napi_defer_hard_irqs[] = "napi_defer_hard_irqs";
      29             : static char const setting_gro_flush_timeout[]    = "gro_flush_timeout";
      30             : 
      31             : static int
      32           0 : enabled( config_t const * config ) {
      33           0 :   return !strcmp( config->net.provider, "xdp" )
      34           0 :       && !strcmp( config->net.xdp.poll_mode, "prefbusy" );
      35           0 : }
      36             : 
      37             : /* get_interfaces returns the number of underlying interfaces in use,
      38             :    and fills the 'interfaces' parameter with the underlying interfaces'
      39             :    names.  Abstracts handling bonds vs standard interfaces. */
      40             : static ulong
      41             : get_interfaces( char const * device,
      42             :                 int          native_bond,
      43           0 :                 char         interfaces[ FD_NET_BOND_SLAVE_MAX ][ IF_NAMESIZE ] ) {
      44           0 :   ulong num_interfaces = 0UL;
      45             : 
      46           0 :   if( native_bond && fd_bonding_is_master( device ) ) {
      47           0 :     fd_bonding_slave_iter_t iter_[1];
      48           0 :     fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, device );
      49             : 
      50           0 :     for( ; !fd_bonding_slave_iter_done( iter ); fd_bonding_slave_iter_next( iter ) ) {
      51           0 :       if( FD_UNLIKELY( num_interfaces >= FD_NET_BOND_SLAVE_MAX ) ) {
      52           0 :         FD_LOG_ERR(( "bond `%s` has more than %u slaves, this is more than allowed in Firedancer.", device, FD_NET_BOND_SLAVE_MAX ));
      53           0 :       }
      54           0 :       fd_cstr_ncpy( interfaces[ num_interfaces++ ], fd_bonding_slave_iter_ele( iter ), IF_NAMESIZE );
      55           0 :     }
      56             : 
      57           0 :     if( FD_UNLIKELY( !num_interfaces ) ) {
      58           0 :       FD_LOG_ERR(( "bond `%s` has no slave interfaces, must have at least one.", device ));
      59           0 :     }
      60             : 
      61           0 :   } else {
      62           0 :     fd_cstr_ncpy( interfaces[ num_interfaces++ ], device, IF_NAMESIZE );
      63           0 :   }
      64             : 
      65           0 :   return num_interfaces;
      66           0 : }
      67             : 
      68             : static void
      69             : init_perm( fd_cap_chk_t   * chk,
      70           0 :            config_t const * config FD_PARAM_UNUSED ) {
      71           0 :   fd_cap_chk_cap( chk, NAME, CAP_NET_ADMIN, "configure preferred busy polling via `/sys/class/net/*/{napi_defer_hard_irqs, gro_flush_timeout}`" );
      72           0 : }
      73             : 
      74             : static void
      75             : sysfs_net_set( char const * device,
      76             :                char const * setting,
      77             :                ulong        value,
      78           0 :                int          native_bond ) {
      79           0 :   char  interfaces[ FD_NET_BOND_SLAVE_MAX ][ IF_NAMESIZE ];
      80           0 :   ulong num_interfaces = get_interfaces( device, native_bond, interfaces );
      81             : 
      82           0 :   for( ulong i=0UL; i<num_interfaces; i++ ) {
      83           0 :     char path[ PATH_MAX ];
      84           0 :     fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/%s", interfaces[ i ], setting );
      85             : 
      86           0 :     FD_LOG_NOTICE(( "%sRUN: `echo \"%lu\" > %s`%s", fd_log_style_dim(), value, path , fd_log_style_normal() ));
      87           0 :     if( FD_UNLIKELY( -1==fd_file_util_write_uint( path, (uint)value ) ) ) {
      88           0 :       FD_LOG_ERR(( "could not write to `%s` (%i-%s), 'prefbusy' poll mode may not be supported for this network configuration, consider switching back 'poll_mode' within your config to 'softirq' mode.", path, errno, fd_io_strerror( errno ) ));
      89           0 :     }
      90           0 :   }
      91           0 : }
      92             : 
      93             : static void
      94           0 : init( config_t const * config ) {
      95           0 :   sysfs_net_set( config->net.interface, setting_napi_defer_hard_irqs, NAPI_DEFER_HARD_IRQS, config->net.xdp.native_bond );
      96             : 
      97           0 :   sysfs_net_set( config->net.interface, setting_gro_flush_timeout,    GRO_FLUSH_TIMEOUT,    config->net.xdp.native_bond );
      98           0 : }
      99             : 
     100             : static int
     101             : fini( config_t const * config,
     102           0 :       int              pre_init FD_PARAM_UNUSED ) {
     103           0 :   sysfs_net_set( config->net.interface, setting_napi_defer_hard_irqs, 0U, config->net.xdp.native_bond );
     104           0 :   sysfs_net_set( config->net.interface, setting_gro_flush_timeout,    0U, config->net.xdp.native_bond );
     105           0 :   return 1;
     106           0 : }
     107             : 
     108             : static configure_result_t
     109             : check( config_t const * config,
     110           0 :        int              check_type FD_PARAM_UNUSED ) {
     111           0 :   char  interfaces[ FD_NET_BOND_SLAVE_MAX ][ IF_NAMESIZE ];
     112           0 :   ulong num_interfaces = get_interfaces( config->net.interface, config->net.xdp.native_bond, interfaces );
     113             : 
     114           0 :   for( ulong i=0UL; i<num_interfaces; i++ ) {
     115           0 :     char path[ PATH_MAX ];
     116           0 :     uint value;
     117           0 :     fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/%s", interfaces[ i ], setting_napi_defer_hard_irqs );
     118             : 
     119             :     /* Check NAPI_DEFER_HARD_IRQS value set correctly. */
     120             : 
     121           0 :     if( FD_UNLIKELY( -1==fd_file_util_read_uint( path, &value ) ) ) {
     122           0 :       FD_LOG_ERR(( "could not read `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     123           0 :     }
     124             : 
     125           0 :     if( FD_UNLIKELY( NAPI_DEFER_HARD_IRQS != value ) ) {
     126           0 :       NOT_CONFIGURED( "kernel parameter `%s` is set to %u, not the expected value of %u.", path, value, NAPI_DEFER_HARD_IRQS );
     127           0 :     }
     128             : 
     129             :     /* Check GRO_FLUSH_TIMEOUT value set correctly. */
     130             : 
     131           0 :     fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/%s", interfaces[ i ], setting_gro_flush_timeout );
     132             : 
     133           0 :     if( FD_UNLIKELY( -1==fd_file_util_read_uint( path, &value ) ) ) {
     134           0 :       FD_LOG_ERR(( "could not read `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     135           0 :     }
     136             : 
     137           0 :     if( FD_UNLIKELY( GRO_FLUSH_TIMEOUT != value ) ) {
     138           0 :       NOT_CONFIGURED( "kernel parameter `%s` is set to %u, not the expected value of %u.", path, value, GRO_FLUSH_TIMEOUT );
     139           0 :     }
     140           0 :   }
     141             : 
     142           0 :   CONFIGURE_OK();
     143           0 : }
     144             : 
     145             : configure_stage_t fd_cfg_stage_sysfs_poll = {
     146             :   .name      = NAME,
     147             :   .enabled   = enabled,
     148             :   .init_perm = init_perm,
     149             :   .fini_perm = init_perm,
     150             :   .init      = init,
     151             :   .fini      = fini,
     152             :   .check     = check,
     153             : };

Generated by: LCOV version 1.14