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

          Line data    Source code
       1             : /* The irq-affinity stage attempts to minimize the amount of CPU time
       2             :    stolen by device interrupts from Firedancer fixed tiles.
       3             : 
       4             :    Configuring interrupt affinity is an annoying problem, and doing so
       5             :    blindly using a default policy is very annoying.  This wall of text
       6             :    documents why the code was written like it is.
       7             : 
       8             :    The problem is that default Linux system configuration dispatches
       9             :    IRQs to arbitrary CPUs.  Since socket-based networking heavily relies
      10             :    on IRQs, heavy incoming traffic can possibly starve a Firedancer tile
      11             :    from running. Tiles cannot evacuate to other CPUs since they are
      12             :    pinned to one CPU each.
      13             : 
      14             :    The goal is to prevent interrupt requests from being delivered to
      15             :    CPUs that Firedancer tiles are spinning on.  The kernel exposes the
      16             :    /proc/irq/N/smp_affinity API for this purpose.
      17             : 
      18             :    ### isolcpus
      19             : 
      20             :    isolcpus is the ideal way to achieve CPU isolation from interrupts
      21             :    and lots of other undesired activity.  At the time of writing,
      22             :    isolcpus can only be configured at boot as a kernel command line
      23             :    parameter, though.
      24             : 
      25             :    ### procfs smp_affinity
      26             : 
      27             :    On a typical Intel/AMD system (with an x2APIC interrupt controller),
      28             :    the kernel binds an IRQ to one CPU core.  That CPU core is picked out
      29             :    of the smp_affinity mask (unless the mask is impossible to satisfy).
      30             :    This CPU can be found in /proc/irq/N/effective_affinity_list.
      31             :    (Technically, IRQ load balancing can be done dynamically at the
      32             :    hardware level with x2APIC, but this is rare...)
      33             : 
      34             :    The kernel picks the effective CPU index out of smp_affinity using
      35             :    roughly these rules:
      36             :    - ignore offline CPUs
      37             :    - ignore isolated CPUs (isolcpus kernel boot parameter)
      38             :    - ignore CPUs on a different NUMA than the device
      39             :    - pick the CPU with the fewest IRQs (kernel/irq/matrix.c)
      40             : 
      41             :    This load balancing policy is decent but does not take into account
      42             :    how busy different IRQs are.  On an unlucky system startup, one CPU
      43             :    might have multiple busy IRQs while another CPU barely gets any
      44             :    interrupts.
      45             : 
      46             :    ### irqbalance
      47             : 
      48             :    The irqbalance userland daemon was created to achieve better load
      49             :    balancing than the kernel's static mapping.
      50             :    It periodically rewrites all /proc/irq/N/smp_affinity files to
      51             :    dynamically rebalance IRQs, reacting to high CPU usage, thermal
      52             :    events, etc.
      53             : 
      54             :    One can ban irqbalance from using certain CPUs either via a config
      55             :    file or unix domain sockets.  The latter is ephemeral in nature,
      56             :    config written via UDS is auto-reset on restart.
      57             : 
      58             :    ### Firedancer smp_affinity interop
      59             : 
      60             :    Firedancer also manually updates the smp_affinity list.  Some IRQs
      61             :    cannot be removed from a CPU (e.g. hardware timer or NVMe managed
      62             :    interrupts), so Firedancer should ignore them.  Unfortunately, the
      63             :    kernel provides no method to tell which IRQs can be moved.
      64             : 
      65             :    Thus, irq-affinity is implemented as follows:
      66             :    - 'check' (which looks for misconfigured IRQs) writes back the
      67             :      existing smp_affinity mask, if the mask includes tile CPUs.  If
      68             :      this results in a permission error, the interrupt likely cannot be
      69             :      reconfigured.
      70             :    - 'init' does the actual reconfiguration (any CPUs that are not
      71             :      Firedancer tiles are allowed)
      72             :    - 'fini' re-admits fixed tile CPUs into the smp_affinity mask
      73             :      (attempts to keep CPUs excluded that were already excluded before
      74             :      Firedancer)
      75             : 
      76             :    Another quirk is that the kernel leaves the effective CPU of an IRQ
      77             :    unchanged if the smp_affinity mask is narrowed, but the effective CPU
      78             :    stays in the mask.  Due to this, 'check' is truly a no-op (ignoring
      79             :    TOCTOU races), and 'fini' fails to restore effective affinity masks.
      80             : 
      81             :    ### Firedancer irqbalance interop
      82             : 
      83             :    If irqbalance is available, Firedancer uses that unix domain socket
      84             :    API to ban tile CPUs.  Since irqbalance forgets UDS config on restart
      85             :    we would ideally periodically re-apply the config.  Unfortunately,
      86             :    irqbalance creates the UDS socket path on each startup.  Opening
      87             :    arbitrary files does not play well with Firedancer's sandbox.
      88             : 
      89             :    Thus, Firedancer only configures the irqbalance daemon on startup
      90             :    using this configure stage.
      91             : 
      92             :    Manual procfs smp_affinity is the lesser evil, so Firedancer logs a
      93             :    warning if it finds irqbalance.
      94             : 
      95             :    ### Firedancer network stack
      96             : 
      97             :    Firedancer code avoids producing interrupts/softirq where possible,
      98             :    instead opting for busy polling.  But at the time of writing, XDP
      99             :    driver code in the Linux kernel is so botched that preferred busy
     100             :    polling cannot be reliably enabled.
     101             : 
     102             :    Thus, unfortunately, IRQs for Firedancer RX XDP traffic will be
     103             :    handled by remote CPU cores. */
     104             : 
     105             : #define _DEFAULT_SOURCE
     106             : #include "configure.h"
     107             : #include "fd_cpu_isolation.h"
     108             : #include "../../../../util/tile/fd_tile_private.h"
     109             : #include <fcntl.h>
     110             : #include <sys/types.h>
     111             : #include <dirent.h>
     112             : #include <ctype.h>
     113             : #include <errno.h>
     114             : #include <stdlib.h>
     115             : #include <unistd.h>
     116             : 
     117             : /* FD_IRQ_AFFINITY_CHECK_TIGHT, when non-zero, makes check() also flag
     118             :    IRQs whose affinity mask is a strict subset of the allowed CPUs (but
     119             :    that do not overlap any tile CPU).  Such IRQs do not steal time from
     120             :    tiles, and irqbalance routinely narrows IRQs this way, which would
     121             :    make check() never pass while it runs.  Disabled for now. */
     122             : #ifndef FD_IRQ_AFFINITY_CHECK_TIGHT
     123             : #define FD_IRQ_AFFINITY_CHECK_TIGHT 0
     124             : #endif
     125             : 
     126             : /* smp_affinity file format is 4a4a4a4a,fcfcfcfc,...
     127             :    So, 9 bytes per 32 bits ~ 3.56 bits per byte. */
     128             : #define SMP_AFFINITY_STR_LEN (FD_TILE_MAX/3)
     129             : #define MISMATCH_SAMPLE_MAX  (16UL)
     130             : #define MISMATCH_STR_LEN     (128UL)
     131             : #define MISMATCH_TILE_STR_LEN (128UL)
     132             : 
     133             : static char *
     134             : append_ulong_list_sample( char *        buf,
     135             :                           ulong         buf_sz,
     136             :                           ulong const * vals,
     137             :                           ulong         val_cnt,
     138           0 :                           ulong         total_cnt ) {
     139           0 :   char * p = fd_cstr_init( buf );
     140           0 :   for( ulong i=0UL; i<val_cnt; i++ ) {
     141           0 :     if( FD_LIKELY( i ) ) p = fd_cstr_append_char( p, ',' );
     142           0 :     if( FD_UNLIKELY( (ulong)(p-buf)+32UL >= buf_sz ) ) break;
     143           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, vals[ i ], fd_ulong_base10_dig_cnt( vals[ i ] ) );
     144           0 :   }
     145           0 :   if( FD_UNLIKELY( total_cnt>val_cnt && (ulong)(p-buf)+32UL<buf_sz ) ) {
     146           0 :     p = fd_cstr_append_cstr( p, ",+" );
     147           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, total_cnt-val_cnt, fd_ulong_base10_dig_cnt( total_cnt-val_cnt ) );
     148           0 :     p = fd_cstr_append_cstr( p, " more" );
     149           0 :   }
     150           0 :   fd_cstr_fini( p );
     151           0 :   return buf;
     152           0 : }
     153             : 
     154             : static char *
     155             : tile_list_sample( char *            buf,
     156             :                   ulong             buf_sz,
     157             :                   fd_topo_t const * topo,
     158             :                   ulong const *     tile_idxs,
     159             :                   ulong             tile_cnt,
     160           0 :                   ulong             total_cnt ) {
     161           0 :   char * p = fd_cstr_init( buf );
     162           0 :   for( ulong i=0UL; i<tile_cnt; i++ ) {
     163           0 :     fd_topo_tile_t const * tile = &topo->tiles[ tile_idxs[ i ] ];
     164           0 :     if( FD_LIKELY( i ) ) p = fd_cstr_append_char( p, ',' );
     165           0 :     if( FD_UNLIKELY( (ulong)(p-buf)+32UL >= buf_sz ) ) break;
     166           0 :     p = fd_cstr_append_cstr( p, tile->name );
     167           0 :     p = fd_cstr_append_char( p, ':' );
     168           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, tile->kind_id, fd_ulong_base10_dig_cnt( tile->kind_id ) );
     169           0 :   }
     170           0 :   if( FD_UNLIKELY( total_cnt>tile_cnt && (ulong)(p-buf)+32UL<buf_sz ) ) {
     171           0 :     p = fd_cstr_append_cstr( p, ",+" );
     172           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, total_cnt-tile_cnt, fd_ulong_base10_dig_cnt( total_cnt-tile_cnt ) );
     173           0 :     p = fd_cstr_append_cstr( p, " more" );
     174           0 :   }
     175           0 :   fd_cstr_fini( p );
     176           0 :   return buf;
     177           0 : }
     178             : 
     179             : static int
     180           0 : irq_dirent_is_irq( char const * name ) {
     181           0 :   if( FD_UNLIKELY( !name[0] ) ) return 0;
     182           0 :   for( char const * p=name; *p; p++ ) {
     183           0 :     if( FD_UNLIKELY( !isdigit( (uchar)*p ) ) ) return 0;
     184           0 :   }
     185           0 :   return 1;
     186           0 : }
     187             : 
     188             : static int
     189             : read_irq_smp_affinity( char const * irq,
     190           0 :                        fd_cpuset_t * cpuset ) {
     191           0 :   char path[ PATH_MAX ];
     192           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/irq/%s/smp_affinity", irq ) );
     193             : 
     194           0 :   int fd = open( path, O_RDONLY );
     195           0 :   if( FD_UNLIKELY( fd<0 ) ) return 0;
     196             : 
     197           0 :   char affinity[ SMP_AFFINITY_STR_LEN+64UL ];
     198           0 :   long affinity_len = read( fd, affinity, sizeof(affinity)-1UL );
     199           0 :   int err = errno;
     200           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     201           0 :   if( FD_UNLIKELY( affinity_len<0 ) ) {
     202           0 :     errno = err;
     203           0 :     return 0;
     204           0 :   }
     205             : 
     206           0 :   affinity[ affinity_len ] = '\0';
     207           0 :   return fd_cpu_isolation_parse_mask( cpuset, affinity );
     208           0 : }
     209             : 
     210             : static int
     211             : write_irq_smp_affinity( char const *        irq,
     212             :                         fd_cpuset_t const * cpuset,
     213           0 :                         int                 warn ) {
     214           0 :   char path[ PATH_MAX ];
     215           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/irq/%s/smp_affinity", irq ) );
     216             : 
     217           0 :   char affinity[ SMP_AFFINITY_STR_LEN+64UL ];
     218           0 :   fd_cpu_isolation_format_mask( affinity, sizeof(affinity), cpuset );
     219           0 :   ulong affinity_len = strlen( affinity );
     220             : 
     221           0 :   int fd = open( path, O_WRONLY );
     222           0 :   if( FD_UNLIKELY( fd<0 ) ) {
     223           0 :     if( FD_LIKELY( warn ) ) FD_LOG_WARNING(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     224           0 :     return 0;
     225           0 :   }
     226             : 
     227           0 :   int ok = 1;
     228           0 :   if( FD_UNLIKELY( write( fd, affinity, affinity_len )!=(long)affinity_len ) ) {
     229           0 :     int err = errno;
     230           0 :     if( FD_LIKELY( warn ) ) FD_LOG_WARNING(( "write(%s) failed (%i-%s)", path, err, fd_io_strerror( err ) ));
     231           0 :     errno = err;
     232           0 :     ok = 0;
     233           0 :   }
     234             : 
     235           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     236           0 :   return ok;
     237           0 : }
     238             : 
     239             : static void
     240             : update_irq_smp_affinities( fd_cpuset_t const * add_cpus,
     241           0 :                            fd_cpuset_t const * remove_cpus ) {
     242           0 :   DIR * dir = opendir( "/proc/irq" );
     243           0 :   if( FD_UNLIKELY( !dir ) ) {
     244           0 :     FD_LOG_WARNING(( "opendir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     245           0 :     return;
     246           0 :   }
     247             : 
     248           0 :   FD_CPUSET_DECL( fallback );
     249           0 :   if( FD_LIKELY( remove_cpus ) ) fd_cpuset_subtract( fallback, fd_cpu_isolation_host_cpus( fallback ), remove_cpus );
     250             : 
     251           0 :   struct dirent * entry;
     252           0 :   while( (entry = readdir( dir )) ) {
     253           0 :     if( FD_UNLIKELY( !irq_dirent_is_irq( entry->d_name ) ) ) continue;
     254             : 
     255           0 :     FD_CPUSET_DECL( current );
     256           0 :     if( FD_UNLIKELY( !read_irq_smp_affinity( entry->d_name, current ) ) ) continue;
     257             : 
     258           0 :     FD_CPUSET_DECL( next );
     259           0 :     if( FD_LIKELY( remove_cpus ) ) fd_cpuset_subtract( next, current, remove_cpus );
     260           0 :     else                           fd_cpuset_copy    ( next, current );
     261           0 :     if( FD_UNLIKELY( !fd_cpuset_cnt( next ) && remove_cpus ) ) fd_cpuset_copy( next, fallback );
     262           0 :     if( FD_LIKELY( add_cpus ) ) fd_cpuset_union( next, next, add_cpus );
     263             : 
     264           0 :     if( FD_LIKELY( fd_cpuset_eq( current, next ) ) ) continue;
     265           0 :     if( FD_UNLIKELY( !write_irq_smp_affinity( entry->d_name, next, 0 ) && errno!=EPERM && errno!=EIO ) ) {
     266           0 :       int err = errno;
     267           0 :       FD_LOG_WARNING(( "write(/proc/irq/%s/smp_affinity) failed (%i-%s)", entry->d_name, err, fd_io_strerror( err ) ));
     268           0 :     }
     269           0 :   }
     270             : 
     271           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     272           0 : }
     273             : 
     274             : static void
     275           0 : set_irq_smp_affinities( fd_cpuset_t const * desired ) {
     276           0 :   DIR * dir = opendir( "/proc/irq" );
     277           0 :   if( FD_UNLIKELY( !dir ) ) {
     278           0 :     FD_LOG_WARNING(( "opendir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     279           0 :     return;
     280           0 :   }
     281             : 
     282           0 :   struct dirent * entry;
     283           0 :   while( (entry = readdir( dir )) ) {
     284           0 :     if( FD_UNLIKELY( !irq_dirent_is_irq( entry->d_name ) ) ) continue;
     285             : 
     286           0 :     FD_CPUSET_DECL( current );
     287           0 :     if( FD_UNLIKELY( !read_irq_smp_affinity( entry->d_name, current ) ) ) continue;
     288             : 
     289           0 :     if( FD_LIKELY( fd_cpuset_eq( current, desired ) ) ) continue;
     290           0 :     if( FD_UNLIKELY( !write_irq_smp_affinity( entry->d_name, desired, 0 ) && errno!=EPERM && errno!=EIO ) ) {
     291           0 :       int err = errno;
     292           0 :       FD_LOG_WARNING(( "write(/proc/irq/%s/smp_affinity) failed (%i-%s)", entry->d_name, err, fd_io_strerror( err ) ));
     293           0 :     }
     294           0 :   }
     295             : 
     296           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     297           0 : }
     298             : 
     299             : /* topo_banned_cpus returns the set of CPUs that should not handle
     300             :    interrupts in *cpuset. */
     301             : 
     302             : static fd_cpuset_t *
     303             : topo_banned_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
     304           0 :                   fd_topo_t const * topo ) {
     305           0 :   fd_cpuset_new( cpuset );
     306           0 :   ulong cpu_cnt = fd_shmem_cpu_cnt();
     307           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     308           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
     309           0 :     if( tile->cpu_idx < cpu_cnt ) fd_cpuset_insert( cpuset, tile->cpu_idx );
     310           0 :   }
     311           0 :   return cpuset;
     312           0 : }
     313             : 
     314             : static void
     315             : init_perm( fd_cap_chk_t *   chk,
     316           0 :            config_t const * config FD_PARAM_UNUSED ) {
     317           0 :   fd_cap_chk_root( chk, "irq-affinity", "modify `/proc/irq/*/smp_affinity`" );
     318           0 : }
     319             : 
     320             : static void
     321             : fini_perm( fd_cap_chk_t *   chk,
     322           0 :            config_t const * config FD_PARAM_UNUSED ) {
     323           0 :   fd_cap_chk_root( chk, "irq-affinity", "modify `/proc/irq/*/smp_affinity`" );
     324           0 : }
     325             : 
     326             : static void
     327           0 : init( config_t const * config ) {
     328           0 :   FD_CPUSET_DECL( banned );
     329           0 :   topo_banned_cpus( banned, &config->topo );
     330             : 
     331           0 :   FD_CPUSET_DECL( allowed );
     332           0 :   fd_cpuset_subtract( allowed, fd_cpu_isolation_host_cpus( allowed ), banned );
     333             : 
     334           0 :   if( FD_UNLIKELY( !fd_cpuset_cnt( allowed ) ) ) {
     335           0 :     FD_LOG_ERR(( "all host CPUs are assigned to Firedancer tiles; cannot reserve any CPU for device interrupts" ));
     336           0 :   }
     337             : 
     338           0 :   set_irq_smp_affinities( allowed );
     339           0 : }
     340             : 
     341             : static int
     342             : fini( config_t const * config,
     343           0 :       int              pre_init ) {
     344           0 :   (void)pre_init;
     345             : 
     346           0 :   FD_CPUSET_DECL( banned );
     347           0 :   topo_banned_cpus( banned, &config->topo );
     348             : 
     349           0 :   update_irq_smp_affinities( banned, NULL );
     350           0 :   return 1;
     351           0 : }
     352             : 
     353             : static configure_result_t
     354             : check( config_t const * config,
     355           0 :        int              check_type ) {
     356           0 :   FD_CPUSET_DECL( banned );
     357           0 :   topo_banned_cpus( banned, &config->topo );
     358             : 
     359           0 :   FD_CPUSET_DECL( allowed );
     360           0 :   fd_cpuset_subtract( allowed, fd_cpu_isolation_host_cpus( allowed ), banned );
     361             : 
     362           0 :   DIR * dir = opendir( "/proc/irq" );
     363           0 :   if( FD_UNLIKELY( !dir ) ) FD_LOG_ERR(( "opendir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     364             : 
     365           0 :   ulong irq_cnt        = 0UL;
     366           0 :   ulong misconfigured  = 0UL;
     367           0 :   ulong unprobeable    = 0UL;
     368           0 :   ulong overlap_irq_sample[ MISMATCH_SAMPLE_MAX ];
     369           0 :   ulong overlap_irq_sample_cnt = 0UL;
     370           0 :   ulong overlap_irq_cnt = 0UL;
     371             : #if FD_IRQ_AFFINITY_CHECK_TIGHT
     372             :   ulong tight_irq_sample[ MISMATCH_SAMPLE_MAX ];
     373             :   ulong tight_irq_sample_cnt = 0UL;
     374             :   ulong tight_irq_cnt = 0UL;
     375             : #endif
     376           0 :   ulong tile_sample[ MISMATCH_SAMPLE_MAX ];
     377           0 :   ulong tile_sample_cnt = 0UL;
     378           0 :   ulong tile_overlap_cnt = 0UL;
     379           0 :   uchar tile_seen[ FD_TOPO_MAX_TILES ] = {0};
     380           0 :   struct dirent * entry;
     381           0 :   while( (entry = readdir( dir )) ) {
     382           0 :     if( FD_UNLIKELY( !irq_dirent_is_irq( entry->d_name ) ) ) continue;
     383           0 :     irq_cnt++;
     384             : 
     385           0 :     FD_CPUSET_DECL( current );
     386           0 :     if( FD_UNLIKELY( !read_irq_smp_affinity( entry->d_name, current ) ) ) {
     387           0 :       unprobeable++;
     388           0 :       continue;
     389           0 :     }
     390             : 
     391           0 :     if( FD_LIKELY( fd_cpuset_eq( current, allowed ) ) ) continue;
     392             : 
     393             :     /* Some IRQs cannot be moved.  Per the stage comment above, writing
     394             :        the same mask back lets us distinguish those from configurable IRQs. */
     395           0 :     if( FD_UNLIKELY( !write_irq_smp_affinity( entry->d_name, current, 0 ) ) ) {
     396           0 :       if( FD_LIKELY( errno==EPERM || errno==EIO || errno==ENOENT || errno==ENODEV || errno==ENXIO ) ) continue;
     397           0 :       else if( FD_LIKELY( errno==EACCES ) ) {
     398           0 :         char irq_name[ NAME_MAX+1UL ];
     399           0 :         FD_TEST( fd_cstr_printf_check( irq_name, sizeof(irq_name), NULL, "%s", entry->d_name ) );
     400           0 :         if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     401           0 :         if( check_type==FD_CONFIGURE_CHECK_TYPE_FINI_PERM ) CONFIGURE_OK();
     402           0 :         NOT_CONFIGURED( "insufficient permissions to write /proc/irq/%s/smp_affinity", irq_name );
     403           0 :       } else {
     404           0 :         FD_LOG_ERR(( "write(/proc/irq/%s/smp_affinity) failed (%i-%s)", entry->d_name, errno, fd_io_strerror( errno ) ));
     405           0 :       }
     406           0 :     }
     407             : 
     408           0 :     FD_CPUSET_DECL( overlap );
     409           0 :     fd_cpuset_intersect( overlap, current, banned );
     410           0 :     ulong irq = strtoul( entry->d_name, NULL, 10 );
     411           0 :     if( FD_UNLIKELY( !fd_cpuset_is_null( overlap ) ) ) {
     412           0 :       misconfigured++;
     413           0 :       if( FD_LIKELY( overlap_irq_sample_cnt<MISMATCH_SAMPLE_MAX ) ) overlap_irq_sample[ overlap_irq_sample_cnt++ ] = irq;
     414           0 :       overlap_irq_cnt++;
     415           0 :       for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
     416           0 :         fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
     417           0 :         if( FD_UNLIKELY( tile->cpu_idx>=FD_TILE_MAX || !fd_cpuset_test( overlap, tile->cpu_idx ) || tile_seen[ i ] ) ) continue;
     418           0 :         tile_seen[ i ] = 1;
     419           0 :         if( FD_LIKELY( tile_sample_cnt<MISMATCH_SAMPLE_MAX ) ) tile_sample[ tile_sample_cnt++ ] = i;
     420           0 :         tile_overlap_cnt++;
     421           0 :       }
     422           0 :     }
     423             : #if FD_IRQ_AFFINITY_CHECK_TIGHT
     424             :     /* An IRQ pinned to a strict subset of the allowed CPUs (but no tile
     425             :        CPUs) does not steal time from any tile, so it does not violate the
     426             :        stage's goal.  irqbalance routinely narrows IRQs this way, which
     427             :        would make check() never pass while it runs, so the tight check is
     428             :        disabled for now. */
     429             :     else if( FD_UNLIKELY( fd_cpuset_subset( current, allowed ) ) ) {
     430             :       misconfigured++;
     431             :       if( FD_LIKELY( tight_irq_sample_cnt<MISMATCH_SAMPLE_MAX ) ) tight_irq_sample[ tight_irq_sample_cnt++ ] = irq;
     432             :       tight_irq_cnt++;
     433             :     }
     434             : #endif
     435           0 :   }
     436             : 
     437           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     438             : 
     439           0 :   if( FD_UNLIKELY( unprobeable==irq_cnt ) ) PARTIALLY_CONFIGURED( "could not read any IRQ affinity masks from /proc/irq" );
     440           0 :   if( FD_UNLIKELY( misconfigured ) ) {
     441           0 :     if( FD_LIKELY( overlap_irq_cnt ) ) {
     442           0 :       char irq_str[ MISMATCH_STR_LEN ];
     443           0 :       char tile_str[ MISMATCH_TILE_STR_LEN ];
     444           0 :       append_ulong_list_sample( irq_str, sizeof(irq_str), overlap_irq_sample, overlap_irq_sample_cnt, overlap_irq_cnt );
     445           0 :       tile_list_sample( tile_str, sizeof(tile_str), &config->topo, tile_sample, tile_sample_cnt, tile_overlap_cnt );
     446           0 :       NOT_CONFIGURED( "found IRQs overlapping with Firedancer tile CPUs (IRQs: %s; tiles: %s)",
     447           0 :                       irq_str,
     448           0 :                       tile_str );
     449           0 :     }
     450             : #if FD_IRQ_AFFINITY_CHECK_TIGHT
     451             :     if( FD_UNLIKELY( tight_irq_cnt ) ) {
     452             :       char tight_irq_str[ MISMATCH_STR_LEN ];
     453             :       append_ulong_list_sample( tight_irq_str, sizeof(tight_irq_str), tight_irq_sample, tight_irq_sample_cnt, tight_irq_cnt );
     454             :       NOT_CONFIGURED( "found IRQ affinity masks excluding allowed CPUs (IRQs: %s)", tight_irq_str );
     455             :     }
     456             : #endif
     457           0 :     NOT_CONFIGURED( "%lu configurable IRQ affinity masks do not match Firedancer CPU policy", misconfigured );
     458           0 :   }
     459           0 :   CONFIGURE_OK();
     460           0 : }
     461             : 
     462             : configure_stage_t fd_cfg_stage_irq_affinity = {
     463             :   .name            = "irq-affinity",
     464             :   .init_perm       = init_perm,
     465             :   .fini_perm       = fini_perm,
     466             :   .init            = init,
     467             :   .fini            = fini,
     468             :   .check           = check
     469             : };
     470             : 
     471             : #undef FD_IRQ_AFFINITY_CHECK_TIGHT
     472             : #undef SMP_AFFINITY_STR_LEN
     473             : #undef MISMATCH_SAMPLE_MAX
     474             : #undef MISMATCH_STR_LEN
     475             : #undef MISMATCH_TILE_STR_LEN

Generated by: LCOV version 1.14