Line data Source code
1 : #include "fd_cpu_topo.h"
2 :
3 : #include "../../util/shmem/fd_shmem_private.h"
4 :
5 : #include <errno.h>
6 : #include <unistd.h>
7 : #include <fcntl.h>
8 : #include <stdio.h>
9 : #include <stdlib.h>
10 :
11 : static uint
12 : read_uint_file( char const * path,
13 0 : char const * errmsg_enoent ) {
14 0 : FILE * fp = fopen( path, "r" );
15 0 : if( FD_UNLIKELY( !fp ) ) {
16 0 : if( FD_LIKELY( errno==ENOENT ) ) FD_LOG_ERR(( "%s fopen failed `%s` (%i-%s)", errmsg_enoent, path, errno, fd_io_strerror( errno ) ));
17 0 : else FD_LOG_ERR(( "fopen failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
18 0 : }
19 :
20 0 : uint value = 0U;
21 0 : if( FD_UNLIKELY( 1!=fscanf( fp, "%u\n", &value ) ) ) FD_LOG_ERR(( "failed to read uint from `%s`", path ));
22 0 : if( FD_UNLIKELY( fclose( fp ) ) ) FD_LOG_ERR(( "fclose failed `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
23 0 : return value;
24 0 : }
25 :
26 : static ulong
27 0 : fd_topo_cpu_cnt( void ) {
28 0 : char path[ PATH_MAX ];
29 0 : fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/devices/system/cpu/present" );
30 :
31 0 : char line[ 128 ];
32 0 : int fd = open( path, O_RDONLY );
33 0 : if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "open( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
34 :
35 0 : long bytes_read = read( fd, line, sizeof( line ) );
36 0 : if( FD_UNLIKELY( -1==bytes_read ) ) FD_LOG_ERR(( "read( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
37 0 : else if ( FD_UNLIKELY( (ulong)bytes_read>=sizeof( line ) ) ) FD_LOG_ERR(( "read( \"%s\" ) failed: buffer too small", path ));
38 :
39 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
40 :
41 0 : line[ bytes_read ] = '\0';
42 0 : char * saveptr;
43 0 : char * token = strtok_r( line, "-", &saveptr );
44 0 : token = strtok_r( NULL, "-", &saveptr );
45 0 : ulong end = fd_cstr_to_ulong( token );
46 :
47 0 : return end+1UL;
48 0 : }
49 :
50 : /* Return the sibling CPU (hyperthreaded pair) of the provided CPU, if
51 : there is one, otherwise return ULONG_MAX. On error, logs an error
52 : and exits the process. */
53 :
54 : ulong
55 0 : fd_topob_sibling_idx( ulong cpu_idx ) {
56 0 : char path[ PATH_MAX ];
57 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/devices/system/cpu/cpu%lu/topology/thread_siblings_list", cpu_idx ) );
58 :
59 0 : int fd = open( path, O_RDONLY );
60 0 : if( FD_UNLIKELY( -1==fd ) ) FD_LOG_ERR(( "open( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
61 :
62 0 : char line[ 1024 ] = {0};
63 0 : long bytes_read = read( fd, line, sizeof( line ) );
64 0 : if( FD_UNLIKELY( -1==bytes_read ) ) FD_LOG_ERR(( "read( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
65 0 : else if ( FD_UNLIKELY( (ulong)bytes_read>=sizeof( line ) ) ) FD_LOG_ERR(( "read( \"%s\" ) failed: buffer too small", path ));
66 :
67 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close( \"%s\" ) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
68 :
69 0 : char * sep = strchr( line, ',' );
70 0 : if( FD_UNLIKELY( !sep ) ) return ULONG_MAX;
71 :
72 0 : *sep = '\0';
73 0 : errno = 0;
74 0 : char * endptr;
75 0 : ulong pair1 = strtoul( line, &endptr, 10 );
76 0 : if( FD_UNLIKELY( *endptr!='\0' || errno==ERANGE || errno==EINVAL ) ) FD_LOG_ERR(( "failed to parse cpu siblings list of cpu%lu `%s`", cpu_idx, line ));
77 :
78 0 : ulong pair2 = strtoul( sep+1UL, &endptr, 10 );
79 0 : if( FD_UNLIKELY( *endptr!='\n' || errno==ERANGE || errno==EINVAL ) ) FD_LOG_ERR(( "failed to parse cpu siblings list of cpu%lu `%s`", pair1, sep+1UL ));
80 :
81 0 : if( FD_LIKELY( pair1==cpu_idx ) ) return pair2;
82 0 : else if( FD_LIKELY( pair2==cpu_idx ) ) return pair1;
83 0 : else FD_LOG_ERR(( "failed to find sibling of cpu%lu", cpu_idx ));
84 0 : }
85 :
86 : static int
87 0 : fd_topo_cpus_online( ulong cpu_idx ) {
88 0 : if( FD_UNLIKELY( cpu_idx==0UL ) ) return 1; /* Cannot set cpu0 to offline */
89 :
90 0 : char path[ PATH_MAX ];
91 0 : FD_TEST( fd_cstr_printf_check( path, sizeof( path ), NULL, "/sys/devices/system/cpu/cpu%lu/online", cpu_idx ) );
92 0 : return (int)read_uint_file( path, "error reading cpu online status" );
93 0 : }
94 :
95 : void
96 0 : fd_topo_cpus_init( fd_topo_cpus_t * cpus ) {
97 0 : cpus->numa_node_cnt = fd_numa_node_cnt();
98 0 : cpus->cpu_cnt = fd_topo_cpu_cnt();
99 :
100 0 : for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
101 0 : cpus->cpu[ i ].idx = i;
102 0 : cpus->cpu[ i ].online = fd_topo_cpus_online( i );
103 0 : cpus->cpu[ i ].numa_node = fd_numa_node_idx( i );
104 0 : if( FD_LIKELY( cpus->cpu[ i ].online ) ) cpus->cpu[ i ].sibling = fd_topob_sibling_idx( i );
105 0 : else cpus->cpu[ i ].sibling = ULONG_MAX;
106 0 : }
107 0 : }
108 :
109 : void
110 0 : fd_topo_cpus_printf( fd_topo_cpus_t * cpus ) {
111 0 : for( ulong i=0UL; i<cpus->cpu_cnt; i++ ) {
112 0 : FD_LOG_NOTICE(( "cpu%lu: online=%i sibling=%lu numa_node=%lu", i, cpus->cpu[ i ].online, cpus->cpu[ i ].sibling, cpus->cpu[ i ].numa_node ));
113 0 : }
114 0 : }
|