LCOV - code coverage report
Current view: top level - util/shmem - fd_numa_linux.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 91 118 77.1 %
Date: 2026-10-22 18:10:43 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /* syscall API requires _GNU_SOURCE */
       2             : #define _GNU_SOURCE
       3             : #include "fd_shmem_private.h"
       4             : #include "../sanitize/fd_msan.h"
       5             : #include <errno.h>
       6             : #include <dirent.h>
       7             : 
       8             : /* The below uses the sysfs API added ~2009-Dec.  See
       9             :    https://github.com/torvalds/linux/commit/1830794ae6392ce12d36dbcc5ff52f11298ddab6 */
      10             : 
      11             : /* fd_numa_private_parse_idx parses a cstr of the form
      12             :    `prefix[0-9]+` into an index.  The value is strictly interpreted as
      13             :    a non-negative base 10 value.  Returns -1 if the value could not be
      14             :    parsed (e.g. s is NULL, s does not have the prefix, s does not
      15             :    have a base 10 suffix, the value overflows an int representation). */
      16             : 
      17             : FD_FN_PURE static int
      18             : fd_numa_private_parse_idx( char const * s,
      19             :                            char const * prefix,
      20        1183 :                            ulong        prefix_len ) {
      21        1183 :   if( FD_UNLIKELY( !s ) ) return -1;
      22        1183 :   if( FD_UNLIKELY( strncmp( s, prefix, prefix_len ) ) ) return -1;
      23         259 :   s += prefix_len;
      24             : 
      25         259 :   long val = 0L;
      26             : 
      27         259 :   char const * t = s;
      28         662 :   for(;;) {
      29         662 :     char c = *t;
      30         662 :     if( !c ) break; /* host dep branch prob */
      31         405 :     if( FD_UNLIKELY( (c<'0') | (c>'9') ) ) return -1; /* non-digit encountered */
      32         403 :     val = (long)(c-'0') + 10L*val;
      33         403 :     if( FD_UNLIKELY( val>(long)INT_MAX ) ) return -1; /* overflow */
      34         403 :     t++;
      35         403 :   }
      36         257 :   if( FD_UNLIKELY( s==t ) ) return -1; /* empty idx */
      37             : 
      38         257 :   return (int)val;
      39         257 : }
      40             : 
      41             : ulong
      42           1 : fd_numa_node_cnt( void ) {
      43             : 
      44             :   /* Open sysfs dir containing NUMA config.  Abort if this fails. */
      45             : 
      46           1 :   char const * path = "/sys/devices/system/node";
      47           1 :   DIR *        dir  = opendir( path );
      48           1 :   if( FD_UNLIKELY( !dir ) ) {
      49           0 :     FD_LOG_WARNING(( "opendir( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      50           0 :     return 0UL;
      51           0 :   }
      52             : 
      53             :   /* Scan dir to get number of NUMA nodes.  Note that we do not assume
      54             :      the system indexes numa nodes contiguously (but it almost certainly
      55             :      does). */
      56             : 
      57           1 :   int node_idx_max = INT_MIN;
      58          12 :   for(;;) {
      59          12 :     struct dirent * dirent = readdir( dir );
      60          12 :     if( !dirent ) break;
      61          11 :     node_idx_max = fd_int_max( fd_numa_private_parse_idx( dirent->d_name, "node", 4UL ), node_idx_max );
      62          11 :   }
      63             : 
      64             :   /* Close dir and return what was found */
      65             : 
      66           1 :   if( FD_UNLIKELY( closedir( dir ) ) )
      67           0 :     FD_LOG_WARNING(( "closedir( \"%s\" ) failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
      68             : 
      69           1 :   if( FD_UNLIKELY( node_idx_max<0 ) ) {
      70           0 :     FD_LOG_WARNING(( "No numa nodes found in \"%s\"", path ));
      71           0 :     return 0UL;
      72           0 :   }
      73             : 
      74           1 :   return ((ulong)node_idx_max) + 1UL;
      75           1 : }
      76             : 
      77             : ulong
      78           1 : fd_numa_private_cpu_cnt( char const * path ) {
      79             : 
      80           1 :   DIR * dir = opendir( path );
      81           1 :   if( FD_UNLIKELY( !dir ) ) {
      82           0 :     FD_LOG_WARNING(( "opendir( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
      83           0 :     return 0UL;
      84           0 :   }
      85             : 
      86           1 :   int cpu_idx_max = -1;
      87           1 :   int read_error  = 0;
      88         149 :   for(;;) {
      89         149 :     errno = 0;
      90         149 :     struct dirent * dirent = readdir( dir );
      91         149 :     if( !dirent ) {
      92           1 :       if( FD_UNLIKELY( errno==EINTR ) ) continue;
      93           1 :       read_error = errno;
      94           1 :       break;
      95           1 :     }
      96         148 :     cpu_idx_max = fd_int_max( fd_numa_private_parse_idx( dirent->d_name, "cpu", 3UL ), cpu_idx_max );
      97         148 :   }
      98             : 
      99           1 :   if( FD_UNLIKELY( closedir( dir ) ) )
     100           0 :     FD_LOG_WARNING(( "closedir( \"%s\" ) failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
     101             : 
     102           1 :   if( FD_UNLIKELY( read_error ) ) {
     103           0 :     FD_LOG_WARNING(( "readdir( \"%s\" ) failed (%i-%s)", path, read_error, fd_io_strerror( read_error ) ));
     104           0 :     return 0UL;
     105           0 :   }
     106             : 
     107           1 :   if( FD_UNLIKELY( cpu_idx_max<0 ) ) {
     108           0 :     FD_LOG_WARNING(( "No CPUs found in \"%s\"", path ));
     109           0 :     return 0UL;
     110           0 :   }
     111             : 
     112           1 :   return ((ulong)cpu_idx_max) + 1UL;
     113           1 : }
     114             : 
     115             : ulong
     116           1 : fd_numa_cpu_cnt( void ) {
     117           1 :   return fd_numa_private_cpu_cnt( "/sys/devices/system/cpu" );
     118           1 : }
     119             : 
     120             : ulong
     121         128 : fd_numa_node_idx( ulong cpu_idx ) {
     122             : 
     123             :   /* Open sysfs dir containing CPU config.  Abort if this fails. */
     124             : 
     125         128 :   char  path[64];
     126         128 :   DIR * dir = opendir( fd_cstr_printf( path, 64UL, NULL, "/sys/devices/system/cpu/cpu%lu", cpu_idx ) );
     127         128 :   if( FD_UNLIKELY( !dir ) ) {
     128           0 :     FD_LOG_WARNING(( "opendir( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     129           0 :     return ULONG_MAX;
     130           0 :   }
     131             : 
     132             :   /* Scan dir for symlink to numa config */
     133             : 
     134         128 :   int node_idx = -1;
     135        1024 :   for(;;) {
     136        1024 :     struct dirent * dirent = readdir( dir );
     137        1024 :     if( !dirent ) break;
     138        1024 :     node_idx = fd_numa_private_parse_idx( dirent->d_name, "node", 4UL );
     139        1024 :     if( node_idx!=-1 ) break;
     140        1024 :   }
     141             : 
     142             :   /* Close dir and return what was found */
     143             : 
     144         128 :   if( FD_UNLIKELY( closedir( dir ) ) )
     145           0 :     FD_LOG_WARNING(( "closedir( \"%s\" ) failed (%i-%s); attempting to continue", path, errno, fd_io_strerror( errno ) ));
     146             : 
     147         128 :   if( FD_UNLIKELY( node_idx<0 ) ) {
     148           0 :     FD_LOG_WARNING(( "No numa node found in \"%s\"", path ));
     149           0 :     return ULONG_MAX;
     150           0 :   }
     151             : 
     152         128 :   return (ulong)node_idx;
     153         128 : }
     154             : 
     155             : /* FIXME: probably should do a FD_HAS_ASAN switch for the below to use
     156             :    the appropriate functionality when FD_HAS_ASAN is set (or maybe have
     157             :    a separate implementation for compiling under FD_HAS_ASAN). */
     158             : 
     159             : #include <unistd.h>
     160             : #include <sys/syscall.h>
     161             : 
     162             : /* Note that the LLVM AddressSanitizer (ASan) intercepts all mlock
     163             :    calls.
     164             : 
     165             :    This has an interesting history.  These interceptors were first added
     166             :    in 2012 and are still present in LLVM 14.0.6:
     167             : 
     168             :      https://github.com/llvm/llvm-project/commit/71d759d392f03025bcc8b20f060bc5c22e580ea1
     169             : 
     170             :    They stub `mlock`, `munlock`, `mlockall`, `munlockall` to no-ops.
     171             : 
     172             :    ASan is known to map large amounts (~16TiB) of unbacked pages.  This
     173             :    rules out the use of `mlockall`.
     174             : 
     175             :    `mlock` only locks selected pages, therefore should be fine.  The
     176             :    comments in various revisions of these interceptors suggest that
     177             :    older Linux kernels had a bug that prevented the use of `mlock`.
     178             : 
     179             :    However, current Firedancer will use the `move_pages` syscall to
     180             :    verify whether "allocated" pages are actually backed by DRAM.
     181             : 
     182             :    This makes Firedancer and ASan incompatible unless we either
     183             : 
     184             :      1) Remove the `mlock` interceptor upstream, or
     185             :      2) Circumvent the interceptor with a raw syscall
     186             : 
     187             :    We do option 2 below */
     188             : 
     189             : int
     190             : fd_numa_mlock( void const * addr,
     191          12 :                ulong        len ) {
     192          12 :   return (int)syscall( SYS_mlock, addr, len );
     193          12 : }
     194             : 
     195             : int
     196             : fd_numa_munlock( void const * addr,
     197           0 :                  ulong        len ) {
     198           0 :   return (int)syscall( SYS_mlock, addr, len );
     199           0 : }
     200             : 
     201             : long
     202             : fd_numa_get_mempolicy( int *   mode,
     203             :                        ulong * nodemask,
     204             :                        ulong   maxnode,
     205             :                        void *  addr,
     206          12 :                        uint    flags ) {
     207          12 :   long rc = syscall( SYS_get_mempolicy, mode, nodemask, maxnode, addr, flags );
     208          12 :   if( rc==0 ) {
     209          12 :     if( mode     ) fd_msan_unpoison( mode, sizeof(int) );
     210          12 :     if( nodemask ) fd_msan_unpoison( nodemask, 8UL*((maxnode+63UL)/64UL) );
     211          12 :   }
     212          12 :   return rc;
     213          12 : }
     214             : 
     215             : long
     216             : fd_numa_set_mempolicy( int           mode,
     217             :                        ulong const * nodemask,
     218          24 :                        ulong         maxnode ) {
     219          24 :   return syscall( SYS_set_mempolicy, mode, nodemask, maxnode );
     220          24 : }
     221             : 
     222             : long
     223             : fd_numa_mbind( void *        addr,
     224             :                ulong         len,
     225             :                int           mode,
     226             :                ulong const * nodemask,
     227             :                ulong         maxnode,
     228          12 :                uint          flags ) {
     229          12 :   return syscall( SYS_mbind, addr, len, mode, nodemask, maxnode, flags );
     230          12 : }
     231             : 
     232             : long
     233             : fd_numa_move_pages( int         pid,
     234             :                     ulong       count,
     235             :                     void **     pages,
     236             :                     int const * nodes,
     237             :                     int *       status,
     238          12 :                     int         flags ) {
     239          12 :   long rc = syscall( SYS_move_pages, pid, count, pages, nodes, status, flags );
     240          12 :   if( rc==0 ) fd_msan_unpoison( status, count*sizeof(int) );
     241          12 :   return rc;
     242          12 : }

Generated by: LCOV version 1.14