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

          Line data    Source code
       1             : /* The kworkers stage removes Firedancer tile CPUs from the cpumask of
       2             :    unbound kernel workqueues, so that deferred kernel work (writeback,
       3             :    vmstat refresh, various driver bottom halves, and notably the
       4             :    nohz_full remote tick offload) runs on housekeeping CPUs instead of
       5             :    stealing time from spinning tiles.
       6             : 
       7             :    The kernel exposes this as a global hex mask at
       8             :    /sys/devices/virtual/workqueue/cpumask.  Per-CPU (bound) kworkers
       9             :    are unaffected: they are idle unless work is queued on their CPU,
      10             :    which the irq-affinity and cpuset stages minimize.
      11             : 
      12             :    The kernel rejects a mask with no online CPU, so like irq-affinity
      13             :    this stage refuses to run if tiles cover every host CPU.
      14             : 
      15             :    fini restores the mask to all host CPUs (the kernel default),
      16             :    which may over-restore if the operator had their own narrower mask
      17             :    before init; the previous mask is not persisted anywhere. */
      18             : 
      19             : #include "configure.h"
      20             : #include "fd_cpu_isolation.h"
      21             : 
      22             : #include <errno.h>
      23             : #include <fcntl.h>
      24             : #include <unistd.h>
      25             : 
      26           0 : #define NAME "kworkers"
      27             : 
      28           0 : #define WQ_CPUMASK_PATH "/sys/devices/virtual/workqueue/cpumask"
      29             : 
      30             : /* read_wq_cpumask reads and parses the workqueue cpumask.  Fails fast
      31             :    on any error except the file being absent (which enabled() already
      32             :    gates on, but guard against removal races anyway): this is a
      33             :    kernel-provided file with a fixed format, so parse or IO failures
      34             :    mean something is wrong that we should not paper over. */
      35             : 
      36             : static void
      37           0 : read_wq_cpumask( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ] ) {
      38           0 :   int fd = open( WQ_CPUMASK_PATH, O_RDONLY );
      39           0 :   if( FD_UNLIKELY( fd<0 ) ) FD_LOG_ERR(( "open(" WQ_CPUMASK_PATH ") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      40             : 
      41           0 :   char mask[ FD_CPU_ISOLATION_MASK_MAX+64UL ];
      42           0 :   long mask_len = read( fd, mask, sizeof(mask)-1UL );
      43           0 :   if( FD_UNLIKELY( mask_len<=0L ) ) FD_LOG_ERR(( "read(" WQ_CPUMASK_PATH ") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      44           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(" WQ_CPUMASK_PATH ") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      45           0 :   mask[ mask_len ] = '\0';
      46             : 
      47           0 :   if( FD_UNLIKELY( !fd_cpu_isolation_parse_mask( cpuset, mask ) ) )
      48           0 :     FD_LOG_ERR(( "failed to parse `" WQ_CPUMASK_PATH "` (\"%s\")", mask ));
      49           0 : }
      50             : 
      51             : static void
      52           0 : write_wq_cpumask( fd_cpuset_t const * cpuset ) {
      53           0 :   char mask[ FD_CPU_ISOLATION_MASK_MAX ];
      54           0 :   fd_cpu_isolation_format_mask( mask, sizeof(mask), cpuset );
      55           0 :   ulong mask_len = strlen( mask );
      56             : 
      57           0 :   FD_LOG_NOTICE(( "%sRUN: `echo \"%s\" > " WQ_CPUMASK_PATH "`%s", fd_log_style_dim(), mask, fd_log_style_normal() ));
      58             : 
      59           0 :   int fd = open( WQ_CPUMASK_PATH, O_WRONLY );
      60           0 :   if( FD_UNLIKELY( fd<0 ) ) FD_LOG_ERR(( "open(" WQ_CPUMASK_PATH ") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      61           0 :   if( FD_UNLIKELY( write( fd, mask, mask_len )!=(long)mask_len ) )
      62           0 :     FD_LOG_ERR(( "write(" WQ_CPUMASK_PATH ") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      63           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(" WQ_CPUMASK_PATH ") failed (%i-%s)", errno, fd_io_strerror( errno ) ));
      64           0 : }
      65             : 
      66             : static int
      67           0 : enabled( config_t const * config ) {
      68           0 :   (void)config;
      69             :   /* Not all kernels expose the unbound workqueue mask (CONFIG_SYSFS,
      70             :      ancient kernels).  If absent there is nothing to configure. */
      71           0 :   return 0==access( WQ_CPUMASK_PATH, F_OK );
      72           0 : }
      73             : 
      74             : static void
      75             : init_perm( fd_cap_chk_t *   chk,
      76           0 :            config_t const * config FD_PARAM_UNUSED ) {
      77           0 :   fd_cap_chk_root( chk, NAME, "modify `" WQ_CPUMASK_PATH "`" );
      78           0 : }
      79             : 
      80             : static void
      81             : fini_perm( fd_cap_chk_t *   chk,
      82           0 :            config_t const * config FD_PARAM_UNUSED ) {
      83           0 :   fd_cap_chk_root( chk, NAME, "modify `" WQ_CPUMASK_PATH "`" );
      84           0 : }
      85             : 
      86             : static void
      87           0 : init( config_t const * config ) {
      88           0 :   FD_CPUSET_DECL( tile_cpus );
      89           0 :   fd_cpu_isolation_tile_cpus( tile_cpus, &config->topo );
      90             : 
      91           0 :   FD_CPUSET_DECL( allowed );
      92           0 :   fd_cpu_isolation_host_cpus( allowed );
      93           0 :   fd_cpuset_subtract( allowed, allowed, tile_cpus );
      94             : 
      95           0 :   if( FD_UNLIKELY( !fd_cpuset_cnt( allowed ) ) )
      96           0 :     FD_LOG_ERR(( "all host CPUs are assigned to Firedancer tiles; cannot reserve any CPU for kernel workqueues" ));
      97             : 
      98           0 :   write_wq_cpumask( allowed );
      99           0 : }
     100             : 
     101             : static int
     102             : fini( config_t const * config,
     103           0 :       int              pre_init ) {
     104           0 :   (void)config; (void)pre_init;
     105             : 
     106           0 :   FD_CPUSET_DECL( all );
     107           0 :   fd_cpu_isolation_host_cpus( all );
     108           0 :   write_wq_cpumask( all );
     109           0 :   return 1;
     110           0 : }
     111             : 
     112             : static configure_result_t
     113             : check( config_t const * config,
     114           0 :        int              check_type ) {
     115           0 :   (void)check_type;
     116             : 
     117           0 :   FD_CPUSET_DECL( current );
     118           0 :   read_wq_cpumask( current );
     119             : 
     120           0 :   FD_CPUSET_DECL( tile_cpus );
     121           0 :   fd_cpu_isolation_tile_cpus( tile_cpus, &config->topo );
     122             : 
     123           0 :   FD_CPUSET_DECL( overlap );
     124           0 :   fd_cpuset_intersect( overlap, current, tile_cpus );
     125           0 :   if( FD_UNLIKELY( !fd_cpuset_is_null( overlap ) ) ) {
     126           0 :     char list[ FD_CPU_ISOLATION_LIST_MAX ];
     127           0 :     fd_cpu_isolation_format_list( list, sizeof(list), overlap );
     128           0 :     NOT_CONFIGURED( "kernel workqueue cpumask includes Firedancer tile CPUs %s", list );
     129           0 :   }
     130             : 
     131           0 :   CONFIGURE_OK();
     132           0 : }
     133             : 
     134             : configure_stage_t fd_cfg_stage_kworkers = {
     135             :   .name            = NAME,
     136             :   .always_recreate = 0,
     137             :   .enabled         = enabled,
     138             :   .init_perm       = init_perm,
     139             :   .fini_perm       = fini_perm,
     140             :   .init            = init,
     141             :   .fini            = fini,
     142             :   .check           = check,
     143             : };
     144             : 
     145             : #undef NAME
     146             : #undef WQ_CPUMASK_PATH

Generated by: LCOV version 1.14