Line data Source code
1 : /* The irq-affinity stage attempts to minimize the amount of CPU time
2 : stolen by device interrupts from Firedancer fixed tiles.
3 :
4 : Configuring interrupt affinity is an annoying problem, and doing so
5 : blindly using a default policy is very annoying. This wall of text
6 : documents why the code was written like it is.
7 :
8 : The problem is that default Linux system configuration dispatches
9 : IRQs to arbitrary CPUs. Since socket-based networking heavily relies
10 : on IRQs, heavy incoming traffic can possibly starve a Firedancer tile
11 : from running. Tiles cannot evacuate to other CPUs since they are
12 : pinned to one CPU each.
13 :
14 : The goal is to prevent interrupt requests from being delivered to
15 : CPUs that Firedancer tiles are spinning on. The kernel exposes the
16 : /proc/irq/N/smp_affinity API for this purpose.
17 :
18 : ### isolcpus
19 :
20 : isolcpus is the ideal way to achieve CPU isolation from interrupts
21 : and lots of other undesired activity. At the time of writing,
22 : isolcpus can only be configured at boot as a kernel command line
23 : parameter, though.
24 :
25 : ### procfs smp_affinity
26 :
27 : On a typical Intel/AMD system (with an x2APIC interrupt controller),
28 : the kernel binds an IRQ to one CPU core. That CPU core is picked out
29 : of the smp_affinity mask (unless the mask is impossible to satisfy).
30 : This CPU can be found in /proc/irq/N/effective_affinity_list.
31 : (Technically, IRQ load balancing can be done dynamically at the
32 : hardware level with x2APIC, but this is rare...)
33 :
34 : The kernel picks the effective CPU index out of smp_affinity using
35 : roughly these rules:
36 : - ignore offline CPUs
37 : - ignore isolated CPUs (isolcpus kernel boot parameter)
38 : - ignore CPUs on a different NUMA than the device
39 : - pick the CPU with the fewest IRQs (kernel/irq/matrix.c)
40 :
41 : This load balancing policy is decent but does not take into account
42 : how busy different IRQs are. On an unlucky system startup, one CPU
43 : might have multiple busy IRQs while another CPU barely gets any
44 : interrupts.
45 :
46 : ### irqbalance
47 :
48 : The irqbalance userland daemon was created to achieve better load
49 : balancing than the kernel's static mapping.
50 : It periodically rewrites all /proc/irq/N/smp_affinity files to
51 : dynamically rebalance IRQs, reacting to high CPU usage, thermal
52 : events, etc.
53 :
54 : One can ban irqbalance from using certain CPUs either via a config
55 : file or unix domain sockets. The latter is ephemeral in nature,
56 : config written via UDS is auto-reset on restart.
57 :
58 : ### Firedancer smp_affinity interop
59 :
60 : Firedancer also manually updates the smp_affinity list. Some IRQs
61 : cannot be removed from a CPU (e.g. hardware timer or NVMe managed
62 : interrupts), so Firedancer should ignore them. Unfortunately, the
63 : kernel provides no method to tell which IRQs can be moved.
64 :
65 : Thus, irq-affinity is implemented as follows:
66 : - 'check' (which looks for misconfigured IRQs) writes back the
67 : existing smp_affinity mask, if the mask includes tile CPUs. If
68 : this results in a permission error, the interrupt likely cannot be
69 : reconfigured.
70 : - 'init' does the actual reconfiguration (any CPUs that are not
71 : Firedancer tiles are allowed)
72 : - 'fini' re-admits fixed tile CPUs into the smp_affinity mask
73 : (attempts to keep CPUs excluded that were already excluded before
74 : Firedancer)
75 :
76 : Another quirk is that the kernel leaves the effective CPU of an IRQ
77 : unchanged if the smp_affinity mask is narrowed, but the effective CPU
78 : stays in the mask. Due to this, 'check' is truly a no-op (ignoring
79 : TOCTOU races), and 'fini' fails to restore effective affinity masks.
80 :
81 : ### Firedancer irqbalance interop
82 :
83 : If irqbalance is available, Firedancer uses that unix domain socket
84 : API to ban tile CPUs. Since irqbalance forgets UDS config on restart
85 : we would ideally periodically re-apply the config. Unfortunately,
86 : irqbalance creates the UDS socket path on each startup. Opening
87 : arbitrary files does not play well with Firedancer's sandbox.
88 :
89 : Thus, Firedancer only configures the irqbalance daemon on startup
90 : using this configure stage.
91 :
92 : Manual procfs smp_affinity is the lesser evil, so Firedancer logs a
93 : warning if it finds irqbalance.
94 :
95 : ### Firedancer network stack
96 :
97 : Firedancer code avoids producing interrupts/softirq where possible,
98 : instead opting for busy polling. But at the time of writing, XDP
99 : driver code in the Linux kernel is so botched that preferred busy
100 : polling cannot be reliably enabled.
101 :
102 : Thus, unfortunately, IRQs for Firedancer RX XDP traffic will be
103 : handled by remote CPU cores. */
104 :
105 : #define _DEFAULT_SOURCE
106 : #include "configure.h"
107 : #include "fd_cpu_isolation.h"
108 : #include "../../../../util/tile/fd_tile_private.h"
109 : #include <fcntl.h>
110 : #include <sys/types.h>
111 : #include <dirent.h>
112 : #include <ctype.h>
113 : #include <errno.h>
114 : #include <stdlib.h>
115 : #include <unistd.h>
116 :
117 : /* FD_IRQ_AFFINITY_CHECK_TIGHT, when non-zero, makes check() also flag
118 : IRQs whose affinity mask is a strict subset of the allowed CPUs (but
119 : that do not overlap any tile CPU). Such IRQs do not steal time from
120 : tiles, and irqbalance routinely narrows IRQs this way, which would
121 : make check() never pass while it runs. Disabled for now. */
122 : #ifndef FD_IRQ_AFFINITY_CHECK_TIGHT
123 : #define FD_IRQ_AFFINITY_CHECK_TIGHT 0
124 : #endif
125 :
126 : /* smp_affinity file format is 4a4a4a4a,fcfcfcfc,...
127 : So, 9 bytes per 32 bits ~ 3.56 bits per byte. */
128 : #define SMP_AFFINITY_STR_LEN (FD_TILE_MAX/3)
129 : #define MISMATCH_SAMPLE_MAX (16UL)
130 : #define MISMATCH_STR_LEN (128UL)
131 : #define MISMATCH_TILE_STR_LEN (128UL)
132 :
133 : static char *
134 : append_ulong_list_sample( char * buf,
135 : ulong buf_sz,
136 : ulong const * vals,
137 : ulong val_cnt,
138 0 : ulong total_cnt ) {
139 0 : char * p = fd_cstr_init( buf );
140 0 : for( ulong i=0UL; i<val_cnt; i++ ) {
141 0 : if( FD_LIKELY( i ) ) p = fd_cstr_append_char( p, ',' );
142 0 : if( FD_UNLIKELY( (ulong)(p-buf)+32UL >= buf_sz ) ) break;
143 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, vals[ i ], fd_ulong_base10_dig_cnt( vals[ i ] ) );
144 0 : }
145 0 : if( FD_UNLIKELY( total_cnt>val_cnt && (ulong)(p-buf)+32UL<buf_sz ) ) {
146 0 : p = fd_cstr_append_cstr( p, ",+" );
147 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, total_cnt-val_cnt, fd_ulong_base10_dig_cnt( total_cnt-val_cnt ) );
148 0 : p = fd_cstr_append_cstr( p, " more" );
149 0 : }
150 0 : fd_cstr_fini( p );
151 0 : return buf;
152 0 : }
153 :
154 : static char *
155 : tile_list_sample( char * buf,
156 : ulong buf_sz,
157 : fd_topo_t const * topo,
158 : ulong const * tile_idxs,
159 : ulong tile_cnt,
160 0 : ulong total_cnt ) {
161 0 : char * p = fd_cstr_init( buf );
162 0 : for( ulong i=0UL; i<tile_cnt; i++ ) {
163 0 : fd_topo_tile_t const * tile = &topo->tiles[ tile_idxs[ i ] ];
164 0 : if( FD_LIKELY( i ) ) p = fd_cstr_append_char( p, ',' );
165 0 : if( FD_UNLIKELY( (ulong)(p-buf)+32UL >= buf_sz ) ) break;
166 0 : p = fd_cstr_append_cstr( p, tile->name );
167 0 : p = fd_cstr_append_char( p, ':' );
168 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, tile->kind_id, fd_ulong_base10_dig_cnt( tile->kind_id ) );
169 0 : }
170 0 : if( FD_UNLIKELY( total_cnt>tile_cnt && (ulong)(p-buf)+32UL<buf_sz ) ) {
171 0 : p = fd_cstr_append_cstr( p, ",+" );
172 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, total_cnt-tile_cnt, fd_ulong_base10_dig_cnt( total_cnt-tile_cnt ) );
173 0 : p = fd_cstr_append_cstr( p, " more" );
174 0 : }
175 0 : fd_cstr_fini( p );
176 0 : return buf;
177 0 : }
178 :
179 : static int
180 0 : irq_dirent_is_irq( char const * name ) {
181 0 : if( FD_UNLIKELY( !name[0] ) ) return 0;
182 0 : for( char const * p=name; *p; p++ ) {
183 0 : if( FD_UNLIKELY( !isdigit( (uchar)*p ) ) ) return 0;
184 0 : }
185 0 : return 1;
186 0 : }
187 :
188 : static int
189 : read_irq_smp_affinity( char const * irq,
190 0 : fd_cpuset_t * cpuset ) {
191 0 : char path[ PATH_MAX ];
192 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/irq/%s/smp_affinity", irq ) );
193 :
194 0 : int fd = open( path, O_RDONLY );
195 0 : if( FD_UNLIKELY( fd<0 ) ) return 0;
196 :
197 0 : char affinity[ SMP_AFFINITY_STR_LEN+64UL ];
198 0 : long affinity_len = read( fd, affinity, sizeof(affinity)-1UL );
199 0 : int err = errno;
200 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
201 0 : if( FD_UNLIKELY( affinity_len<0 ) ) {
202 0 : errno = err;
203 0 : return 0;
204 0 : }
205 :
206 0 : affinity[ affinity_len ] = '\0';
207 0 : return fd_cpu_isolation_parse_mask( cpuset, affinity );
208 0 : }
209 :
210 : static int
211 : write_irq_smp_affinity( char const * irq,
212 : fd_cpuset_t const * cpuset,
213 0 : int warn ) {
214 0 : char path[ PATH_MAX ];
215 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/proc/irq/%s/smp_affinity", irq ) );
216 :
217 0 : char affinity[ SMP_AFFINITY_STR_LEN+64UL ];
218 0 : fd_cpu_isolation_format_mask( affinity, sizeof(affinity), cpuset );
219 0 : ulong affinity_len = strlen( affinity );
220 :
221 0 : int fd = open( path, O_WRONLY );
222 0 : if( FD_UNLIKELY( fd<0 ) ) {
223 0 : if( FD_LIKELY( warn ) ) FD_LOG_WARNING(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
224 0 : return 0;
225 0 : }
226 :
227 0 : int ok = 1;
228 0 : if( FD_UNLIKELY( write( fd, affinity, affinity_len )!=(long)affinity_len ) ) {
229 0 : int err = errno;
230 0 : if( FD_LIKELY( warn ) ) FD_LOG_WARNING(( "write(%s) failed (%i-%s)", path, err, fd_io_strerror( err ) ));
231 0 : errno = err;
232 0 : ok = 0;
233 0 : }
234 :
235 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
236 0 : return ok;
237 0 : }
238 :
239 : static void
240 : update_irq_smp_affinities( fd_cpuset_t const * add_cpus,
241 0 : fd_cpuset_t const * remove_cpus ) {
242 0 : DIR * dir = opendir( "/proc/irq" );
243 0 : if( FD_UNLIKELY( !dir ) ) {
244 0 : FD_LOG_WARNING(( "opendir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
245 0 : return;
246 0 : }
247 :
248 0 : FD_CPUSET_DECL( fallback );
249 0 : if( FD_LIKELY( remove_cpus ) ) fd_cpuset_subtract( fallback, fd_cpu_isolation_host_cpus( fallback ), remove_cpus );
250 :
251 0 : struct dirent * entry;
252 0 : while( (entry = readdir( dir )) ) {
253 0 : if( FD_UNLIKELY( !irq_dirent_is_irq( entry->d_name ) ) ) continue;
254 :
255 0 : FD_CPUSET_DECL( current );
256 0 : if( FD_UNLIKELY( !read_irq_smp_affinity( entry->d_name, current ) ) ) continue;
257 :
258 0 : FD_CPUSET_DECL( next );
259 0 : if( FD_LIKELY( remove_cpus ) ) fd_cpuset_subtract( next, current, remove_cpus );
260 0 : else fd_cpuset_copy ( next, current );
261 0 : if( FD_UNLIKELY( !fd_cpuset_cnt( next ) && remove_cpus ) ) fd_cpuset_copy( next, fallback );
262 0 : if( FD_LIKELY( add_cpus ) ) fd_cpuset_union( next, next, add_cpus );
263 :
264 0 : if( FD_LIKELY( fd_cpuset_eq( current, next ) ) ) continue;
265 0 : if( FD_UNLIKELY( !write_irq_smp_affinity( entry->d_name, next, 0 ) && errno!=EPERM && errno!=EIO ) ) {
266 0 : int err = errno;
267 0 : FD_LOG_WARNING(( "write(/proc/irq/%s/smp_affinity) failed (%i-%s)", entry->d_name, err, fd_io_strerror( err ) ));
268 0 : }
269 0 : }
270 :
271 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
272 0 : }
273 :
274 : static void
275 0 : set_irq_smp_affinities( fd_cpuset_t const * desired ) {
276 0 : DIR * dir = opendir( "/proc/irq" );
277 0 : if( FD_UNLIKELY( !dir ) ) {
278 0 : FD_LOG_WARNING(( "opendir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
279 0 : return;
280 0 : }
281 :
282 0 : struct dirent * entry;
283 0 : while( (entry = readdir( dir )) ) {
284 0 : if( FD_UNLIKELY( !irq_dirent_is_irq( entry->d_name ) ) ) continue;
285 :
286 0 : FD_CPUSET_DECL( current );
287 0 : if( FD_UNLIKELY( !read_irq_smp_affinity( entry->d_name, current ) ) ) continue;
288 :
289 0 : if( FD_LIKELY( fd_cpuset_eq( current, desired ) ) ) continue;
290 0 : if( FD_UNLIKELY( !write_irq_smp_affinity( entry->d_name, desired, 0 ) && errno!=EPERM && errno!=EIO ) ) {
291 0 : int err = errno;
292 0 : FD_LOG_WARNING(( "write(/proc/irq/%s/smp_affinity) failed (%i-%s)", entry->d_name, err, fd_io_strerror( err ) ));
293 0 : }
294 0 : }
295 :
296 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
297 0 : }
298 :
299 : /* topo_banned_cpus returns the set of CPUs that should not handle
300 : interrupts in *cpuset. */
301 :
302 : static fd_cpuset_t *
303 : topo_banned_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
304 0 : fd_topo_t const * topo ) {
305 0 : fd_cpuset_new( cpuset );
306 0 : ulong cpu_cnt = fd_shmem_cpu_cnt();
307 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
308 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
309 0 : if( tile->cpu_idx < cpu_cnt ) fd_cpuset_insert( cpuset, tile->cpu_idx );
310 0 : }
311 0 : return cpuset;
312 0 : }
313 :
314 : static void
315 : init_perm( fd_cap_chk_t * chk,
316 0 : config_t const * config FD_PARAM_UNUSED ) {
317 0 : fd_cap_chk_root( chk, "irq-affinity", "modify `/proc/irq/*/smp_affinity`" );
318 0 : }
319 :
320 : static void
321 : fini_perm( fd_cap_chk_t * chk,
322 0 : config_t const * config FD_PARAM_UNUSED ) {
323 0 : fd_cap_chk_root( chk, "irq-affinity", "modify `/proc/irq/*/smp_affinity`" );
324 0 : }
325 :
326 : static void
327 0 : init( config_t const * config ) {
328 0 : FD_CPUSET_DECL( banned );
329 0 : topo_banned_cpus( banned, &config->topo );
330 :
331 0 : FD_CPUSET_DECL( allowed );
332 0 : fd_cpuset_subtract( allowed, fd_cpu_isolation_host_cpus( allowed ), banned );
333 :
334 0 : if( FD_UNLIKELY( !fd_cpuset_cnt( allowed ) ) ) {
335 0 : FD_LOG_ERR(( "all host CPUs are assigned to Firedancer tiles; cannot reserve any CPU for device interrupts" ));
336 0 : }
337 :
338 0 : set_irq_smp_affinities( allowed );
339 0 : }
340 :
341 : static int
342 : fini( config_t const * config,
343 0 : int pre_init ) {
344 0 : (void)pre_init;
345 :
346 0 : FD_CPUSET_DECL( banned );
347 0 : topo_banned_cpus( banned, &config->topo );
348 :
349 0 : update_irq_smp_affinities( banned, NULL );
350 0 : return 1;
351 0 : }
352 :
353 : static configure_result_t
354 : check( config_t const * config,
355 0 : int check_type ) {
356 0 : FD_CPUSET_DECL( banned );
357 0 : topo_banned_cpus( banned, &config->topo );
358 :
359 0 : FD_CPUSET_DECL( allowed );
360 0 : fd_cpuset_subtract( allowed, fd_cpu_isolation_host_cpus( allowed ), banned );
361 :
362 0 : DIR * dir = opendir( "/proc/irq" );
363 0 : if( FD_UNLIKELY( !dir ) ) FD_LOG_ERR(( "opendir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
364 :
365 0 : ulong irq_cnt = 0UL;
366 0 : ulong misconfigured = 0UL;
367 0 : ulong unprobeable = 0UL;
368 0 : ulong overlap_irq_sample[ MISMATCH_SAMPLE_MAX ];
369 0 : ulong overlap_irq_sample_cnt = 0UL;
370 0 : ulong overlap_irq_cnt = 0UL;
371 : #if FD_IRQ_AFFINITY_CHECK_TIGHT
372 : ulong tight_irq_sample[ MISMATCH_SAMPLE_MAX ];
373 : ulong tight_irq_sample_cnt = 0UL;
374 : ulong tight_irq_cnt = 0UL;
375 : #endif
376 0 : ulong tile_sample[ MISMATCH_SAMPLE_MAX ];
377 0 : ulong tile_sample_cnt = 0UL;
378 0 : ulong tile_overlap_cnt = 0UL;
379 0 : uchar tile_seen[ FD_TOPO_MAX_TILES ] = {0};
380 0 : struct dirent * entry;
381 0 : while( (entry = readdir( dir )) ) {
382 0 : if( FD_UNLIKELY( !irq_dirent_is_irq( entry->d_name ) ) ) continue;
383 0 : irq_cnt++;
384 :
385 0 : FD_CPUSET_DECL( current );
386 0 : if( FD_UNLIKELY( !read_irq_smp_affinity( entry->d_name, current ) ) ) {
387 0 : unprobeable++;
388 0 : continue;
389 0 : }
390 :
391 0 : if( FD_LIKELY( fd_cpuset_eq( current, allowed ) ) ) continue;
392 :
393 : /* Some IRQs cannot be moved. Per the stage comment above, writing
394 : the same mask back lets us distinguish those from configurable IRQs. */
395 0 : if( FD_UNLIKELY( !write_irq_smp_affinity( entry->d_name, current, 0 ) ) ) {
396 0 : if( FD_LIKELY( errno==EPERM || errno==EIO || errno==ENOENT || errno==ENODEV || errno==ENXIO ) ) continue;
397 0 : else if( FD_LIKELY( errno==EACCES ) ) {
398 0 : char irq_name[ NAME_MAX+1UL ];
399 0 : FD_TEST( fd_cstr_printf_check( irq_name, sizeof(irq_name), NULL, "%s", entry->d_name ) );
400 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
401 0 : if( check_type==FD_CONFIGURE_CHECK_TYPE_FINI_PERM ) CONFIGURE_OK();
402 0 : NOT_CONFIGURED( "insufficient permissions to write /proc/irq/%s/smp_affinity", irq_name );
403 0 : } else {
404 0 : FD_LOG_ERR(( "write(/proc/irq/%s/smp_affinity) failed (%i-%s)", entry->d_name, errno, fd_io_strerror( errno ) ));
405 0 : }
406 0 : }
407 :
408 0 : FD_CPUSET_DECL( overlap );
409 0 : fd_cpuset_intersect( overlap, current, banned );
410 0 : ulong irq = strtoul( entry->d_name, NULL, 10 );
411 0 : if( FD_UNLIKELY( !fd_cpuset_is_null( overlap ) ) ) {
412 0 : misconfigured++;
413 0 : if( FD_LIKELY( overlap_irq_sample_cnt<MISMATCH_SAMPLE_MAX ) ) overlap_irq_sample[ overlap_irq_sample_cnt++ ] = irq;
414 0 : overlap_irq_cnt++;
415 0 : for( ulong i=0UL; i<config->topo.tile_cnt; i++ ) {
416 0 : fd_topo_tile_t const * tile = &config->topo.tiles[ i ];
417 0 : if( FD_UNLIKELY( tile->cpu_idx>=FD_TILE_MAX || !fd_cpuset_test( overlap, tile->cpu_idx ) || tile_seen[ i ] ) ) continue;
418 0 : tile_seen[ i ] = 1;
419 0 : if( FD_LIKELY( tile_sample_cnt<MISMATCH_SAMPLE_MAX ) ) tile_sample[ tile_sample_cnt++ ] = i;
420 0 : tile_overlap_cnt++;
421 0 : }
422 0 : }
423 : #if FD_IRQ_AFFINITY_CHECK_TIGHT
424 : /* An IRQ pinned to a strict subset of the allowed CPUs (but no tile
425 : CPUs) does not steal time from any tile, so it does not violate the
426 : stage's goal. irqbalance routinely narrows IRQs this way, which
427 : would make check() never pass while it runs, so the tight check is
428 : disabled for now. */
429 : else if( FD_UNLIKELY( fd_cpuset_subset( current, allowed ) ) ) {
430 : misconfigured++;
431 : if( FD_LIKELY( tight_irq_sample_cnt<MISMATCH_SAMPLE_MAX ) ) tight_irq_sample[ tight_irq_sample_cnt++ ] = irq;
432 : tight_irq_cnt++;
433 : }
434 : #endif
435 0 : }
436 :
437 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir(/proc/irq) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
438 :
439 0 : if( FD_UNLIKELY( unprobeable==irq_cnt ) ) PARTIALLY_CONFIGURED( "could not read any IRQ affinity masks from /proc/irq" );
440 0 : if( FD_UNLIKELY( misconfigured ) ) {
441 0 : if( FD_LIKELY( overlap_irq_cnt ) ) {
442 0 : char irq_str[ MISMATCH_STR_LEN ];
443 0 : char tile_str[ MISMATCH_TILE_STR_LEN ];
444 0 : append_ulong_list_sample( irq_str, sizeof(irq_str), overlap_irq_sample, overlap_irq_sample_cnt, overlap_irq_cnt );
445 0 : tile_list_sample( tile_str, sizeof(tile_str), &config->topo, tile_sample, tile_sample_cnt, tile_overlap_cnt );
446 0 : NOT_CONFIGURED( "found IRQs overlapping with Firedancer tile CPUs (IRQs: %s; tiles: %s)",
447 0 : irq_str,
448 0 : tile_str );
449 0 : }
450 : #if FD_IRQ_AFFINITY_CHECK_TIGHT
451 : if( FD_UNLIKELY( tight_irq_cnt ) ) {
452 : char tight_irq_str[ MISMATCH_STR_LEN ];
453 : append_ulong_list_sample( tight_irq_str, sizeof(tight_irq_str), tight_irq_sample, tight_irq_sample_cnt, tight_irq_cnt );
454 : NOT_CONFIGURED( "found IRQ affinity masks excluding allowed CPUs (IRQs: %s)", tight_irq_str );
455 : }
456 : #endif
457 0 : NOT_CONFIGURED( "%lu configurable IRQ affinity masks do not match Firedancer CPU policy", misconfigured );
458 0 : }
459 0 : CONFIGURE_OK();
460 0 : }
461 :
462 : configure_stage_t fd_cfg_stage_irq_affinity = {
463 : .name = "irq-affinity",
464 : .init_perm = init_perm,
465 : .fini_perm = fini_perm,
466 : .init = init,
467 : .fini = fini,
468 : .check = check
469 : };
470 :
471 : #undef FD_IRQ_AFFINITY_CHECK_TIGHT
472 : #undef SMP_AFFINITY_STR_LEN
473 : #undef MISMATCH_SAMPLE_MAX
474 : #undef MISMATCH_STR_LEN
475 : #undef MISMATCH_TILE_STR_LEN
|