Line data Source code
1 : #include "fd_cpu_isolation.h"
2 :
3 : #include "../../../../disco/topo/fd_cpu_topo.h"
4 :
5 : #include <ctype.h>
6 : #include <errno.h>
7 : #include <fcntl.h>
8 : #include <string.h>
9 : #include <unistd.h>
10 :
11 : fd_cpuset_t *
12 : fd_cpu_isolation_tile_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
13 0 : fd_topo_t const * topo ) {
14 0 : fd_cpuset_new( cpuset );
15 0 : ulong cpu_cnt = fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX );
16 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
17 0 : fd_topo_tile_t const * tile = &topo->tiles[ i ];
18 0 : if( tile->cpu_idx<cpu_cnt ) fd_cpuset_insert( cpuset, tile->cpu_idx );
19 0 : }
20 0 : return cpuset;
21 0 : }
22 :
23 : fd_cpuset_t *
24 0 : fd_cpu_isolation_host_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ] ) {
25 0 : fd_cpuset_new( cpuset );
26 0 : ulong cpu_cnt = fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX );
27 0 : for( ulong cpu_idx=0UL; cpu_idx<cpu_cnt; cpu_idx++ ) fd_cpuset_insert( cpuset, cpu_idx );
28 0 : return cpuset;
29 0 : }
30 :
31 : int
32 : fd_cpu_isolation_parse_list( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
33 0 : char const * list ) {
34 0 : fd_cpuset_new( cpuset );
35 :
36 0 : char const * p = list;
37 0 : while( isspace( (uchar)*p ) ) p++;
38 0 : if( FD_UNLIKELY( !*p ) ) return 1; /* empty set */
39 :
40 : /* /sys/devices/system/cpu/nohz_full contains the literal string
41 : "(null)" on kernels with CONFIG_NO_HZ_FULL but no nohz_full= boot
42 : parameter. */
43 0 : if( FD_UNLIKELY( !strncmp( p, "(null)", 6UL ) ) ) return 1;
44 :
45 0 : for(;;) {
46 0 : if( FD_UNLIKELY( !isdigit( (uchar)*p ) ) ) return 0;
47 0 : ulong lo = 0UL;
48 0 : while( isdigit( (uchar)*p ) ) { lo = lo*10UL + (ulong)(*p-'0'); if( FD_UNLIKELY( lo>FD_TILE_MAX*2UL ) ) return 0; p++; }
49 0 : ulong hi = lo;
50 0 : if( FD_UNLIKELY( *p=='-' ) ) {
51 0 : p++;
52 0 : if( FD_UNLIKELY( !isdigit( (uchar)*p ) ) ) return 0;
53 0 : hi = 0UL;
54 0 : while( isdigit( (uchar)*p ) ) { hi = hi*10UL + (ulong)(*p-'0'); if( FD_UNLIKELY( hi>FD_TILE_MAX*2UL ) ) return 0; p++; }
55 0 : }
56 0 : if( FD_UNLIKELY( hi<lo ) ) return 0;
57 0 : for( ulong cpu=lo; cpu<=hi; cpu++ ) {
58 0 : if( FD_LIKELY( cpu<FD_TILE_MAX ) ) fd_cpuset_insert( cpuset, cpu );
59 0 : }
60 :
61 0 : if( FD_LIKELY( *p==',' ) ) { p++; continue; }
62 0 : while( isspace( (uchar)*p ) ) p++;
63 0 : if( FD_LIKELY( !*p ) ) return 1;
64 0 : return 0;
65 0 : }
66 0 : }
67 :
68 : char *
69 : fd_cpu_isolation_format_list( char * buf,
70 : ulong buf_sz,
71 0 : fd_cpuset_t const * cpuset ) {
72 0 : char * p = buf;
73 0 : char * end = buf+buf_sz-1UL;
74 0 : int first = 1;
75 :
76 0 : ulong cpu = 0UL;
77 0 : while( cpu<FD_TILE_MAX ) {
78 0 : if( FD_LIKELY( !fd_cpuset_test( cpuset, cpu ) ) ) { cpu++; continue; }
79 0 : ulong lo = cpu;
80 0 : while( cpu+1UL<FD_TILE_MAX && fd_cpuset_test( cpuset, cpu+1UL ) ) cpu++;
81 0 : ulong hi = cpu;
82 0 : cpu++;
83 :
84 0 : if( FD_UNLIKELY( (ulong)(end-p)<48UL ) ) break; /* can't overflow with LIST_MAX, defensive */
85 0 : if( FD_LIKELY( !first ) ) *p++ = ',';
86 0 : first = 0;
87 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, lo, fd_ulong_base10_dig_cnt( lo ) );
88 0 : if( FD_UNLIKELY( hi>lo ) ) {
89 0 : *p++ = '-';
90 0 : p = fd_cstr_append_ulong_as_text( p, 0, 0, hi, fd_ulong_base10_dig_cnt( hi ) );
91 0 : }
92 0 : }
93 0 : *p = '\0';
94 0 : return buf;
95 0 : }
96 :
97 : /* Tiles whose per-core throughput is sensitive enough to SMT resource
98 : sharing that their hyperthread sibling should be kept quiet. Keep
99 : in sync with the hyperthreads configure stage. */
100 :
101 : static char const * const smt_sensitive_tiles[] = { "pack", "pohh", "poh", NULL };
102 :
103 : char const *
104 0 : fd_cpu_isolation_partition_type( fd_topo_t const * topo ) {
105 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) if( topo->tiles[ i ].floats ) return "root";
106 0 : return "isolated";
107 0 : }
108 :
109 : fd_cpuset_t *
110 : fd_cpu_isolation_partition_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
111 0 : fd_topo_t const * topo ) {
112 0 : fd_cpu_isolation_tile_cpus( cpuset, topo );
113 :
114 0 : fd_topo_cpus_t cpus[1];
115 0 : fd_topo_cpus_init( cpus );
116 :
117 0 : for( char const * const * name=smt_sensitive_tiles; *name; name++ ) {
118 0 : ulong tile_idx = fd_topo_find_tile( topo, *name, 0UL );
119 0 : if( FD_LIKELY( tile_idx==ULONG_MAX ) ) continue;
120 :
121 0 : ulong cpu_idx = topo->tiles[ tile_idx ].cpu_idx;
122 0 : if( FD_UNLIKELY( cpu_idx>=fd_ulong_min( cpus->cpu_cnt, FD_TILE_MAX ) ) ) continue;
123 0 : if( FD_UNLIKELY( topo->tiles[ tile_idx ].floats ) ) continue; /* not alone on that core anyway */
124 :
125 0 : ulong sibling = cpus->cpu[ cpu_idx ].sibling;
126 0 : if( FD_UNLIKELY( sibling==ULONG_MAX || sibling>=FD_TILE_MAX ) ) continue; /* no sibling */
127 0 : if( FD_UNLIKELY( !cpus->cpu[ sibling ].online ) ) continue; /* offline is even better */
128 0 : if( FD_UNLIKELY( fd_cpuset_test( cpuset, sibling ) ) ) continue; /* used by another tile */
129 :
130 0 : fd_cpuset_insert( cpuset, sibling );
131 0 : }
132 0 : return cpuset;
133 0 : }
134 :
135 : int
136 : fd_cpu_isolation_parse_mask( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
137 0 : char const * mask ) {
138 0 : fd_cpuset_new( cpuset );
139 :
140 0 : char const * end = mask;
141 0 : while( *end && !isspace( (uchar)*end ) ) end++;
142 0 : if( FD_UNLIKELY( end==mask ) ) return 0;
143 :
144 0 : ulong cpu_idx = 0UL;
145 0 : for( char const * p=end; p>mask; ) {
146 0 : p--;
147 0 : if( FD_UNLIKELY( *p==',' ) ) continue;
148 :
149 0 : int digit;
150 0 : if( FD_LIKELY( (*p>='0') & (*p<='9') ) ) digit = *p-'0';
151 0 : else if( FD_LIKELY( (*p>='a') & (*p<='f') ) ) digit = 10+*p-'a';
152 0 : else if( FD_LIKELY( (*p>='A') & (*p<='F') ) ) digit = 10+*p-'A';
153 0 : else return 0;
154 :
155 0 : for( ulong bit=0UL; bit<4UL; bit++ ) {
156 0 : if( FD_UNLIKELY( cpu_idx>=FD_TILE_MAX ) ) return 1;
157 0 : if( FD_UNLIKELY( digit & (1<<bit) ) ) fd_cpuset_insert( cpuset, cpu_idx );
158 0 : cpu_idx++;
159 0 : }
160 0 : }
161 0 : return 1;
162 0 : }
163 :
164 : char *
165 : fd_cpu_isolation_format_mask( char * buf,
166 : ulong buf_sz,
167 0 : fd_cpuset_t const * cpuset ) {
168 0 : ulong cpu_cnt = fd_ulong_max( fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX ), 1UL );
169 0 : ulong word_cnt = fd_ulong_max( (cpu_cnt+31UL)/32UL, 1UL );
170 0 : char * p = buf;
171 :
172 0 : FD_TEST( buf_sz>=word_cnt*9UL+1UL );
173 :
174 0 : for( ulong word_rem=word_cnt; word_rem; word_rem-- ) {
175 0 : ulong word_idx = word_rem-1UL;
176 0 : if( FD_UNLIKELY( word_idx!=word_cnt-1UL ) ) *p++ = ',';
177 :
178 0 : for( ulong nib_rem=8UL; nib_rem; nib_rem-- ) {
179 0 : ulong nib_idx = nib_rem-1UL;
180 0 : int digit = 0;
181 0 : for( ulong bit=0UL; bit<4UL; bit++ ) {
182 0 : ulong cpu_idx = word_idx*32UL + nib_idx*4UL + bit;
183 0 : if( FD_LIKELY( cpu_idx<FD_TILE_MAX && fd_cpuset_test( cpuset, cpu_idx ) ) ) digit |= (int)(1UL<<bit);
184 0 : }
185 0 : *p++ = "0123456789abcdef"[ digit ];
186 0 : }
187 0 : }
188 0 : *p = '\0';
189 0 : return buf;
190 0 : }
191 :
192 :
193 : int
194 : fd_cpu_isolation_read_list( char const * path,
195 0 : fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ] ) {
196 0 : int fd = open( path, O_RDONLY );
197 0 : if( FD_UNLIKELY( fd<0 ) ) {
198 0 : if( FD_LIKELY( errno==ENOENT ) ) return 0;
199 0 : FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
200 0 : }
201 :
202 0 : char list[ FD_CPU_ISOLATION_LIST_MAX ];
203 0 : long list_len = read( fd, list, sizeof(list)-1UL );
204 0 : if( FD_UNLIKELY( list_len<0L ) ) FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
205 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
206 0 : list[ list_len ] = '\0';
207 :
208 0 : if( FD_UNLIKELY( !fd_cpu_isolation_parse_list( cpuset, list ) ) )
209 0 : FD_LOG_ERR(( "failed to parse `%s` (\"%s\")", path, list ));
210 0 : return 1;
211 0 : }
|