Line data Source code
1 : /* irq-balance is the sibling of the irq-affinity configure stage.
2 : See the long doc comment in irq-affinity.c. */
3 :
4 : #define _DEFAULT_SOURCE
5 : #include "configure.h"
6 : #include "../../../../util/tile/fd_tile_private.h"
7 : #include "fd_cpu_isolation.h"
8 : #include "fd_irqbalance_client.h"
9 :
10 : #include <errno.h>
11 :
12 0 : #define MISMATCH_SAMPLE_MAX (16UL)
13 : #define MISMATCH_STR_LEN (128UL)
14 :
15 : static void
16 : init_perm( fd_cap_chk_t * chk,
17 0 : config_t const * config FD_PARAM_UNUSED ) {
18 0 : fd_cap_chk_root( chk, "irq-balance", "connect to `/run/irqbalance/<sock>`" );
19 0 : }
20 :
21 : static void
22 : fini_perm( fd_cap_chk_t * chk,
23 0 : config_t const * config FD_PARAM_UNUSED ) {
24 0 : fd_cap_chk_root( chk, "irq-balance", "connect to `/run/irqbalance/<sock>`" );
25 0 : }
26 :
27 : static char *
28 : cpuset_to_list_sample( char * buf,
29 : ulong buf_sz,
30 0 : fd_cpuset_t const * cpuset ) {
31 0 : char * p = fd_cstr_init( buf );
32 0 : ulong total_cnt = 0UL;
33 0 : for( ulong iter = fd_cpuset_const_iter_init( cpuset );
34 0 : !fd_cpuset_const_iter_done( iter );
35 0 : iter = fd_cpuset_const_iter_next( cpuset, iter ) ) {
36 0 : if( FD_LIKELY( total_cnt<MISMATCH_SAMPLE_MAX ) ) {
37 0 : if( FD_LIKELY( total_cnt ) ) p = fd_cstr_append_char( p, ',' );
38 0 : if( FD_UNLIKELY( (ulong)(p-buf)+32UL >= buf_sz ) ) break;
39 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, iter, fd_ulong_base10_dig_cnt( iter ) );
40 0 : }
41 0 : total_cnt++;
42 0 : }
43 0 : if( FD_UNLIKELY( total_cnt>MISMATCH_SAMPLE_MAX && (ulong)(p-buf)+32UL<buf_sz ) ) {
44 0 : p = fd_cstr_append_cstr( p, ",+" );
45 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, total_cnt-MISMATCH_SAMPLE_MAX, fd_ulong_base10_dig_cnt( total_cnt-MISMATCH_SAMPLE_MAX ) );
46 0 : p = fd_cstr_append_cstr( p, " more" );
47 0 : }
48 0 : fd_cstr_fini( p );
49 0 : return buf;
50 0 : }
51 :
52 : static configure_result_t
53 : check( config_t const * config,
54 0 : int check_type ) {
55 : /* The irqbalance daemon applies a `settings cpus` command only on its
56 : next periodic scan() tick (the SLEEP interval, 10s by default), and
57 : the `setup` query reports the committed mask, never the pending
58 : one. init() and fini() read the state back immediately, so the
59 : framework's synchronous POST_INIT / POST_FINI checks always observe
60 : the pre-change state and would spuriously fail ("didn't do
61 : anything" / "not undone"). There is no socket command to force a
62 : synchronous commit or to read back the pending ban, so these
63 : post-action checks are unverifiable; report the outcome the
64 : framework expects for a successful action and rely on init()/fini()
65 : having sent the command. POST_INIT wants OK (configured);
66 : POST_FINI wants NOT_CONFIGURED (undone). The standalone CHECK
67 : still reports the true committed state once the daemon has had a
68 : tick to apply it. */
69 0 : if( check_type==FD_CONFIGURE_CHECK_TYPE_POST_INIT ) CONFIGURE_OK();
70 0 : if( check_type==FD_CONFIGURE_CHECK_TYPE_POST_FINI ) NOT_CONFIGURED( "irqbalance commits asynchronously; undo not verifiable" );
71 :
72 0 : FD_CPUSET_DECL( actual );
73 0 : if( FD_UNLIKELY( -1==fd_irqbalance_ban_cpus_get( actual ) ) ) {
74 0 : int err = errno;
75 0 : switch( err ) {
76 0 : case ENOENT:
77 0 : case ECONNREFUSED:
78 : /* irqbalance is not installed or not running, so there is nothing
79 : to configure and nothing to undo. */
80 0 : CONFIGURE_OK();
81 0 : case EACCES:
82 : /* During the permission check phases, an EACCES means we need to
83 : escalate privileges before the real check; FINI_PERM treats it
84 : as OK so fini can proceed. Outside those phases it is a
85 : genuine misconfiguration. */
86 0 : if( check_type==FD_CONFIGURE_CHECK_TYPE_FINI_PERM ) CONFIGURE_OK();
87 0 : NOT_CONFIGURED( "insufficient permissions to query irqbalance daemon banned CPU list" );
88 0 : default:
89 0 : FD_LOG_ERR(( "fd_irqbalance_ban_cpus_get() failed unexpectedly (%i-%s)", err, fd_io_strerror( err ) ));
90 0 : }
91 0 : }
92 :
93 0 : FD_CPUSET_DECL( required );
94 0 : fd_cpu_isolation_banned_cpus( required, &config->topo );
95 :
96 0 : if( FD_LIKELY( !fd_cpuset_cnt( actual ) ) ) {
97 0 : if( FD_LIKELY( !fd_cpuset_cnt( required ) ) ) CONFIGURE_OK();
98 0 : NOT_CONFIGURED( "irqbalance daemon has no banned CPUs" );
99 0 : }
100 0 : if( FD_LIKELY( fd_cpuset_eq( actual, required ) ) ) CONFIGURE_OK();
101 :
102 0 : FD_CPUSET_DECL( mismatched );
103 0 : fd_cpuset_xor( mismatched, actual, required );
104 0 : char cpu_str[ MISMATCH_STR_LEN ];
105 0 : cpuset_to_list_sample( cpu_str, sizeof(cpu_str), mismatched );
106 0 : NOT_CONFIGURED( "irqbalance daemon banned CPU list does not match Firedancer tile CPUs (CPUs: %s)", cpu_str );
107 0 : }
108 :
109 : static void
110 0 : init( config_t const * config ) {
111 0 : FD_CPUSET_DECL( firedancer );
112 0 : fd_cpu_isolation_banned_cpus( firedancer, &config->topo );
113 :
114 0 : if( FD_UNLIKELY( -1==fd_irqbalance_ban_cpus_set( firedancer ) ) ) {
115 0 : int err = errno;
116 0 : if( FD_UNLIKELY( err!=ENOENT && err!=ECONNREFUSED && err!=EACCES ) ) {
117 0 : FD_LOG_ERR(( "fd_irqbalance_ban_cpus_set() failed unexpectedly (%i-%s)", err, fd_io_strerror( err ) ));
118 0 : }
119 0 : }
120 0 : }
121 :
122 : static int
123 : fini( config_t const * config,
124 0 : int pre_init ) {
125 0 : (void)config; (void)pre_init;
126 :
127 0 : FD_CPUSET_DECL( banned );
128 0 : fd_cpuset_new( banned );
129 :
130 0 : if( FD_UNLIKELY( -1==fd_irqbalance_ban_cpus_set( banned ) ) ) {
131 0 : int err = errno;
132 0 : if( FD_UNLIKELY( err!=ENOENT && err!=ECONNREFUSED && err!=EACCES ) ) {
133 0 : FD_LOG_ERR(( "fd_irqbalance_ban_cpus_set() failed unexpectedly (%i-%s)", err, fd_io_strerror( err ) ));
134 0 : }
135 0 : }
136 0 : return 1;
137 0 : }
138 :
139 : configure_stage_t fd_cfg_stage_irq_balance = {
140 : .name = "irq-balance",
141 : .always_recreate = 0,
142 : .init_perm = init_perm,
143 : .fini_perm = fini_perm,
144 : .init = init,
145 : .fini = fini,
146 : .check = check
147 : };
148 :
149 : #undef MISMATCH_SAMPLE_MAX
150 : #undef MISMATCH_STR_LEN
|