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

          Line data    Source code
       1             : #include "fd_cpu_isolation.h"
       2             : 
       3             : #include "../../../../disco/topo/fd_cpu_topo.h"
       4             : 
       5             : #include <ctype.h>
       6             : #include <errno.h>
       7             : #include <fcntl.h>
       8             : #include <string.h>
       9             : #include <unistd.h>
      10             : 
      11             : fd_cpuset_t *
      12             : fd_cpu_isolation_tile_cpus( fd_cpuset_t       cpuset[ static fd_cpuset_word_cnt ],
      13           0 :                             fd_topo_t const * topo ) {
      14           0 :   fd_cpuset_new( cpuset );
      15           0 :   ulong cpu_cnt = fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX );
      16           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
      17           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
      18           0 :     if( tile->cpu_idx<cpu_cnt ) fd_cpuset_insert( cpuset, tile->cpu_idx );
      19           0 :   }
      20           0 :   return cpuset;
      21           0 : }
      22             : 
      23             : fd_cpuset_t *
      24           0 : fd_cpu_isolation_host_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ] ) {
      25           0 :   fd_cpuset_new( cpuset );
      26           0 :   ulong cpu_cnt = fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX );
      27           0 :   for( ulong cpu_idx=0UL; cpu_idx<cpu_cnt; cpu_idx++ ) fd_cpuset_insert( cpuset, cpu_idx );
      28           0 :   return cpuset;
      29           0 : }
      30             : 
      31             : int
      32             : fd_cpu_isolation_parse_list( fd_cpuset_t  cpuset[ static fd_cpuset_word_cnt ],
      33           0 :                              char const * list ) {
      34           0 :   fd_cpuset_new( cpuset );
      35             : 
      36           0 :   char const * p = list;
      37           0 :   while( isspace( (uchar)*p ) ) p++;
      38           0 :   if( FD_UNLIKELY( !*p ) ) return 1; /* empty set */
      39             : 
      40             :   /* /sys/devices/system/cpu/nohz_full contains the literal string
      41             :      "(null)" on kernels with CONFIG_NO_HZ_FULL but no nohz_full= boot
      42             :      parameter. */
      43           0 :   if( FD_UNLIKELY( !strncmp( p, "(null)", 6UL ) ) ) return 1;
      44             : 
      45           0 :   for(;;) {
      46           0 :     if( FD_UNLIKELY( !isdigit( (uchar)*p ) ) ) return 0;
      47           0 :     ulong lo = 0UL;
      48           0 :     while( isdigit( (uchar)*p ) ) { lo = lo*10UL + (ulong)(*p-'0'); if( FD_UNLIKELY( lo>FD_TILE_MAX*2UL ) ) return 0; p++; }
      49           0 :     ulong hi = lo;
      50           0 :     if( FD_UNLIKELY( *p=='-' ) ) {
      51           0 :       p++;
      52           0 :       if( FD_UNLIKELY( !isdigit( (uchar)*p ) ) ) return 0;
      53           0 :       hi = 0UL;
      54           0 :       while( isdigit( (uchar)*p ) ) { hi = hi*10UL + (ulong)(*p-'0'); if( FD_UNLIKELY( hi>FD_TILE_MAX*2UL ) ) return 0; p++; }
      55           0 :     }
      56           0 :     if( FD_UNLIKELY( hi<lo ) ) return 0;
      57           0 :     for( ulong cpu=lo; cpu<=hi; cpu++ ) {
      58           0 :       if( FD_LIKELY( cpu<FD_TILE_MAX ) ) fd_cpuset_insert( cpuset, cpu );
      59           0 :     }
      60             : 
      61           0 :     if( FD_LIKELY( *p==',' ) ) { p++; continue; }
      62           0 :     while( isspace( (uchar)*p ) ) p++;
      63           0 :     if( FD_LIKELY( !*p ) ) return 1;
      64           0 :     return 0;
      65           0 :   }
      66           0 : }
      67             : 
      68             : char *
      69             : fd_cpu_isolation_format_list( char *              buf,
      70             :                               ulong               buf_sz,
      71           0 :                               fd_cpuset_t const * cpuset ) {
      72           0 :   char * p   = buf;
      73           0 :   char * end = buf+buf_sz-1UL;
      74           0 :   int first = 1;
      75             : 
      76           0 :   ulong cpu = 0UL;
      77           0 :   while( cpu<FD_TILE_MAX ) {
      78           0 :     if( FD_LIKELY( !fd_cpuset_test( cpuset, cpu ) ) ) { cpu++; continue; }
      79           0 :     ulong lo = cpu;
      80           0 :     while( cpu+1UL<FD_TILE_MAX && fd_cpuset_test( cpuset, cpu+1UL ) ) cpu++;
      81           0 :     ulong hi = cpu;
      82           0 :     cpu++;
      83             : 
      84           0 :     if( FD_UNLIKELY( (ulong)(end-p)<48UL ) ) break; /* can't overflow with LIST_MAX, defensive */
      85           0 :     if( FD_LIKELY( !first ) ) *p++ = ',';
      86           0 :     first = 0;
      87           0 :     p = fd_cstr_append_ulong_as_text( p, 0, 0, lo, fd_ulong_base10_dig_cnt( lo ) );
      88           0 :     if( FD_UNLIKELY( hi>lo ) ) {
      89           0 :       *p++ = '-';
      90           0 :       p = fd_cstr_append_ulong_as_text( p, 0, 0, hi, fd_ulong_base10_dig_cnt( hi ) );
      91           0 :     }
      92           0 :   }
      93           0 :   *p = '\0';
      94           0 :   return buf;
      95           0 : }
      96             : 
      97             : /* Tiles whose per-core throughput is sensitive enough to SMT resource
      98             :    sharing that their hyperthread sibling should be kept quiet.  Keep
      99             :    in sync with the hyperthreads configure stage. */
     100             : 
     101             : static char const * const smt_sensitive_tiles[] = { "pack", "pohh", "poh", NULL };
     102             : 
     103             : fd_cpuset_t *
     104             : fd_cpu_isolation_partition_cpus( fd_cpuset_t       cpuset[ static fd_cpuset_word_cnt ],
     105           0 :                                  fd_topo_t const * topo ) {
     106           0 :   fd_cpu_isolation_tile_cpus( cpuset, topo );
     107             : 
     108           0 :   fd_topo_cpus_t cpus[1];
     109           0 :   fd_topo_cpus_init( cpus );
     110             : 
     111           0 :   for( char const * const * name=smt_sensitive_tiles; *name; name++ ) {
     112           0 :     ulong tile_idx = fd_topo_find_tile( topo, *name, 0UL );
     113           0 :     if( FD_LIKELY( tile_idx==ULONG_MAX ) ) continue;
     114             : 
     115           0 :     ulong cpu_idx = topo->tiles[ tile_idx ].cpu_idx;
     116           0 :     if( FD_UNLIKELY( cpu_idx>=fd_ulong_min( cpus->cpu_cnt, FD_TILE_MAX ) ) ) continue;
     117             : 
     118           0 :     ulong sibling = cpus->cpu[ cpu_idx ].sibling;
     119           0 :     if( FD_UNLIKELY( sibling==ULONG_MAX || sibling>=FD_TILE_MAX ) ) continue;      /* no sibling */
     120           0 :     if( FD_UNLIKELY( !cpus->cpu[ sibling ].online ) ) continue;                    /* offline is even better */
     121           0 :     if( FD_UNLIKELY( fd_cpuset_test( cpuset, sibling ) ) ) continue;               /* used by another tile */
     122             : 
     123           0 :     fd_cpuset_insert( cpuset, sibling );
     124           0 :   }
     125           0 :   return cpuset;
     126           0 : }
     127             : 
     128             : int
     129             : fd_cpu_isolation_parse_mask( fd_cpuset_t  cpuset[ static fd_cpuset_word_cnt ],
     130           0 :                              char const * mask ) {
     131           0 :   fd_cpuset_new( cpuset );
     132             : 
     133           0 :   char const * end = mask;
     134           0 :   while( *end && !isspace( (uchar)*end ) ) end++;
     135           0 :   if( FD_UNLIKELY( end==mask ) ) return 0;
     136             : 
     137           0 :   ulong cpu_idx = 0UL;
     138           0 :   for( char const * p=end; p>mask; ) {
     139           0 :     p--;
     140           0 :     if( FD_UNLIKELY( *p==',' ) ) continue;
     141             : 
     142           0 :     int digit;
     143           0 :     if(      FD_LIKELY( (*p>='0') & (*p<='9') ) ) digit = *p-'0';
     144           0 :     else if( FD_LIKELY( (*p>='a') & (*p<='f') ) ) digit = 10+*p-'a';
     145           0 :     else if( FD_LIKELY( (*p>='A') & (*p<='F') ) ) digit = 10+*p-'A';
     146           0 :     else return 0;
     147             : 
     148           0 :     for( ulong bit=0UL; bit<4UL; bit++ ) {
     149           0 :       if( FD_UNLIKELY( cpu_idx>=FD_TILE_MAX ) ) return 1;
     150           0 :       if( FD_UNLIKELY( digit & (1<<bit) ) ) fd_cpuset_insert( cpuset, cpu_idx );
     151           0 :       cpu_idx++;
     152           0 :     }
     153           0 :   }
     154           0 :   return 1;
     155           0 : }
     156             : 
     157             : char *
     158             : fd_cpu_isolation_format_mask( char *              buf,
     159             :                               ulong               buf_sz,
     160           0 :                               fd_cpuset_t const * cpuset ) {
     161           0 :   ulong cpu_cnt  = fd_ulong_max( fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX ), 1UL );
     162           0 :   ulong word_cnt = fd_ulong_max( (cpu_cnt+31UL)/32UL, 1UL );
     163           0 :   char * p = buf;
     164             : 
     165           0 :   FD_TEST( buf_sz>=word_cnt*9UL+1UL );
     166             : 
     167           0 :   for( ulong word_rem=word_cnt; word_rem; word_rem-- ) {
     168           0 :     ulong word_idx = word_rem-1UL;
     169           0 :     if( FD_UNLIKELY( word_idx!=word_cnt-1UL ) ) *p++ = ',';
     170             : 
     171           0 :     for( ulong nib_rem=8UL; nib_rem; nib_rem-- ) {
     172           0 :       ulong nib_idx = nib_rem-1UL;
     173           0 :       int digit = 0;
     174           0 :       for( ulong bit=0UL; bit<4UL; bit++ ) {
     175           0 :         ulong cpu_idx = word_idx*32UL + nib_idx*4UL + bit;
     176           0 :         if( FD_LIKELY( cpu_idx<FD_TILE_MAX && fd_cpuset_test( cpuset, cpu_idx ) ) ) digit |= (int)(1UL<<bit);
     177           0 :       }
     178           0 :       *p++ = "0123456789abcdef"[ digit ];
     179           0 :     }
     180           0 :   }
     181           0 :   *p = '\0';
     182           0 :   return buf;
     183           0 : }
     184             : 
     185             : 
     186             : int
     187             : fd_cpu_isolation_read_list( char const * path,
     188           0 :                             fd_cpuset_t  cpuset[ static fd_cpuset_word_cnt ] ) {
     189           0 :   int fd = open( path, O_RDONLY );
     190           0 :   if( FD_UNLIKELY( fd<0 ) ) {
     191           0 :     if( FD_LIKELY( errno==ENOENT ) ) return 0;
     192           0 :     FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     193           0 :   }
     194             : 
     195           0 :   char list[ FD_CPU_ISOLATION_LIST_MAX ];
     196           0 :   long list_len = read( fd, list, sizeof(list)-1UL );
     197           0 :   if( FD_UNLIKELY( list_len<0L ) ) FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     198           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     199           0 :   list[ list_len ] = '\0';
     200             : 
     201           0 :   if( FD_UNLIKELY( !fd_cpu_isolation_parse_list( cpuset, list ) ) )
     202           0 :     FD_LOG_ERR(( "failed to parse `%s` (\"%s\")", path, list ));
     203           0 :   return 1;
     204           0 : }

Generated by: LCOV version 1.14