LCOV - code coverage report
Current view: top level - disco/topo - fd_cpu_topo.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 200 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 22 0.0 %

          Line data    Source code
       1             : #include "fd_cpu_topo.h"
       2             : 
       3             : #include "../../util/shmem/fd_shmem_private.h"
       4             : #include "../../util/tile/fd_tile_private.h"
       5             : 
       6             : #include <errno.h>
       7             : #include <unistd.h>
       8             : #include <fcntl.h>
       9             : #include <stdio.h>
      10             : #include <stdlib.h>
      11             : #include <string.h>
      12             : 
      13             : static uint
      14             : read_uint_file( char const * path,
      15           0 :                 char const * errmsg_enoent ) {
      16           0 :   FILE * fp = fopen( path, "r" );
      17           0 :   if( FD_UNLIKELY( !fp ) ) {
      18           0 :     if( FD_LIKELY( errno==ENOENT ) ) FD_LOG_ERR(( "%s fopen failed `%s` (%i-%s)", errmsg_enoent, path, errno, fd_io_strerror( errno ) ));
      19           0 :     else                             FD_LOG_ERR(( "fopen failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      20           0 :   }
      21             : 
      22           0 :   uint value = 0U;
      23           0 :   if( FD_UNLIKELY( 1!=fscanf( fp, "%u\n", &value ) ) ) FD_LOG_ERR(( "failed to read uint from `%s`", path ));
      24           0 :   if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "fclose failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      25           0 :   return value;
      26           0 : }
      27             : 
      28             : static int
      29             : parse_topology_id( char const * buf,
      30           0 :                    ulong        sz ) {
      31           0 :   if( FD_UNLIKELY( !sz ) ) return -1;
      32           0 :   ulong i = 0UL;
      33           0 :   uint id = 0U;
      34           0 :   while( i<sz && buf[ i ]>='0' && buf[ i ]<='9' ) {
      35           0 :     uint digit = (uint)(buf[ i++ ]-'0');
      36           0 :     if( FD_UNLIKELY( id>((uint)INT_MAX-digit)/10U ) ) return -1;
      37           0 :     id = 10U*id+digit;
      38           0 :   }
      39           0 :   if( FD_UNLIKELY( !i || (i!=sz && !(i+1UL==sz && buf[ i ]=='\n')) ) ) return -1;
      40           0 :   return (int)id;
      41           0 : }
      42             : 
      43             : static int
      44           0 : read_topology_id( char const * path ) {
      45           0 :   int fd;
      46           0 : retry:
      47           0 :   do fd = open( path, O_RDONLY ); while( fd<0 && errno==EINTR );
      48           0 :   if( FD_UNLIKELY( fd<0 ) ) {
      49           0 :     if( errno!=ENOENT ) FD_LOG_WARNING(( "open `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      50           0 :     return -1;
      51           0 :   }
      52             : 
      53           0 :   char buf[ 64 ];
      54           0 :   ulong sz;
      55           0 :   int err = fd_io_read( fd, buf, sizeof(buf), sizeof(buf), &sz );
      56           0 :   if( FD_UNLIKELY( close( fd ) ) ) {
      57           0 :     FD_LOG_WARNING(( "close `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      58           0 :     return -1;
      59           0 :   }
      60             :   /* fd_io_read may have advanced the file offset before EINTR. */
      61           0 :   if( FD_UNLIKELY( err==EINTR ) ) goto retry;
      62           0 :   if( FD_UNLIKELY( err>0 ) ) {
      63           0 :     FD_LOG_WARNING(( "read `%s` failed (%i-%s)", path, err, fd_io_strerror( err ) ));
      64           0 :     return -1;
      65           0 :   }
      66           0 :   if( FD_UNLIKELY( sz==sizeof(buf) ) ) return -1;
      67           0 :   return parse_topology_id( buf, sz );
      68           0 : }
      69             : 
      70             : static void
      71             : assign_die_indices( fd_topo_cpus_t * cpus,
      72             :                     int const *      package_ids,
      73           0 :                     int const *      die_ids ) {
      74           0 :   ulong die_cnt = 0UL;
      75           0 :   for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
      76           0 :     cpus->cpu[ i ].die_idx = ULONG_MAX;
      77           0 :     if( FD_UNLIKELY( package_ids[ i ]<0 || die_ids[ i ]<0 ) ) continue;
      78           0 :     for( ulong j=0UL; j<i; j++ ) {
      79           0 :       if( package_ids[ i ]==package_ids[ j ] && die_ids[ i ]==die_ids[ j ] ) {
      80           0 :         cpus->cpu[ i ].die_idx = cpus->cpu[ j ].die_idx;
      81           0 :         break;
      82           0 :       }
      83           0 :     }
      84           0 :     if( cpus->cpu[ i ].die_idx==ULONG_MAX ) cpus->cpu[ i ].die_idx = die_cnt++;
      85           0 :   }
      86           0 : }
      87             : 
      88             : static void
      89             : assign_l3_indices( fd_topo_cpus_t * cpus,
      90             :                    int const *      package_ids,
      91           0 :                    int const *      l3_ids ) {
      92           0 :   cpus->l3_cnt = 0UL;
      93           0 :   for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
      94           0 :     cpus->cpu[ i ].l3_idx = ULONG_MAX;
      95           0 :     if( FD_UNLIKELY( package_ids[ i ]<0 || l3_ids[ i ]<0 ) ) continue;
      96             : 
      97           0 :     for( ulong j=0UL; j<i; j++ ) {
      98           0 :       if( package_ids[ i ]==package_ids[ j ] && l3_ids[ i ]==l3_ids[ j ] ) {
      99           0 :         cpus->cpu[ i ].l3_idx = cpus->cpu[ j ].l3_idx;
     100           0 :         break;
     101           0 :       }
     102           0 :     }
     103           0 :     if( cpus->cpu[ i ].l3_idx==ULONG_MAX ) cpus->cpu[ i ].l3_idx = cpus->l3_cnt++;
     104           0 :   }
     105           0 : }
     106             : 
     107             : static int
     108           0 : read_l3_id( ulong cpu_idx ) {
     109           0 :   for( ulong index=0UL; index<8UL; index++ ) {
     110           0 :     char path[ PATH_MAX ];
     111           0 :     fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/cache/index%lu/level", cpu_idx, index );
     112           0 :     int level = read_topology_id( path );
     113           0 :     if( level<0 ) return -1;
     114           0 :     if( level!=3 ) continue;
     115           0 :     fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/cache/index%lu/id", cpu_idx, index );
     116           0 :     return read_topology_id( path );
     117           0 :   }
     118           0 :   return -1;
     119           0 : }
     120             : 
     121             : static int
     122           0 : fd_topo_cpus_online( ulong cpu_idx ) {
     123           0 :   if( FD_UNLIKELY( cpu_idx==0UL ) ) return 1; /* Cannot set cpu0 to offline */
     124             : 
     125           0 :   char path[ PATH_MAX ];
     126           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/sys/devices/system/cpu/cpu%lu/online", cpu_idx ) );
     127           0 :   return (int)read_uint_file( path, "error reading cpu online status" );
     128           0 : }
     129             : 
     130             : static int
     131             : parse_cpulist( char const *  list,
     132           0 :                fd_cpuset_t * cpuset ) {
     133           0 :   fd_cpuset_new( cpuset );
     134           0 :   char const * p = list;
     135           0 :   while( *p==' ' ) p++;
     136           0 :   if( !strncmp( p, "(null)", 6UL ) ) return 1;
     137           0 :   while( *p && *p!='\n' ) {
     138           0 :     char * end;
     139           0 :     ulong lo = strtoul( p, &end, 10 );
     140           0 :     if( end==p ) return 0;
     141           0 :     ulong hi = lo;
     142           0 :     p = end;
     143           0 :     if( *p=='-' ) {
     144           0 :       hi = strtoul( p+1, &end, 10 );
     145           0 :       if( end==p+1 || hi<lo ) return 0;
     146           0 :       p = end;
     147           0 :     }
     148           0 :     for( ulong cpu=lo; cpu<=hi && cpu<FD_TILE_MAX; cpu++ ) fd_cpuset_insert( cpuset, cpu );
     149           0 :     if( *p==',' ) p++;
     150           0 :     else if( *p && *p!='\n' ) return 0;
     151           0 :   }
     152           0 :   return 1;
     153           0 : }
     154             : 
     155             : static void
     156             : read_cpulist( char const *  path,
     157           0 :               fd_cpuset_t * cpuset ) {
     158           0 :   fd_cpuset_new( cpuset );
     159           0 :   int fd = open( path, O_RDONLY );
     160           0 :   if( FD_UNLIKELY( fd<0 ) ) {
     161           0 :     if( FD_UNLIKELY( errno!=ENOENT ) ) FD_LOG_WARNING(( "open `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     162           0 :     return;
     163           0 :   }
     164           0 :   char list[ 16384 ]; /* alternating cpulist over 1024 cpus */
     165           0 :   long sz = read( fd, list, sizeof(list)-1UL );
     166           0 :   if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     167           0 :   if( FD_UNLIKELY( sz<0L ) ) FD_LOG_ERR(( "read `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     168           0 :   list[ sz ] = '\0';
     169           0 :   if( FD_UNLIKELY( !parse_cpulist( list, cpuset ) ) ) FD_LOG_ERR(( "failed to parse `%s` (\"%s\")", path, list ));
     170           0 : }
     171             : 
     172             : void
     173           0 : fd_topo_cpus_init( fd_topo_cpus_t * cpus ) {
     174           0 :   cpus->numa_node_cnt = fd_numa_node_cnt();
     175           0 :   cpus->cpu_cnt = fd_numa_cpu_cnt();
     176           0 :   if( FD_UNLIKELY( !cpus->cpu_cnt ) ) FD_LOG_ERR(( "could not determine the number of CPUs" ));
     177           0 :   if( FD_UNLIKELY( cpus->cpu_cnt > FD_TILE_MAX ) ) {
     178           0 :     FD_LOG_ERR(( "unsupported system: Firedancer supports up to %lu CPUs", FD_TILE_MAX ));
     179           0 :   }
     180             : 
     181           0 :   int package_ids[ FD_TILE_MAX ];
     182           0 :   int die_ids    [ FD_TILE_MAX ];
     183           0 :   int l3_ids     [ FD_TILE_MAX ];
     184           0 :   for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
     185           0 :     cpus->cpu[ i ].idx = i;
     186           0 :     cpus->cpu[ i ].online = fd_topo_cpus_online( i );
     187           0 :     cpus->cpu[ i ].numa_node = fd_numa_node_idx( i );
     188           0 :     if( FD_LIKELY( cpus->cpu[ i ].online ) ) cpus->cpu[ i ].sibling = fd_tile_private_sibling_idx( i );
     189           0 :     else                                     cpus->cpu[ i ].sibling = ULONG_MAX;
     190             : 
     191           0 :     char path[ PATH_MAX ];
     192           0 :     fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/physical_package_id", i );
     193           0 :     package_ids[ i ] = read_topology_id( path );
     194           0 :     fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/devices/system/cpu/cpu%lu/topology/die_id", i );
     195           0 :     die_ids[ i ] = read_topology_id( path );
     196           0 :     l3_ids[ i ] = cpus->cpu[ i ].online ? read_l3_id( i ) : -1;
     197           0 :   }
     198             : 
     199           0 :   FD_CPUSET_DECL( isolated  ); read_cpulist( "/sys/devices/system/cpu/isolated",  isolated  );
     200           0 :   FD_CPUSET_DECL( nohz_full ); read_cpulist( "/sys/devices/system/cpu/nohz_full", nohz_full );
     201           0 :   for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
     202           0 :     cpus->cpu[ i ].isolated  = fd_cpuset_test( isolated,  i );
     203           0 :     cpus->cpu[ i ].nohz_full = fd_cpuset_test( nohz_full, i );
     204           0 :   }
     205           0 :   assign_die_indices( cpus, package_ids, die_ids );
     206           0 :   assign_l3_indices( cpus, package_ids, l3_ids );
     207           0 : }
     208             : 
     209             : int
     210           0 : fd_topo_cpus_l3_complete( fd_topo_cpus_t const * cpus ) {
     211           0 :   if( FD_UNLIKELY( cpus->l3_cnt<2UL ) ) return 0;
     212           0 :   for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
     213           0 :     if( FD_UNLIKELY( cpus->cpu[ i ].online && cpus->cpu[ i ].l3_idx==ULONG_MAX ) ) return 0;
     214           0 :   }
     215           0 :   return 1;
     216           0 : }
     217             : 
     218             : void
     219           0 : fd_topo_cpus_printf( fd_topo_cpus_t * cpus ) {
     220           0 :   for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
     221           0 :     FD_LOG_NOTICE(( "cpu%lu: online=%i sibling=%lu numa_node=%lu die_idx=%lu l3_idx=%lu isolated=%i nohz_full=%i", i, cpus->cpu[ i ].online, cpus->cpu[ i ].sibling, cpus->cpu[ i ].numa_node, cpus->cpu[ i ].die_idx, cpus->cpu[ i ].l3_idx, cpus->cpu[ i ].isolated, cpus->cpu[ i ].nohz_full ));
     222           0 :   }
     223           0 : }
     224             : 
     225             : void
     226             : fd_topo_cpus_floating( fd_topo_cpus_t const * cpus,
     227             :                        fd_topo_t const *      topo,
     228             :                        fd_cpuset_t const *    launch,
     229             :                        fd_cpuset_t *          float_cpu_set,
     230           0 :                        fd_cpuset_t *          cpuless_cpu_set ) {
     231           0 :   FD_CPUSET_DECL( pinned );
     232           0 :   fd_cpuset_new( float_cpu_set );
     233           0 :   for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     234           0 :     fd_topo_tile_t const * tile = &topo->tiles[ i ];
     235           0 :     if( FD_UNLIKELY( tile->cpu_idx>=FD_TILE_MAX ) ) continue;
     236           0 :     fd_cpuset_insert( tile->floats ? float_cpu_set : pinned, tile->cpu_idx );
     237           0 :   }
     238             : 
     239           0 :   FD_CPUSET_DECL( claimed );
     240           0 :   fd_cpuset_copy( claimed, pinned );
     241           0 :   for( ulong cpu=0UL; cpu<fd_ulong_min( cpus->cpu_cnt, FD_TILE_MAX ); cpu++ ) {
     242           0 :     ulong sibling = cpus->cpu[ cpu ].sibling;
     243           0 :     if( fd_cpuset_test( pinned, cpu ) && sibling<FD_TILE_MAX ) fd_cpuset_insert( claimed, sibling );
     244           0 :   }
     245           0 :   fd_cpuset_subtract( float_cpu_set, float_cpu_set, claimed );
     246             : 
     247           0 :   for( ulong cpu=0UL; cpu<fd_ulong_min( cpus->cpu_cnt, FD_TILE_MAX ); cpu++ ) {
     248           0 :     if( cpus->cpu[ cpu ].isolated ) fd_cpuset_insert( claimed, cpu );
     249           0 :   }
     250           0 :   fd_cpuset_intersect( cpuless_cpu_set, float_cpu_set, launch );
     251           0 :   fd_cpuset_subtract( cpuless_cpu_set, cpuless_cpu_set, claimed );
     252           0 :   if( FD_LIKELY( !fd_cpuset_cnt( cpuless_cpu_set ) ) ) {
     253             :     /* floats tiles are claimed too: they share their own pool */
     254           0 :     for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
     255           0 :       if( topo->tiles[ i ].cpu_idx<FD_TILE_MAX ) fd_cpuset_insert( claimed, topo->tiles[ i ].cpu_idx );
     256           0 :     }
     257           0 :     fd_cpuset_subtract( cpuless_cpu_set, launch, claimed );
     258           0 :   }
     259           0 :   if( FD_UNLIKELY( !fd_cpuset_cnt( cpuless_cpu_set ) ) ) fd_cpuset_copy( cpuless_cpu_set, launch );
     260           0 : }

Generated by: LCOV version 1.14