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 : }
|