Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_topo.h"
3 : #include "fd_cpu_topo.h"
4 :
5 : #include "../waker/fd_waker.h"
6 : #include "../sleep/fd_sleep.h"
7 : #include "../metrics/fd_metrics.h"
8 : #include "../events/fd_event_report.h"
9 : #include "../keyguard/fd_keyload.h"
10 : #include "../../util/tile/fd_tile_private.h"
11 :
12 : #include <unistd.h>
13 : #include <errno.h>
14 : #include <fcntl.h>
15 : #include <limits.h>
16 : #include <pthread.h>
17 : #include <linux/futex.h>
18 : #include <sys/resource.h>
19 : #include <sys/prctl.h>
20 : #include <sys/stat.h>
21 : #include <sys/mman.h>
22 : #include <net/if.h>
23 :
24 : static void
25 : initialize_logging( char const * tile_name,
26 : ulong tile_kind_id,
27 0 : ulong tid ) {
28 0 : fd_log_cpu_set( NULL );
29 0 : fd_log_private_tid_set( tid );
30 0 : char thread_name[ 20 ];
31 0 : FD_TEST( fd_cstr_printf_check( thread_name, sizeof( thread_name ), NULL, "%s:%lu", tile_name, tile_kind_id ) );
32 0 : fd_log_thread_set( thread_name );
33 0 : fd_log_private_stack_discover( FD_TILE_PRIVATE_STACK_SZ,
34 0 : &fd_tile_private_stack0, &fd_tile_private_stack1 );
35 0 : FD_LOG_INFO(( "booting tile %s pid:%lu tid:%lu", thread_name, fd_log_group_id(), tid ));
36 :
37 : /* FD_LOG_* calls fd_log_wallclock_cstr, which calls localtime_r. In
38 : glibc, this ends up calling a function called tzset_internal. The
39 : first time tzset_internal is called by a process, it may (and
40 : almost always does) call __tzfile_read, which invokes the openat
41 : syscall, and possibly several others on the time zone file
42 : (typically /etc/localtime) or on a file in the time zone directory.
43 : This kind of behavior is tricky to sandbox, so the easiest thing to
44 : do is initialize it prior to the sandbox and hope whatever libc is
45 : used behaves like glibc. This only matters when both the logfile
46 : and stderr filters are strict enough so that the immediately prior
47 : FD_LOG_INFO call is a no-op, since otherwise that call would have
48 : taken care of it. */
49 0 : char wallclock[FD_LOG_WALLCLOCK_CSTR_BUF_SZ];
50 0 : fd_log_wallclock_cstr( 0L, wallclock );
51 0 : }
52 :
53 : void
54 : fd_topo_run_tile( fd_topo_t * topo,
55 : fd_topo_tile_t * tile,
56 : int sandbox,
57 : int keep_controlling_terminal,
58 : int core_dump_level,
59 : uint uid,
60 : uint gid,
61 : int allow_fd,
62 0 : fd_topo_run_tile_t * tile_run ) {
63 0 : char thread_name[ 20 ];
64 0 : FD_TEST( fd_cstr_printf_check( thread_name, sizeof( thread_name ), NULL, "%s:%lu", tile->name, tile->kind_id ) );
65 0 : 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 ) ));
66 :
67 0 : if( FD_UNLIKELY( topo->sleep_obj_id!=ULONG_MAX ) ) {
68 0 : if( FD_UNLIKELY( -1==prctl( PR_SET_TIMERSLACK, 1UL, 0UL, 0UL, 0UL ) ) ) FD_LOG_ERR(( "prctl(PR_SET_TIMERSLACK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
69 0 : }
70 :
71 0 : fd_tile_rseq_unregister();
72 :
73 0 : ulong pid = fd_sandbox_getpid(); /* Need to read /proc again.. we got a new PID from clone */
74 0 : ulong tid = fd_sandbox_gettid(); /* Need to read /proc again.. we got a new TID from clone */
75 :
76 0 : initialize_logging( tile->name, tile->kind_id, tid );
77 :
78 : /* preload shared memory before sandboxing, so it is already mapped */
79 0 : fd_topo_join_tile_workspaces( topo, tile, core_dump_level );
80 0 : fd_topo_mlock_code();
81 :
82 0 : fd_keyload_set_failover_junk( topo->failover_junk, strcmp( tile->name, "sign" ) ? -1 : tile->sign.failover_junk_fd );
83 :
84 0 : if( FD_UNLIKELY( tile_run->privileged_init ) )
85 0 : tile_run->privileged_init( topo, tile );
86 :
87 0 : ulong allow_fds_offset = 0UL;
88 0 : int allow_fds[ FD_SANDBOX_ALLOWED_FD_CNT_MAX ];
89 0 : for( ulong i=0UL; i<FD_SANDBOX_ALLOWED_FD_CNT_MAX; i++ ) allow_fds[ i ] = -1;
90 0 : if( FD_LIKELY( -1!=allow_fd ) ) {
91 0 : allow_fds_offset = 1UL;
92 0 : allow_fds[ 0 ] = allow_fd;
93 0 : }
94 0 : ulong allow_fds_cnt = 0UL;
95 0 : if( FD_LIKELY( tile_run->populate_allowed_fds ) ) {
96 0 : allow_fds_cnt = tile_run->populate_allowed_fds( topo,
97 0 : tile,
98 0 : FD_SANDBOX_ALLOWED_FD_CNT_MAX-allow_fds_offset,
99 0 : allow_fds+allow_fds_offset );
100 0 : }
101 :
102 :
103 0 : struct sock_filter seccomp_filter[ 256UL ];
104 0 : ulong seccomp_filter_cnt = 0UL;
105 0 : if( FD_LIKELY( tile_run->populate_allowed_seccomp ) ) {
106 0 : seccomp_filter_cnt = tile_run->populate_allowed_seccomp( topo,
107 0 : tile,
108 0 : sizeof(seccomp_filter)/sizeof(seccomp_filter[ 0 ]),
109 0 : seccomp_filter );
110 0 : }
111 :
112 0 : ulong rlimit_file_cnt = tile_run->rlimit_file_cnt;
113 0 : if( tile_run->rlimit_file_cnt_fn ) {
114 0 : rlimit_file_cnt = tile_run->rlimit_file_cnt_fn( topo, tile );
115 0 : }
116 :
117 0 : if( FD_LIKELY( sandbox ) ) {
118 0 : int dumpable = core_dump_level == FD_TOPO_CORE_DUMP_LEVEL_DISABLED ? 0 : 1;
119 0 : fd_sandbox_enter( uid,
120 0 : gid,
121 0 : tile_run->keep_host_networking,
122 0 : tile_run->allow_connect,
123 0 : tile_run->allow_renameat,
124 0 : keep_controlling_terminal,
125 0 : dumpable,
126 0 : rlimit_file_cnt,
127 0 : tile_run->rlimit_address_space,
128 0 : tile_run->rlimit_data,
129 0 : tile_run->rlimit_nproc,
130 0 : allow_fds_cnt+allow_fds_offset,
131 0 : allow_fds,
132 0 : seccomp_filter_cnt,
133 0 : seccomp_filter );
134 0 : } else {
135 0 : fd_sandbox_switch_uid_gid( uid, gid );
136 0 : }
137 :
138 : /* Now we are sandboxed, join all the tango IPC objects in the workspaces */
139 0 : fd_topo_fill_tile( topo, tile );
140 :
141 0 : FD_TEST( tile->metrics );
142 0 : fd_metrics_register( tile->metrics );
143 0 : fd_event_register( topo, tile );
144 :
145 0 : FD_MGAUGE_SET( TILE, PID, pid );
146 0 : FD_MGAUGE_SET( TILE, TID, tid );
147 0 : FD_MGAUGE_SET( TILE, HEARTBEAT_TIME, (ulong)fd_log_wallclock() );
148 :
149 0 : if( FD_UNLIKELY( tile_run->unprivileged_init ) )
150 0 : tile_run->unprivileged_init( topo, tile );
151 :
152 0 : tile_run->run( topo, tile );
153 0 : if( FD_UNLIKELY( !tile->allow_shutdown ) ) FD_LOG_ERR(( "tile %s:%lu run loop returned", tile->name, tile->kind_id ));
154 :
155 0 : FD_MGAUGE_SET( TILE, STATE, 2UL );
156 0 : }
157 :
158 : typedef struct {
159 : fd_topo_t * topo;
160 : fd_topo_tile_t * tile;
161 : fd_topo_run_tile_t tile_run;
162 : uint uid;
163 : uint gid;
164 : volatile int copied;
165 : void * stack_lo;
166 : void * stack_hi;
167 : } fd_topo_run_thread_args_t;
168 :
169 : static void *
170 0 : run_tile_thread_main( void * _args ) {
171 0 : fd_topo_run_thread_args_t args = *(fd_topo_run_thread_args_t *)_args;
172 0 : FD_COMPILER_MFENCE();
173 0 : ((fd_topo_run_thread_args_t *)_args)->copied = 1;
174 0 : FD_COMPILER_MFENCE();
175 :
176 : /* Prevent fork() from smashing the stack */
177 0 : if( FD_UNLIKELY( madvise( args.stack_lo, (ulong)args.stack_hi - (ulong)args.stack_lo, MADV_DONTFORK ) ) ) {
178 0 : FD_LOG_ERR(( "madvise(stack,MADV_DONTFORK) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
179 0 : }
180 :
181 0 : fd_topo_run_tile( args.topo, args.tile, 0, 1, 1, args.uid, args.gid, -1, &args.tile_run );
182 0 : FD_TEST( args.tile->allow_shutdown );
183 0 : return NULL;
184 0 : }
185 :
186 : /* fd_topo_tile_stack_join_anon is a variant of fd_topo_tile_stack_join
187 : that acquires private anonymous memory instead of shared pages.
188 :
189 : This is required for fork() to work, as the parent and child process
190 : would otherwise share a stack and corrupt each other. While fork()
191 : is banned in tile user code, some dynamic analysis tools (like MSan)
192 : unfortunately rely on it. */
193 :
194 : FD_FN_UNUSED static void *
195 0 : fd_topo_tile_stack_join_anon( void ) {
196 0 :
197 0 : ulong sz = 2*FD_TILE_PRIVATE_STACK_SZ;
198 0 : int prot = PROT_READ|PROT_WRITE;
199 0 : int flags = MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK;
200 0 :
201 0 : uchar * stack = MAP_FAILED;
202 0 : #if !FD_HAS_ASAN && !FD_HAS_MSAN
203 0 : stack = mmap( NULL, sz, prot, flags|MAP_HUGETLB, -1, 0 );
204 0 : #endif
205 0 :
206 0 : if( stack==MAP_FAILED ) {
207 0 : stack = mmap( NULL, sz, prot, flags, -1, 0 );
208 0 : if( FD_UNLIKELY( stack==MAP_FAILED ) ) {
209 0 : FD_LOG_ERR(( "mmap() for stack failed (%i-%s)", errno, fd_io_strerror( errno ) ));
210 0 : }
211 0 : }
212 0 :
213 0 : /* Create the guard regions in the extra space */
214 0 : void * guard_lo = (void *)( stack - FD_SHMEM_NORMAL_PAGE_SZ );
215 0 : if( FD_UNLIKELY( mmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
216 0 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_lo ) )
217 0 : FD_LOG_ERR(( "mmap(%p) failed (%i-%s)", guard_lo, errno, fd_io_strerror( errno ) ));
218 0 :
219 0 : void * guard_hi = (void *)( stack + FD_TILE_PRIVATE_STACK_SZ );
220 0 : if( FD_UNLIKELY( mmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
221 0 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_hi ) )
222 0 : FD_LOG_ERR(( "mmap(%p) failed (%i-%s)", guard_hi, errno, fd_io_strerror( errno ) ));
223 0 :
224 0 : return stack;
225 0 : }
226 :
227 : void *
228 : fd_topo_tile_stack_join( char const * app_name,
229 : char const * tile_name,
230 0 : ulong tile_kind_id ) {
231 : #if FD_HAS_MSAN
232 : return fd_topo_tile_stack_join_anon();
233 : #endif
234 :
235 0 : char name[ PATH_MAX ];
236 0 : FD_TEST( fd_cstr_printf_check( name, PATH_MAX, NULL, "%s_stack_%s%lu", app_name, tile_name, tile_kind_id ) );
237 :
238 : /* avoid core dumps of sign tile stacks, and of failov ones, which hold
239 : TLS session secrets */
240 0 : int dump = strcmp( tile_name, "sign" ) && strcmp( tile_name, "failov" ) ? 1 : 0;
241 0 : uchar * stack = fd_shmem_join( name, FD_SHMEM_JOIN_MODE_READ_WRITE, dump, NULL, NULL, NULL );
242 0 : if( FD_UNLIKELY( !stack ) ) FD_LOG_ERR(( "fd_shmem_join failed" ));
243 :
244 : /* Make space for guard lo and guard hi */
245 0 : if( FD_UNLIKELY( fd_shmem_release( stack, FD_SHMEM_HUGE_PAGE_SZ, 1UL ) ) )
246 0 : FD_LOG_ERR(( "fd_shmem_release (%d-%s)", errno, fd_io_strerror( errno ) ));
247 0 : stack += FD_SHMEM_HUGE_PAGE_SZ;
248 0 : if( FD_UNLIKELY( fd_shmem_release( stack + FD_TILE_PRIVATE_STACK_SZ, FD_SHMEM_HUGE_PAGE_SZ, 1UL ) ) )
249 0 : FD_LOG_ERR(( "fd_shmem_release (%d-%s)", errno, fd_io_strerror( errno ) ));
250 :
251 : /* Create the guard regions in the extra space */
252 0 : void * guard_lo = (void *)(stack - FD_SHMEM_NORMAL_PAGE_SZ );
253 0 : if( FD_UNLIKELY( mmap( guard_lo, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
254 0 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_lo ) )
255 0 : FD_LOG_ERR(( "mmap failed (%i-%s)", errno, fd_io_strerror( errno ) ));
256 :
257 0 : void * guard_hi = (void *)(stack + FD_TILE_PRIVATE_STACK_SZ);
258 0 : if( FD_UNLIKELY( mmap( guard_hi, FD_SHMEM_NORMAL_PAGE_SZ, PROT_NONE,
259 0 : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, (off_t)0 )!=guard_hi ) )
260 0 : FD_LOG_ERR(( "mmap failed (%i-%s)", errno, fd_io_strerror( errno ) ));
261 :
262 0 : return stack;
263 0 : }
264 :
265 : static inline void
266 : run_tile_thread( fd_topo_t * topo,
267 : fd_topo_tile_t * tile,
268 : fd_topo_run_tile_t tile_run,
269 : uint uid,
270 : uint gid,
271 : fd_cpuset_t const * float_cpu_set,
272 : fd_cpuset_t const * cpuless_cpu_set,
273 : int floating_priority,
274 0 : fd_topo_run_thread_args_t * args ) {
275 : /* tpool will assign a thread later */
276 0 : if( FD_UNLIKELY( tile_run.for_tpool ) ) return;
277 0 : void * stack = fd_topo_tile_stack_join( topo->app_name, tile->name, tile->kind_id );
278 :
279 0 : pthread_attr_t attr[ 1 ];
280 0 : int err = pthread_attr_init( attr );
281 0 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "pthread_attr_init() failed (%i-%s)", err, fd_io_strerror( err ) ));
282 0 : err = pthread_attr_setstack( attr, stack, FD_TILE_PRIVATE_STACK_SZ );
283 0 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "pthread_attr_setstack() failed (%i-%s)", err, fd_io_strerror( err ) ));
284 :
285 0 : FD_CPUSET_DECL( cpu_set );
286 0 : if( FD_LIKELY( tile->cpu_idx<65535UL ) ) {
287 : /* set the thread affinity before we clone the new process to ensure
288 : kernel first touch happens on the desired thread. */
289 0 : if( FD_UNLIKELY( tile->floats ) ) {
290 0 : ulong numa_idx = fd_shmem_numa_idx( tile->cpu_idx );
291 0 : for( ulong cpu=0UL; cpu<FD_TILE_MAX; cpu++ )
292 0 : if( fd_cpuset_test( float_cpu_set, cpu ) && fd_shmem_numa_idx( cpu )==numa_idx ) fd_cpuset_insert( cpu_set, cpu );
293 0 : if( FD_UNLIKELY( !fd_cpuset_cnt( cpu_set ) ) ) fd_memcpy( cpu_set, float_cpu_set, fd_cpuset_footprint() );
294 0 : }
295 0 : if( FD_UNLIKELY( !fd_cpuset_cnt( cpu_set ) ) ) fd_cpuset_insert( cpu_set, tile->cpu_idx );
296 0 : if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, -19 ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
297 0 : } else {
298 0 : fd_cpuset_copy( cpu_set, cpuless_cpu_set );
299 0 : if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, floating_priority ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
300 0 : }
301 :
302 0 : if( FD_UNLIKELY( fd_cpuset_setaffinity( 0, cpu_set ) ) ) {
303 0 : if( FD_LIKELY( errno==EINVAL ) ) {
304 0 : FD_LOG_ERR(( "Unable to set the thread affinity for tile %s:%lu on cpu %lu. It is likely that the affinity "
305 0 : "you have specified for this tile in [layout.affinity] of your configuration file contains a "
306 0 : "CPU (%lu) which does not exist on this machine.",
307 0 : tile->name, tile->kind_id, tile->cpu_idx, tile->cpu_idx ));
308 0 : } else {
309 0 : FD_LOG_ERR(( "sched_setaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
310 0 : }
311 0 : }
312 :
313 0 : *args = (fd_topo_run_thread_args_t) {
314 0 : .topo = topo,
315 0 : .tile = tile,
316 0 : .tile_run = tile_run,
317 0 : .uid = uid,
318 0 : .gid = gid,
319 0 : .copied = 0,
320 0 : .stack_lo = stack,
321 0 : .stack_hi = (uchar *)stack + FD_TILE_PRIVATE_STACK_SZ
322 0 : };
323 :
324 0 : pthread_t pthread;
325 0 : err = pthread_create( &pthread, attr, run_tile_thread_main, args );
326 0 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "pthread_create() failed (%i-%s)", err, fd_io_strerror( err ) ));
327 0 : }
328 :
329 : static void
330 0 : join_isolation_cgroup( char const * app_name ) {
331 0 : char path[ PATH_MAX ];
332 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/sys/fs/cgroup/%s/cgroup.procs", app_name ) );
333 :
334 0 : int fd = open( path, O_WRONLY );
335 0 : if( FD_UNLIKELY( fd<0 ) ) {
336 0 : if( FD_LIKELY( errno==ENOENT ) ) return; /* cpuset stage not configured */
337 0 : FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
338 0 : }
339 :
340 0 : char pid[ 32 ];
341 0 : ulong pid_len;
342 0 : FD_TEST( fd_cstr_printf_check( pid, sizeof(pid), &pid_len, "%ld", (long)getpid() ) );
343 0 : if( FD_UNLIKELY( write( fd, pid, pid_len )!=(long)pid_len ) )
344 0 : FD_LOG_ERR(( "write(%s,\"%s\") failed (%i-%s)", path, pid, errno, fd_io_strerror( errno ) ));
345 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
346 0 : }
347 :
348 : void
349 : fd_topo_run_single_process( fd_topo_t * topo,
350 : uint uid,
351 : uint gid,
352 0 : fd_topo_run_tile_t (* tile_run )( fd_topo_tile_t const * tile ) ) {
353 0 : join_isolation_cgroup( topo->app_name );
354 :
355 : /* Lock code once before spawning tile threads, so they don't all
356 : queue on mmap_lock behind a cold-cache populate. */
357 0 : fd_topo_mlock_code();
358 :
359 0 : ulong waker_client_cnt = 0UL;
360 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
361 0 : ulong idx = topo->tiles[ i ].waker_client_idx;
362 0 : if( FD_UNLIKELY( idx!=ULONG_MAX ) ) waker_client_cnt = fd_ulong_max( waker_client_cnt, idx+1UL );
363 0 : }
364 0 : fd_waker_install( waker_client_cnt );
365 0 : fd_sleep_eventfd_install( topo );
366 :
367 : /* Save the current affinity, it will be restored after creating any child tiles */
368 0 : FD_CPUSET_DECL( floating_cpu_set );
369 0 : if( FD_UNLIKELY( fd_cpuset_getaffinity( 0, floating_cpu_set ) ) )
370 0 : FD_LOG_ERR(( "sched_getaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
371 :
372 0 : FD_CPUSET_DECL( float_cpu_set );
373 0 : FD_CPUSET_DECL( cpuless_cpu_set );
374 0 : static fd_topo_cpus_t cpus[1];
375 0 : fd_topo_cpus_init( cpus );
376 0 : fd_topo_cpus_floating( cpus, topo, floating_cpu_set, float_cpu_set, cpuless_cpu_set );
377 0 : int any_floats = 0;
378 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) any_floats |= topo->tiles[ i ].floats;
379 :
380 0 : errno = 0;
381 0 : int save_priority = getpriority( PRIO_PROCESS, 0 );
382 0 : if( FD_UNLIKELY( -1==save_priority && errno ) ) FD_LOG_ERR(( "getpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
383 :
384 0 : fd_topo_run_thread_args_t args[ FD_TOPO_MAX_TILES ];
385 :
386 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
387 0 : fd_topo_tile_t * tile = &topo->tiles[ i ];
388 0 : fd_topo_run_tile_t run_tile = tile_run( tile );
389 0 : int floating_priority = ( any_floats && !strcmp( tile->name, "waker" ) ) ? -19 : save_priority; /* the waker delivers floaters' fd readiness: never behind them */
390 0 : run_tile_thread( topo, tile, run_tile, uid, gid, float_cpu_set, cpuless_cpu_set, floating_priority, &args[ i ] );
391 0 : }
392 :
393 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
394 0 : while( !FD_VOLATILE( args[ i ].copied ) ) FD_SPIN_PAUSE();
395 0 : }
396 :
397 0 : fd_sandbox_switch_uid_gid( uid, gid );
398 :
399 0 : if( FD_UNLIKELY( -1==setpriority( PRIO_PROCESS, 0, save_priority ) ) ) FD_LOG_ERR(( "setpriority() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
400 0 : if( FD_UNLIKELY( fd_cpuset_setaffinity( 0, floating_cpu_set ) ) )
401 0 : FD_LOG_ERR(( "sched_setaffinity failed (%i-%s)", errno, fd_io_strerror( errno ) ));
402 0 : }
|