Line data Source code
1 : #ifndef _GNU_SOURCE
2 : #define _GNU_SOURCE
3 : #endif
4 :
5 : #include <ctype.h>
6 : #include <errno.h>
7 : #include <pthread.h>
8 : #include <unistd.h>
9 : #include <sched.h>
10 : #include <syscall.h>
11 : #include <sys/resource.h>
12 : #include <sys/mman.h>
13 : #include <sys/prctl.h>
14 :
15 : #include "../sanitize/fd_sanitize.h"
16 : #include "fd_tile_private.h"
17 :
18 : /* Operating system shims ********************************************/
19 :
20 : struct fd_tile_private_cpu_config {
21 : int prio;
22 : };
23 :
24 : typedef struct fd_tile_private_cpu_config fd_tile_private_cpu_config_t;
25 :
26 : /* Configure the CPU optimally */
27 :
28 : static inline void
29 : fd_tile_private_cpu_config( fd_tile_private_cpu_config_t * save,
30 13 : ulong cpu_idx ) {
31 :
32 : /* If a floating tile, leave scheduler priority unchanged from however
33 : the thread group launcher configured it. */
34 :
35 13 : if( cpu_idx==65535UL ) {
36 1 : save->prio = INT_MIN;
37 1 : return;
38 1 : }
39 :
40 : /* Otherwise, configure high scheduler priority */
41 :
42 12 : errno = 0;
43 12 : int prio = getpriority( PRIO_PROCESS, (id_t)0 );
44 12 : if( prio==-1 && errno ) {
45 0 : FD_LOG_WARNING(( "fd_tile: getpriority failed (%i-%s)\n\t"
46 0 : "Unable to determine initial tile priority so not configuring the tile\n\t"
47 0 : "for high scheduler priority. Attempting to continue but this thread\n\t"
48 0 : "group's performance and stability might be compromised. Probably should\n\t"
49 0 : "configure 'ulimit -e 39' (or 40 and this might require adjusting\n\t"
50 0 : "/etc/security/limits.conf as superuser to nice -19 or -20 for this user)\n\t"
51 0 : "to eliminate this warning. Also consider starting this thread group\n\t"
52 0 : "with 'nice --19'.",
53 0 : errno, fd_io_strerror( errno ) ));
54 0 : save->prio = INT_MIN;
55 0 : }
56 :
57 12 : if( FD_UNLIKELY( prio!=-19 ) && FD_UNLIKELY( setpriority( PRIO_PROCESS, (id_t)0, -19 ) ) ) {
58 0 : FD_LOG_WARNING(( "fd_tile: setpriority failed (%i-%s)\n\t"
59 0 : "Unable to configure this tile for high scheduler priority. Attempting\n\t"
60 0 : "to continue but this thread group's performance and stability might be\n\t"
61 0 : "compromised. Probably should configure 'ulimit -e 39' (or 40 and this\n\t"
62 0 : "might require adjusting /etc/security/limits.conf to nice -19 or -20\n\t"
63 0 : "for this user) to eliminate this warning. Also consider starting this\n\t"
64 0 : "thread group with 'nice --19'.",
65 0 : errno, fd_io_strerror( errno ) ));
66 0 : save->prio = INT_MIN;
67 0 : return;
68 0 : }
69 :
70 12 : save->prio = prio;
71 12 : }
72 :
73 : /* Restore the CPU to the given state */
74 :
75 : static inline void
76 1 : fd_tile_private_cpu_restore( fd_tile_private_cpu_config_t * save ) {
77 1 : int prio = save->prio;
78 1 : if( FD_LIKELY( prio!=INT_MIN ) && FD_UNLIKELY( prio!=-19 ) && FD_UNLIKELY( setpriority( PRIO_PROCESS, (id_t)0, prio ) ) )
79 0 : FD_LOG_WARNING(( "fd_tile: setpriority failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
80 1 : }
81 :
82 : void *
83 : fd_tile_private_stack_new( int optimize,
84 12 : ulong cpu_idx ) { /* Ignored if optimize is not requested */
85 :
86 12 : uchar * stack = NULL;
87 :
88 12 : if( optimize ) { /* Create a NUMA and TLB optimized stack for a tile running on cpu cpu_idx */
89 :
90 12 : stack = (uchar *)
91 12 : fd_shmem_acquire( FD_SHMEM_HUGE_PAGE_SZ, (FD_TILE_PRIVATE_STACK_SZ/FD_SHMEM_HUGE_PAGE_SZ)+2UL, cpu_idx ); /* logs details */
92 :
93 12 : if( FD_LIKELY( stack ) ) { /* Make space for guard lo and guard hi */
94 :
95 12 : fd_shmem_release( stack, FD_SHMEM_HUGE_PAGE_SZ, 1UL );
96 :
97 12 : stack += FD_SHMEM_HUGE_PAGE_SZ;
98 :
99 12 : fd_shmem_release( stack + FD_TILE_PRIVATE_STACK_SZ, FD_SHMEM_HUGE_PAGE_SZ, 1UL );
100 :
101 12 : } else {
102 :
103 0 : ulong numa_idx = fd_shmem_numa_idx( cpu_idx );
104 0 : static ulong warn = 0UL;
105 0 : if( FD_LIKELY( !(warn & (1UL<<numa_idx) ) ) ) {
106 0 : FD_LOG_WARNING(( "fd_tile: fd_shmem_acquire failed\n\t"
107 0 : "There are probably not enough huge pages allocated by the OS on numa\n\t"
108 0 : "node %lu. Falling back on normal page backed stack for tile on cpu %lu\n\t"
109 0 : "and attempting to continue. Run:\n\t"
110 0 : "\techo [CNT] > /sys/devices/system/node/node%lu/hugepages/hugepages-2048kB/nr_hugepages\n\t"
111 0 : "(probably as superuser) or equivalent where [CNT] is a sufficient number\n\t"
112 0 : "huge pages to reserve on this numa node system wide and/or adjust\n\t"
113 0 : "/etc/security/limits.conf to permit this user to lock a sufficient\n\t"
114 0 : "amount of memory to eliminate this warning.",
115 0 : numa_idx, cpu_idx, numa_idx ));
116 0 : warn |= 1UL<<numa_idx;
117 0 : }
118 :
119 0 : }
120 :
121 12 : }
122 :
123 12 : if( !stack ) { /* Request for a non-optimized stack (or optimized stack creation failed above and we are falling back) */
124 :
125 0 : ulong mmap_sz = FD_TILE_PRIVATE_STACK_SZ + 2UL*FD_SHMEM_NORMAL_PAGE_SZ;
126 0 : stack = (uchar *)mmap( NULL, mmap_sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0 );
127 :
128 0 : if( FD_LIKELY( ((void *)stack)!=MAP_FAILED ) ) { /* Make space for guard lo and guard hi */
129 :
130 0 : if( FD_UNLIKELY( munmap( stack, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
131 0 : FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
132 :
133 0 : stack += FD_SHMEM_NORMAL_PAGE_SZ;
134 :
135 0 : if( FD_UNLIKELY( munmap( stack + FD_TILE_PRIVATE_STACK_SZ, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
136 0 : FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
137 :
138 0 : } else {
139 :
140 0 : FD_LOG_WARNING(( "fd_tile: mmap(NULL,%lu KiB,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) failed (%i-%s)\n\t"
141 0 : "Falling back on pthreads created stack and attempting to continue.",
142 0 : mmap_sz >> 10, errno, fd_io_strerror( errno ) ));
143 0 : return NULL;
144 :
145 0 : }
146 :
147 0 : }
148 :
149 : /* Create the guard regions in the extra space */
150 :
151 12 : void * guard_lo = (void *)(stack - FD_SHMEM_NORMAL_PAGE_SZ );
152 12 : if( FD_UNLIKELY( mmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
153 12 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_lo ) )
154 0 : FD_LOG_WARNING(( "fd_tile: mmap failed (%i-%s)\n\tAttempting to continue without stack guard lo.",
155 12 : errno, fd_io_strerror( errno ) ));
156 :
157 12 : void * guard_hi = (void *)(stack + FD_TILE_PRIVATE_STACK_SZ);
158 12 : if( FD_UNLIKELY( mmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
159 12 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_hi ) )
160 0 : FD_LOG_WARNING(( "fd_tile: mmap failed (%i-%s)\n\tAttempting to continue without stack guard hi.",
161 12 : errno, fd_io_strerror( errno ) ));
162 :
163 12 : return stack;
164 12 : }
165 :
166 : static void
167 12 : fd_tile_private_stack_delete( void * _stack ) {
168 12 : if( FD_UNLIKELY( !_stack ) ) return;
169 :
170 12 : uchar * stack = (uchar *)_stack;
171 12 : uchar * guard_lo = stack - FD_SHMEM_NORMAL_PAGE_SZ;
172 12 : uchar * guard_hi = stack + FD_TILE_PRIVATE_STACK_SZ;
173 :
174 12 : if( FD_UNLIKELY( munmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
175 0 : FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
176 :
177 12 : if( FD_UNLIKELY( munmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ ) ) )
178 0 : FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
179 :
180 : /* Note that fd_shmem_release is just a wrapper around munmap such
181 : that this covers both the optimized and non-optimized cases */
182 :
183 12 : if( FD_UNLIKELY( munmap( stack, FD_TILE_PRIVATE_STACK_SZ ) ) )
184 0 : FD_LOG_WARNING(( "fd_tile: munmap failed (%i-%s); attempting to continue", errno, fd_io_strerror( errno ) ));
185 12 : }
186 :
187 : /* Tile side APIs ****************************************************/
188 :
189 : static ulong fd_tile_private_id0; /* Zeroed at app start, initialized by the boot manager */
190 : static ulong fd_tile_private_id1; /* " */
191 : static ulong fd_tile_private_cnt; /* " */
192 :
193 12 : ulong fd_tile_id0( void ) { return fd_tile_private_id0; }
194 0 : ulong fd_tile_id1( void ) { return fd_tile_private_id1; }
195 2 : ulong fd_tile_cnt( void ) { return fd_tile_private_cnt; }
196 :
197 : static FD_TL ulong fd_tile_private_id; /* Zeroed at app/thread start, initialized by the boot / tile manager */
198 : static FD_TL ulong fd_tile_private_idx; /* " */
199 : /**/ FD_TL ulong fd_tile_private_stack0; /* " */
200 : /**/ FD_TL ulong fd_tile_private_stack1; /* " */
201 :
202 0 : ulong fd_tile_id ( void ) { return fd_tile_private_id; }
203 0 : ulong fd_tile_idx( void ) { return fd_tile_private_idx; }
204 :
205 : static ushort fd_tile_private_cpu_id[ FD_TILE_MAX ]; /* Zeroed at app start, initialized by boot */
206 :
207 : ulong
208 0 : fd_tile_cpu_id( ulong tile_idx ) {
209 0 : if( FD_UNLIKELY( tile_idx>=fd_tile_private_cnt ) ) return ULONG_MAX;
210 0 : ulong cpu_idx = (ulong)fd_tile_private_cpu_id[ tile_idx ];
211 0 : return fd_ulong_if( cpu_idx<65535UL, cpu_idx, ULONG_MAX-1UL );
212 0 : }
213 :
214 : /* This is used for the OS services to communicate information with the
215 : tile managers */
216 :
217 24 : #define FD_TILE_PRIVATE_STATE_BOOT (0) /* Tile is booting */
218 >1844*10^16 : #define FD_TILE_PRIVATE_STATE_IDLE (1) /* Tile is idle */
219 12 : #define FD_TILE_PRIVATE_STATE_EXEC (2) /* Tile is executing a task */
220 12 : #define FD_TILE_PRIVATE_STATE_HALT (3) /* Tile is halting */
221 :
222 : struct __attribute__((aligned(128))) fd_tile_private { /* Double cache line aligned to avoid aclpf false sharing */
223 : ulong id;
224 : ulong idx;
225 : int state; /* FD_TILE_PRIVATE_STATE_* */
226 : int argc;
227 : char ** argv;
228 : fd_tile_task_t task;
229 : char const * fail;
230 : int ret;
231 : };
232 :
233 : typedef struct fd_tile_private fd_tile_private_t;
234 :
235 : struct fd_tile_private_manager_args {
236 : ulong id;
237 : ulong idx;
238 : ulong cpu_idx;
239 : void * stack; /* NULL if pthread created, non-NULL if user created */
240 : ulong stack_sz;
241 : fd_tile_private_t * tile;
242 : };
243 :
244 : typedef struct fd_tile_private_manager_args fd_tile_private_manager_args_t;
245 :
246 : static void *
247 12 : fd_tile_private_manager( void * _args ) {
248 12 : fd_tile_private_manager_args_t * args = (fd_tile_private_manager_args_t *)_args;
249 :
250 : # if !__GLIBC__
251 : if( args->cpu_idx<65535UL ) {
252 : FD_CPUSET_DECL( cpu_set );
253 : fd_cpuset_insert( cpu_set, args->cpu_idx );
254 : int err = fd_cpuset_setaffinity( (pid_t)0, cpu_set );
255 : if( FD_UNLIKELY( err ) )
256 : FD_LOG_WARNING(( "fd_tile: fd_cpuset_setaffinity_failed (%i-%s)\n\t"
257 : "Unable to set the thread affinity for tile %lu to cpu %lu. Attempting to\n\t"
258 : "continue without explicitly specifying this tile's cpu affinity but it\n\t"
259 : "is likely this thread group's performance and stability are compromised\n\t"
260 : "(possibly catastrophically so). Update --tile-cpus to specify a set of\n\t"
261 : "allowed cpus that have been reserved for this thread group on this host\n\t"
262 : "to eliminate this warning.", err, fd_io_strerror( err ), args->idx, args->cpu_idx ));
263 : }
264 : # endif /* !__GLIBC__ */
265 :
266 12 : ulong id = args->id;
267 12 : ulong idx = args->idx;
268 12 : void * stack = args->stack;
269 12 : ulong stack_sz = args->stack_sz;
270 :
271 12 : char thread_name[ 20 ];
272 12 : FD_TEST( fd_cstr_printf_check( thread_name, sizeof( thread_name ), NULL, "tile:%lu", idx ) );
273 12 : if( FD_UNLIKELY( prctl( PR_SET_NAME, thread_name, 0, 0, 0 ) ) ) FD_LOG_ERR(( "prctl(PR_SET_NAME) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
274 :
275 12 : if( FD_UNLIKELY( !( (id ==fd_log_thread_id() ) &
276 12 : (idx==(id-fd_tile_private_id0) ) &
277 12 : ((fd_tile_private_id0<id) & (id<fd_tile_private_id1) ) &
278 12 : (fd_tile_private_cnt==(fd_tile_private_id1-fd_tile_private_id0)) ) ) )
279 0 : FD_LOG_ERR(( "fd_tile: internal error (unexpected thread identifiers)" ));
280 :
281 12 : fd_tile_private_t tile[1];
282 12 : FD_VOLATILE( tile->id ) = id;
283 12 : FD_VOLATILE( tile->idx ) = idx;
284 12 : FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_BOOT;
285 12 : FD_VOLATILE( tile->argc ) = 0;
286 12 : FD_VOLATILE( tile->argv ) = NULL;
287 12 : FD_VOLATILE( tile->task ) = NULL;
288 12 : FD_VOLATILE( tile->fail ) = NULL;
289 12 : FD_VOLATILE( tile->ret ) = 0;
290 :
291 : /* state is BOOT ... configure the tile, transition to IDLE and then
292 : start polling for tasks */
293 :
294 12 : fd_tile_private_id = id;
295 12 : fd_tile_private_idx = idx;
296 :
297 12 : if( FD_LIKELY( stack ) ) { /* User provided stack */
298 12 : fd_tile_private_stack0 = (ulong)stack;
299 12 : fd_tile_private_stack1 = (ulong)stack + stack_sz;
300 :
301 : /* Prevent another fork() from smashing the stack */
302 12 : if( FD_UNLIKELY( madvise( stack, FD_TILE_PRIVATE_STACK_SZ, MADV_DONTFORK ) ) ) {
303 0 : FD_LOG_ERR(( "madvise(stack,MADV_DONTFORK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
304 0 : }
305 :
306 12 : } else { /* Pthread provided stack */
307 0 : fd_log_private_stack_discover( stack_sz, &fd_tile_private_stack0, &fd_tile_private_stack1 ); /* logs details */
308 0 : if( FD_UNLIKELY( !fd_tile_private_stack0 ) )
309 0 : FD_LOG_WARNING(( "stack diagnostics not available on this tile; attempting to continue" ));
310 0 : }
311 :
312 12 : fd_tile_private_cpu_config_t dummy[1];
313 12 : fd_tile_private_cpu_config( dummy, args->cpu_idx );
314 :
315 12 : ulong app_id = fd_log_app_id();
316 12 : FD_LOG_INFO(( "fd_tile: boot tile %lu success (thread %lu:%lu in thread group %lu:%lu/%lu)",
317 12 : idx, app_id, id, app_id, fd_tile_private_id0, fd_tile_private_cnt ));
318 :
319 12 : FD_COMPILER_MFENCE();
320 12 : FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_IDLE;
321 12 : FD_VOLATILE( args->tile ) = tile;
322 :
323 10063223 : for(;;) {
324 :
325 : /* We are awake ... see what we should do next */
326 :
327 10063223 : int state = FD_VOLATILE_CONST( tile->state );
328 10094495 : if( FD_UNLIKELY( state!=FD_TILE_PRIVATE_STATE_EXEC ) ) {
329 10094495 : if( FD_UNLIKELY( state!=FD_TILE_PRIVATE_STATE_IDLE ) ) break;
330 : /* state is IDLE ... try again */
331 10094489 : FD_SPIN_PAUSE();
332 10094489 : continue;
333 10094495 : }
334 :
335 : /* state is EXEC ... the run assigned task and then
336 : transition to IDLE when done */
337 :
338 >1844*10^16 : int argc = FD_VOLATILE_CONST( tile->argc );
339 >1844*10^16 : char ** argv = FD_VOLATILE_CONST( tile->argv );
340 >1844*10^16 : fd_tile_task_t task = FD_VOLATILE_CONST( tile->task );
341 >1844*10^16 : FD_VOLATILE( tile->ret ) = task( argc, argv );
342 >1844*10^16 : FD_VOLATILE( tile->fail ) = NULL;
343 :
344 >1844*10^16 : FD_COMPILER_MFENCE();
345 >1844*10^16 : FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_IDLE;
346 >1844*10^16 : }
347 :
348 : /* state is HALT, clean up and then reset back to BOOT */
349 :
350 12 : FD_LOG_INFO(( "fd_tile: halting tile %lu", idx ));
351 :
352 12 : FD_COMPILER_MFENCE();
353 12 : FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_BOOT;
354 12 : return stack;
355 12 : }
356 :
357 : /* Dispatch side APIs ************************************************/
358 :
359 : static struct __attribute__((aligned(128))) { /* Each on its own cache line pair to limit false sharing in parallel dispatch */
360 : fd_tile_private_t * lock; /* Non-NULL if tile idx is available for dispatch, ==tile otherwise */
361 : fd_tile_private_t * tile;
362 : pthread_t pthread;
363 : } fd_tile_private[ FD_TILE_MAX ];
364 :
365 : /* FIXME: ATOMIC_XCHG BASED INSTEAD? */
366 : static inline fd_tile_private_t *
367 12 : fd_tile_private_trylock( ulong tile_idx ) {
368 12 : fd_tile_private_t * volatile * vtile = (fd_tile_private_t * volatile *)&fd_tile_private[ tile_idx ].lock;
369 12 : fd_tile_private_t * tile = *vtile;
370 12 : if( FD_LIKELY( tile ) && FD_LIKELY( FD_ATOMIC_CAS( vtile, tile, NULL )==tile ) ) return tile;
371 0 : return NULL;
372 12 : }
373 :
374 : static inline fd_tile_private_t *
375 12 : fd_tile_private_lock( ulong tile_idx ) {
376 12 : fd_tile_private_t * volatile * vtile = (fd_tile_private_t * volatile *)&fd_tile_private[ tile_idx ].lock;
377 12 : fd_tile_private_t * tile;
378 12 : for(;;) {
379 12 : tile = *vtile;
380 12 : if( FD_LIKELY( tile ) && FD_LIKELY( FD_ATOMIC_CAS( vtile, tile, NULL )==tile ) ) break;
381 0 : FD_SPIN_PAUSE();
382 0 : }
383 12 : return tile;
384 12 : }
385 :
386 : static inline void
387 : fd_tile_private_unlock( ulong tile_idx,
388 24 : fd_tile_private_t * tile ) {
389 24 : FD_VOLATILE( fd_tile_private[ tile_idx ].lock ) = tile;
390 24 : }
391 :
392 : fd_tile_exec_t *
393 : fd_tile_exec_new( ulong idx,
394 : fd_tile_task_t task,
395 : int argc,
396 12 : char ** argv ) {
397 12 : if( FD_UNLIKELY( (idx==fd_tile_private_idx) | (!idx) ) ) return NULL; /* Can't dispatch to self or to tile 0 */
398 :
399 12 : fd_tile_private_t * tile = fd_tile_private_trylock( idx );
400 12 : if( FD_UNLIKELY( !tile ) ) return NULL;
401 :
402 : /* Exec holds the lock and tile state is idle here */
403 12 : FD_VOLATILE( tile->argc ) = argc;
404 12 : FD_VOLATILE( tile->argv ) = argv;
405 12 : FD_VOLATILE( tile->task ) = task;
406 12 : FD_COMPILER_MFENCE();
407 12 : FD_VOLATILE( tile->state ) = FD_TILE_PRIVATE_STATE_EXEC;
408 12 : return (fd_tile_exec_t *)tile;
409 12 : }
410 :
411 : char const *
412 : fd_tile_exec_delete( fd_tile_exec_t * exec,
413 12 : int * opt_ret ) {
414 12 : fd_tile_private_t * tile = (fd_tile_private_t *)exec;
415 12 : ulong tile_idx = tile->idx;
416 :
417 12 : int state;
418 42872 : for(;;) {
419 42872 : state = FD_VOLATILE_CONST( tile->state );
420 42872 : if( FD_LIKELY( state==FD_TILE_PRIVATE_STATE_IDLE ) ) break;
421 42860 : FD_SPIN_PAUSE();
422 42860 : }
423 : /* state is IDLE at this point */
424 12 : char const * fail = FD_VOLATILE_CONST( tile->fail );
425 12 : if( FD_LIKELY( (!fail) & (!!opt_ret) ) ) *opt_ret = FD_VOLATILE_CONST( tile->ret );
426 12 : fd_tile_private_unlock( tile_idx, tile );
427 12 : return fail;
428 12 : }
429 :
430 12 : fd_tile_exec_t * fd_tile_exec( ulong tile_idx ) { return (fd_tile_exec_t *)fd_tile_private[ tile_idx ].tile; }
431 :
432 0 : ulong fd_tile_exec_id ( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->id; }
433 0 : ulong fd_tile_exec_idx ( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->idx; }
434 0 : fd_tile_task_t fd_tile_exec_task( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->task; }
435 0 : int fd_tile_exec_argc( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->argc; }
436 0 : char ** fd_tile_exec_argv( fd_tile_exec_t const * exec ) { return ((fd_tile_private_t const *)exec)->argv; }
437 :
438 : int
439 0 : fd_tile_exec_done( fd_tile_exec_t const * exec ) {
440 0 : fd_tile_private_t const * tile = (fd_tile_private_t const *)exec;
441 0 : return FD_VOLATILE_CONST( tile->state )==FD_TILE_PRIVATE_STATE_IDLE;
442 0 : }
443 :
444 : /* Boot/halt APIs ****************************************************/
445 :
446 : /* Parse a list of cpu tiles */
447 :
448 : FD_STATIC_ASSERT( FD_TILE_MAX<65535, update_tile_to_cpu_type );
449 :
450 : ulong
451 : fd_tile_private_cpus_parse( char const * cstr,
452 1 : ushort * tile_to_cpu ) {
453 1 : if( !cstr ) return 0UL;
454 1 : ulong cnt = 0UL;
455 :
456 1 : FD_CPUSET_DECL( assigned_set );
457 :
458 1 : char const * p = cstr;
459 3 : for(;;) {
460 :
461 3 : while( fd_isspace( (int)p[0] ) ) p++; /* Munch whitespace */
462 :
463 3 : if( p[0]=='f' ) { /* These tiles have been requested to float on the original core set */
464 1 : p++;
465 :
466 1 : ulong float_cnt;
467 :
468 1 : while( fd_isspace( (int)p[0] ) ) p++; /* Munch whitespace */
469 1 : if ( p[0]==',' ) float_cnt = 1UL, p++;
470 0 : else if( p[0]=='\0' ) float_cnt = 1UL;
471 0 : else if( !fd_isdigit( (int)p[0] ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (malformed count)" ));
472 0 : else {
473 0 : float_cnt = fd_cstr_to_ulong( p );
474 0 : if( FD_UNLIKELY( !float_cnt ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (bad count)" ));
475 0 : p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
476 0 : while( fd_isspace( (int)p[0] ) ) p++; /* Munch whitespace */
477 0 : if( FD_UNLIKELY( !( p[0]==',' || p[0]=='\0' ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (bad count delimiter)" ));
478 0 : if( p[0]==',' ) p++;
479 0 : }
480 :
481 : /* float_cnt is at least 1 at this point */
482 1 : do {
483 1 : if( FD_UNLIKELY( cnt>=FD_TILE_MAX ) ) FD_LOG_ERR(( "fd_tile: too many --tile-cpus" ));
484 1 : tile_to_cpu[ cnt++ ] = (ushort)65535;
485 1 : } while( --float_cnt );
486 :
487 1 : continue;
488 1 : }
489 :
490 2 : if( !fd_isdigit( (int)p[0] ) ) {
491 1 : if( FD_UNLIKELY( p[0]!='\0' ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (range lo not a cpu)" ));
492 1 : break;
493 1 : }
494 1 : ulong cpu0 = fd_cstr_to_ulong( p );
495 1 : ulong cpu1 = cpu0;
496 1 : ulong stride = 1UL;
497 1 : int rev = 0; /* Nh lists N first */
498 1 : p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
499 1 : while( fd_isspace( (int)p[0] ) ) p++;
500 1 : if( p[0]=='-' ) {
501 1 : p++;
502 1 : while( fd_isspace( (int)p[0] ) ) p++;
503 1 : if( FD_UNLIKELY( !fd_isdigit( (int)p[0] ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (range hi not a cpu)" ));
504 1 : cpu1 = fd_cstr_to_ulong( p );
505 2 : p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
506 1 : while( fd_isspace( (int)p[0] ) ) p++;
507 1 : if( p[0]=='/' || p[0]==':' ) {
508 0 : p++;
509 0 : while( fd_isspace( (int)p[0] ) ) p++;
510 0 : if( FD_UNLIKELY( !fd_isdigit( (int)p[0] ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (stride not an int)" ));
511 0 : stride = fd_cstr_to_ulong( p );
512 0 : p++; while( fd_isdigit( (int)p[0] ) ) p++; /* FIXME: USE STRTOUL ENDPTR FOR CORRECT HANDLING OF NON-BASE-10 */
513 0 : }
514 1 : }
515 0 : else if( p[0]=='h' ) {
516 : /* `Nh` is a shorthand for "core N and its hyperthread sibling".
517 : With M the sibling id it translates as "N,M".
518 : If core N doesn't have a sibling, the `h` suffix is ignored.
519 : This way an expression like "0h" also works on systems where
520 : hyperthread is not available. */
521 0 : p++;
522 0 : ulong sibling = fd_tile_private_sibling_idx( cpu0 );
523 0 : if( sibling!=ULONG_MAX ) {
524 0 : rev = sibling<cpu0;
525 0 : cpu1 = fd_ulong_max( cpu0, sibling );
526 0 : cpu0 = fd_ulong_min( cpu0, sibling );
527 0 : stride = cpu1-cpu0;
528 0 : }
529 0 : }
530 1 : while( fd_isspace( (int)p[0] ) ) p++;
531 1 : if( FD_UNLIKELY( !( p[0]==',' || p[0]=='\0' ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (bad range delimiter)" ));
532 1 : if( p[0]==',' ) p++;
533 1 : cpu1++;
534 1 : if( FD_UNLIKELY( cpu1<=cpu0 ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (invalid range)" ));
535 1 : if( FD_UNLIKELY( !stride ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (invalid stride)" ));
536 :
537 13 : for( ulong cpu=cpu0; cpu<cpu1; cpu+=stride ) {
538 12 : if( FD_UNLIKELY( cnt>=FD_TILE_MAX ) ) FD_LOG_ERR(( "fd_tile: too many --tile-cpus" ));
539 12 : if( FD_UNLIKELY( cpu>=FD_TILE_MAX ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (cpu index too large)" ));
540 12 : if( FD_UNLIKELY( fd_cpuset_test( assigned_set, cpu ) ) ) FD_LOG_ERR(( "fd_tile: malformed --tile-cpus (repeated cpu)" ));
541 12 : tile_to_cpu[ cnt++ ] = (ushort)cpu;
542 12 : fd_cpuset_insert( assigned_set, cpu );
543 12 : }
544 1 : if( rev ) {
545 0 : ushort t = tile_to_cpu[ cnt-2UL ];
546 0 : tile_to_cpu[ cnt-2UL ] = tile_to_cpu[ cnt-1UL ];
547 0 : tile_to_cpu[ cnt-1UL ] = t;
548 0 : }
549 1 : }
550 :
551 1 : return cnt;
552 1 : }
553 :
554 : static fd_tile_private_cpu_config_t fd_tile_private_cpu_config_save[1];
555 :
556 : void
557 : fd_tile_private_map_boot( ushort * tile_to_cpu,
558 1 : ulong tile_cnt ) {
559 1 : fd_tile_private_id0 = fd_log_thread_id();
560 1 : fd_tile_private_id1 = fd_tile_private_id0 + tile_cnt;
561 1 : fd_tile_private_cnt = tile_cnt;
562 :
563 1 : ulong app_id = fd_log_app_id();
564 1 : ulong host_id = fd_log_host_id();
565 1 : FD_LOG_INFO(( "fd_tile: booting thread group %lu:%lu/%lu", app_id, fd_tile_private_id0, fd_tile_private_cnt ));
566 :
567 : /* We create the tiles [1,tile_cnt) first so that any floating tiles
568 : in this inherit the appropriate scheduler priorities and affinities
569 : from the thread group launcher. */
570 :
571 13 : for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) {
572 :
573 12 : ulong cpu_idx = (ulong)tile_to_cpu[ tile_idx ];
574 12 : int fixed = (cpu_idx<65535UL);
575 :
576 12 : if( fixed ) FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:%lu", tile_idx, host_id, cpu_idx ));
577 0 : else FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:float", tile_idx, host_id ));
578 :
579 12 : pthread_attr_t attr[1];
580 12 : int err = pthread_attr_init( attr );
581 12 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_tile: pthread_attr_init failed (%i-%s) for tile %lu.\n\t",
582 12 : err, fd_io_strerror( err ), tile_idx ));
583 :
584 : /* Set affinity ahead of time. This is a GNU-specific extension
585 : that is not available on musl. On musl, we just skip this
586 : step as we call sched_setaffinity(2) later on regardless. */
587 :
588 12 : # if __GLIBC__
589 12 : if( fixed ) {
590 12 : FD_CPUSET_DECL( cpu_set );
591 12 : fd_cpuset_insert( cpu_set, cpu_idx );
592 12 : err = pthread_attr_setaffinity_np( attr, fd_cpuset_footprint(), (cpu_set_t const *)fd_type_pun_const( cpu_set ) );
593 12 : if( FD_UNLIKELY( err ) ) FD_LOG_WARNING(( "fd_tile: pthread_attr_setaffinity_failed (%i-%s)\n\t"
594 12 : "Unable to set the thread affinity for tile %lu on cpu %lu. Attempting to\n\t"
595 12 : "continue without explicitly specifying this cpu's thread affinity but it\n\t"
596 12 : "is likely this thread group's performance and stability are compromised\n\t"
597 12 : "(possibly catastrophically so). Update --tile-cpus to specify a set of\n\t"
598 12 : "allowed cpus that have been reserved for this thread group on this host\n\t"
599 12 : "to eliminate this warning.",
600 12 : err, fd_io_strerror( err ), tile_idx, cpu_idx ));
601 12 : }
602 12 : # endif /* __GLIBC__ */
603 :
604 : /* Create an optimized stack with guard regions if the build target
605 : is x86 (e.g. supports huge pages necessary to optimize TLB usage)
606 : and the tile is assigned to a particular CPU (e.g. bind the stack
607 : memory to the NUMA node closest to the cpu).
608 :
609 : Otherwise (or if an optimized stack could not be created), create
610 : vanilla pthread-style stack with guard regions. We DIY here
611 : because pthreads seems to be missing an API to determine the
612 : extents of the stacks it creates and we need to know the stack
613 : extents for run-time stack diagnostics. Though we can use
614 : fd_log_private_stack_discover to determine stack extents after
615 : the thread is started, it is faster, more flexible, more reliable
616 : and more portable to use a user specified stack when possible.
617 :
618 : If neither can be done, we will let pthreads create the tile's
619 : stack and try to discover the stack extents after the thread is
620 : started. */
621 :
622 12 : int optimize = FD_HAS_X86 & fixed;
623 :
624 12 : void * stack = fd_tile_private_stack_new( optimize, cpu_idx );
625 12 : if( FD_LIKELY( stack ) ) {
626 12 : err = pthread_attr_setstack( attr, stack, FD_TILE_PRIVATE_STACK_SZ );
627 12 : if( FD_UNLIKELY( err ) ) {
628 0 : FD_LOG_WARNING(( "fd_tile: pthread_attr_setstack failed (%i-%s)\n\t", err, fd_io_strerror( err ) ));
629 0 : fd_tile_private_stack_delete( stack );
630 0 : stack = NULL;
631 0 : }
632 12 : }
633 :
634 12 : if( FD_UNLIKELY( !stack ) ) FD_LOG_WARNING(( "fd_tile: Unable to create a stack for tile %lu.\n\t"
635 12 : "Attempting to continue with the default stack but it is likely this\n\t"
636 12 : "thread group's performance and stability is compromised (possibly\n\t"
637 12 : "catastrophically so).",
638 12 : tile_idx ));
639 :
640 12 : ulong stack_sz;
641 12 : err = pthread_attr_getstacksize( attr, &stack_sz );
642 12 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_tile: pthread_attr_getstacksize failed (%i-%s) for tile %lu.\n\t",
643 12 : err, fd_io_strerror( err ), tile_idx ));
644 :
645 12 : FD_VOLATILE( fd_tile_private[ tile_idx ].lock ) = NULL;
646 :
647 12 : fd_tile_private_manager_args_t args[1];
648 :
649 12 : FD_VOLATILE( args->id ) = fd_tile_private_id0 + tile_idx;
650 12 : FD_VOLATILE( args->idx ) = tile_idx;
651 12 : FD_VOLATILE( args->cpu_idx ) = cpu_idx;
652 12 : FD_VOLATILE( args->stack ) = stack;
653 12 : FD_VOLATILE( args->stack_sz ) = stack_sz;
654 12 : FD_VOLATILE( args->tile ) = NULL;
655 :
656 12 : FD_COMPILER_MFENCE();
657 :
658 12 : err = pthread_create( &fd_tile_private[tile_idx].pthread, attr, fd_tile_private_manager, args );
659 12 : if( FD_UNLIKELY( err ) ) {
660 0 : if( fixed ) FD_LOG_ERR(( "fd_tile: pthread_create failed (%i-%s)\n\t"
661 0 : "Unable to start up the tile %lu on cpu %lu. Likely causes for this include\n\t"
662 0 : "this cpu is restricted from the user or does not exist on this host.\n\t"
663 0 : "Update --tile-cpus to specify a set of allowed cpus that have been reserved\n\t"
664 0 : "for this thread group on this host.",
665 0 : err, fd_io_strerror( err ), tile_idx, cpu_idx ));
666 0 : FD_LOG_ERR(( "fd_tile: pthread_create failed (%i-%s)\n\tUnable to start up the tile %lu (floating).",
667 0 : err, fd_io_strerror( err ), tile_idx ));
668 0 : }
669 :
670 : /* Wait for the tile to be ready to exec */
671 :
672 12 : fd_tile_private_t * tile;
673 1231 : for(;;) {
674 1231 : tile = FD_VOLATILE_CONST( args->tile );
675 1231 : if( FD_LIKELY( tile ) ) break;
676 1219 : FD_YIELD();
677 1219 : }
678 12 : FD_VOLATILE( fd_tile_private[ tile_idx ].tile ) = tile;
679 12 : FD_VOLATILE( fd_tile_private[ tile_idx ].lock ) = tile;
680 :
681 : /* Tile is running, args is safe to reuse */
682 :
683 12 : err = pthread_attr_destroy( attr );
684 12 : if( FD_UNLIKELY( err ) )
685 0 : FD_LOG_WARNING(( "fd_tile: pthread_attr_destroy failed (%i-%s) for tile %lu; attempting to continue",
686 12 : err, fd_io_strerror( err ), tile_idx ));
687 12 : }
688 :
689 : /* And now we "boot" tile 0 */
690 :
691 1 : ulong cpu_idx = (ulong)tile_to_cpu[ 0UL ];
692 1 : int fixed = (cpu_idx<65535UL);
693 1 : if( fixed ) FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:%lu", 0UL, host_id, cpu_idx ));
694 1 : else FD_LOG_INFO(( "fd tile: booting tile %lu on cpu %lu:float", 0UL, host_id ));
695 :
696 1 : if( fixed ) {
697 :
698 0 : int good_taskset;
699 0 : FD_CPUSET_DECL( cpu_set );
700 0 : if( FD_UNLIKELY( fd_cpuset_getaffinity( (pid_t)0, cpu_set ) ) ) {
701 0 : FD_LOG_WARNING(( "fd_tile: fd_cpuset_getaffinity failed (%i-%s) for tile 0 on cpu %lu",
702 0 : errno, fd_io_strerror( errno ), cpu_idx ));
703 0 : good_taskset = 0;
704 0 : } else {
705 0 : ulong cnt = fd_cpuset_cnt( cpu_set );
706 0 : ulong idx = fd_cpuset_first( cpu_set );
707 0 : good_taskset = (cnt==1UL) & (idx==cpu_idx);
708 0 : }
709 :
710 0 : if( FD_UNLIKELY( !good_taskset ) ) {
711 0 : FD_LOG_WARNING(( "fd_tile: --tile-cpus for tile 0 may not match initial kernel affinity\n\t"
712 0 : "Tile 0 might not be fully optimized because of kernel first touch.\n\t"
713 0 : "Overriding fd_log_cpu_id(), fd_log_cpu(), fd_log_thread() on tile 0 to\n\t"
714 0 : "match --tile-cpus and attempting to continue. Launch this thread\n\t"
715 0 : "group via 'taskset -c %lu' or equivalent to eliminate this warning.", cpu_idx ));
716 0 : fd_cpuset_null( cpu_set );
717 0 : fd_cpuset_insert( cpu_set, cpu_idx );
718 0 : if( FD_UNLIKELY( fd_cpuset_setaffinity( (pid_t)0, cpu_set ) ) )
719 0 : FD_LOG_WARNING(( "fd_tile: fd_cpuset_setaffinity_failed (%i-%s)\n\t"
720 0 : "Unable to set the thread affinity for tile 0 on cpu %lu. Attempting to\n\t"
721 0 : "continue without explicitly specifying this cpu's thread affinity but it\n\t"
722 0 : "is likely this thread group's performance and stability are compromised\n\t"
723 0 : "(possibly catastrophically so). Update --tile-cpus to specify a set of\n\t"
724 0 : "allowed cpus that have been reserved for this thread group on this host\n\t"
725 0 : "to eliminate this warning.",
726 0 : errno, fd_io_strerror( errno ), cpu_idx ));
727 0 : fd_log_private_cpu_id_set( cpu_idx );
728 0 : fd_log_cpu_set ( NULL );
729 0 : fd_log_thread_set( NULL );
730 0 : }
731 0 : }
732 :
733 : /* Tile 0 "pthread_create" */
734 1 : fd_tile_private[0].pthread = pthread_self();
735 : /* FIXME: ON X86, DETECT IF TILE 0 STACK ISN'T HUGE PAGE AND WARN AS NECESSARY? */
736 :
737 : /* Tile 0 "thread manager init" */
738 1 : fd_tile_private_id = fd_tile_private_id0;
739 1 : fd_tile_private_idx = 0UL;
740 :
741 1 : # if !FD_HAS_ASAN
742 1 : fd_log_private_stack_discover( fd_log_private_main_stack_sz(),
743 1 : &fd_tile_private_stack0, &fd_tile_private_stack1 ); /* logs details */
744 1 : if( FD_UNLIKELY( !fd_tile_private_stack0 ) )
745 0 : FD_LOG_WARNING(( "stack diagnostics not available on this tile; attempting to continue" ));
746 1 : # endif /* FD_HAS_ASAN */
747 :
748 1 : fd_tile_private_cpu_config( fd_tile_private_cpu_config_save, cpu_idx );
749 1 : fd_tile_private[0].lock = NULL; /* Can't dispatch to tile 0 */
750 1 : fd_tile_private[0].tile = NULL; /* " */
751 :
752 1 : FD_LOG_INFO(( "fd_tile: boot tile %lu success (thread %lu:%lu in thread group %lu:%lu/%lu)",
753 1 : fd_tile_private_idx, app_id, fd_tile_private_id, app_id, fd_tile_private_id0, fd_tile_private_cnt ));
754 :
755 1 : fd_memcpy( fd_tile_private_cpu_id, tile_to_cpu, fd_tile_private_cnt*sizeof(ushort) );
756 :
757 1 : FD_LOG_INFO(( "fd_tile: boot success" ));
758 1 : }
759 :
760 : void
761 1 : fd_tile_private_boot_str( char const * cpus ) {
762 1 : ushort tile_to_cpu[ FD_TILE_MAX ];
763 1 : ulong tile_cnt = fd_tile_private_cpus_parse( cpus, tile_to_cpu );
764 :
765 1 : if( FD_UNLIKELY( !tile_cnt ) ) {
766 0 : FD_LOG_INFO(( "fd_tile: no cpus specified; treating thread group as single tile running on O/S assigned cpu(s)" ));
767 0 : tile_to_cpu[0] = (ushort)65535;
768 0 : tile_cnt = 1UL;
769 0 : }
770 :
771 1 : fd_tile_private_map_boot( tile_to_cpu, tile_cnt );
772 1 : }
773 :
774 : void
775 : fd_tile_private_boot( int * pargc,
776 1 : char *** pargv ) {
777 : /* Extract the tile configuration from the command line */
778 :
779 1 : char const * cpus = fd_env_strip_cmdline_cstr( pargc, pargv, "--tile-cpus", "FD_TILE_CPUS", NULL );
780 :
781 1 : if( !cpus ) FD_LOG_INFO(( "fd_tile: --tile-cpus not specified" ));
782 1 : else FD_LOG_INFO(( "fd_tile: --tile-cpus \"%s\"", cpus ));
783 :
784 1 : fd_tile_private_boot_str( cpus );
785 1 : }
786 :
787 : void
788 1 : fd_tile_private_halt( void ) {
789 1 : FD_LOG_INFO(( "fd_tile: halt" ));
790 :
791 1 : fd_memset( fd_tile_private_cpu_id, 0, fd_tile_private_cnt*sizeof(ushort) );
792 :
793 1 : ulong tile_cnt = fd_tile_private_cnt;
794 :
795 1 : fd_tile_private_t * tile[ FD_TILE_MAX ]; /* FIXME: ALLOCA TO TILE_CNT? */
796 :
797 1 : FD_LOG_INFO(( "fd_tile: disabling dispatch" ));
798 13 : for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) tile[ tile_idx ] = fd_tile_private_lock( tile_idx );
799 : /* All tile to tile dispatches will fail at this point */
800 :
801 1 : FD_LOG_INFO(( "fd_tile: waiting for all tasks to complete" ));
802 13 : for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ )
803 12 : while( FD_VOLATILE_CONST( tile[ tile_idx ]->state )!=FD_TILE_PRIVATE_STATE_IDLE ) FD_YIELD();
804 : /* All halt transitions will be valid at this point */
805 :
806 1 : FD_LOG_INFO(( "fd_tile: signaling all tiles to halt" ));
807 13 : for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) FD_VOLATILE( tile[ tile_idx ]->state ) = FD_TILE_PRIVATE_STATE_HALT;
808 : /* All tiles are halting at this point. tile[*] is no longer safe */
809 :
810 1 : FD_LOG_INFO(( "fd_tile: waiting for all tiles to halt" ));
811 13 : for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) {
812 12 : void * stack;
813 12 : int err = pthread_join( fd_tile_private[ tile_idx ].pthread, &stack );
814 12 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_tile: pthread_join failed (%i-%s)", err, fd_io_strerror( err ) ));
815 12 : fd_tile_private_stack_delete( stack );
816 12 : FD_LOG_INFO(( "fd_tile: halt tile %lu success", tile_idx ));
817 12 : }
818 :
819 : /* All tiles but this one are halted at this point */
820 :
821 1 : fd_tile_private_cpu_restore( fd_tile_private_cpu_config_save );
822 :
823 1 : FD_LOG_INFO(( "fd_tile: halt tile 0 success" ));
824 :
825 1 : FD_LOG_INFO(( "fd_tile: cleaning up" ));
826 :
827 13 : for( ulong tile_idx=1UL; tile_idx<tile_cnt; tile_idx++ ) fd_tile_private_unlock( tile_idx, NULL );
828 :
829 1 : fd_memset( fd_tile_private_cpu_config_save, 0, sizeof(fd_tile_private_cpu_config_t) );
830 :
831 1 : fd_tile_private_stack1 = 0UL;
832 1 : fd_tile_private_stack0 = 0UL;
833 1 : fd_tile_private_idx = 0UL;
834 1 : fd_tile_private_id = 0UL;
835 :
836 1 : fd_tile_private_cnt = 0UL;
837 1 : fd_tile_private_id1 = 0UL;
838 1 : fd_tile_private_id0 = 0UL;
839 :
840 1 : FD_LOG_INFO(( "fd_tile: halt success" ));
841 1 : }
|