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 : fd_cpuset_t *
104 : fd_cpu_isolation_partition_cpus( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
105 0 : fd_topo_t const * topo ) {
106 0 : fd_cpu_isolation_tile_cpus( cpuset, topo );
107 :
108 0 : fd_topo_cpus_t cpus[1];
109 0 : fd_topo_cpus_init( cpus );
110 :
111 0 : for( char const * const * name=smt_sensitive_tiles; *name; name++ ) {
112 0 : ulong tile_idx = fd_topo_find_tile( topo, *name, 0UL );
113 0 : if( FD_LIKELY( tile_idx==ULONG_MAX ) ) continue;
114 :
115 0 : ulong cpu_idx = topo->tiles[ tile_idx ].cpu_idx;
116 0 : if( FD_UNLIKELY( cpu_idx>=fd_ulong_min( cpus->cpu_cnt, FD_TILE_MAX ) ) ) continue;
117 :
118 0 : ulong sibling = cpus->cpu[ cpu_idx ].sibling;
119 0 : if( FD_UNLIKELY( sibling==ULONG_MAX || sibling>=FD_TILE_MAX ) ) continue; /* no sibling */
120 0 : if( FD_UNLIKELY( !cpus->cpu[ sibling ].online ) ) continue; /* offline is even better */
121 0 : if( FD_UNLIKELY( fd_cpuset_test( cpuset, sibling ) ) ) continue; /* used by another tile */
122 :
123 0 : fd_cpuset_insert( cpuset, sibling );
124 0 : }
125 0 : return cpuset;
126 0 : }
127 :
128 : int
129 : fd_cpu_isolation_parse_mask( fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ],
130 0 : char const * mask ) {
131 0 : fd_cpuset_new( cpuset );
132 :
133 0 : char const * end = mask;
134 0 : while( *end && !isspace( (uchar)*end ) ) end++;
135 0 : if( FD_UNLIKELY( end==mask ) ) return 0;
136 :
137 0 : ulong cpu_idx = 0UL;
138 0 : for( char const * p=end; p>mask; ) {
139 0 : p--;
140 0 : if( FD_UNLIKELY( *p==',' ) ) continue;
141 :
142 0 : int digit;
143 0 : if( FD_LIKELY( (*p>='0') & (*p<='9') ) ) digit = *p-'0';
144 0 : else if( FD_LIKELY( (*p>='a') & (*p<='f') ) ) digit = 10+*p-'a';
145 0 : else if( FD_LIKELY( (*p>='A') & (*p<='F') ) ) digit = 10+*p-'A';
146 0 : else return 0;
147 :
148 0 : for( ulong bit=0UL; bit<4UL; bit++ ) {
149 0 : if( FD_UNLIKELY( cpu_idx>=FD_TILE_MAX ) ) return 1;
150 0 : if( FD_UNLIKELY( digit & (1<<bit) ) ) fd_cpuset_insert( cpuset, cpu_idx );
151 0 : cpu_idx++;
152 0 : }
153 0 : }
154 0 : return 1;
155 0 : }
156 :
157 : char *
158 : fd_cpu_isolation_format_mask( char * buf,
159 : ulong buf_sz,
160 0 : fd_cpuset_t const * cpuset ) {
161 0 : ulong cpu_cnt = fd_ulong_max( fd_ulong_min( fd_shmem_cpu_cnt(), FD_TILE_MAX ), 1UL );
162 0 : ulong word_cnt = fd_ulong_max( (cpu_cnt+31UL)/32UL, 1UL );
163 0 : char * p = buf;
164 :
165 0 : FD_TEST( buf_sz>=word_cnt*9UL+1UL );
166 :
167 0 : for( ulong word_rem=word_cnt; word_rem; word_rem-- ) {
168 0 : ulong word_idx = word_rem-1UL;
169 0 : if( FD_UNLIKELY( word_idx!=word_cnt-1UL ) ) *p++ = ',';
170 :
171 0 : for( ulong nib_rem=8UL; nib_rem; nib_rem-- ) {
172 0 : ulong nib_idx = nib_rem-1UL;
173 0 : int digit = 0;
174 0 : for( ulong bit=0UL; bit<4UL; bit++ ) {
175 0 : ulong cpu_idx = word_idx*32UL + nib_idx*4UL + bit;
176 0 : if( FD_LIKELY( cpu_idx<FD_TILE_MAX && fd_cpuset_test( cpuset, cpu_idx ) ) ) digit |= (int)(1UL<<bit);
177 0 : }
178 0 : *p++ = "0123456789abcdef"[ digit ];
179 0 : }
180 0 : }
181 0 : *p = '\0';
182 0 : return buf;
183 0 : }
184 :
185 :
186 : int
187 : fd_cpu_isolation_read_list( char const * path,
188 0 : fd_cpuset_t cpuset[ static fd_cpuset_word_cnt ] ) {
189 0 : int fd = open( path, O_RDONLY );
190 0 : if( FD_UNLIKELY( fd<0 ) ) {
191 0 : if( FD_LIKELY( errno==ENOENT ) ) return 0;
192 0 : FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
193 0 : }
194 :
195 0 : char list[ FD_CPU_ISOLATION_LIST_MAX ];
196 0 : long list_len = read( fd, list, sizeof(list)-1UL );
197 0 : if( FD_UNLIKELY( list_len<0L ) ) FD_LOG_ERR(( "read(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
198 0 : if( FD_UNLIKELY( close( fd ) ) ) FD_LOG_ERR(( "close(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
199 0 : list[ list_len ] = '\0';
200 :
201 0 : if( FD_UNLIKELY( !fd_cpu_isolation_parse_list( cpuset, list ) ) )
202 0 : FD_LOG_ERR(( "failed to parse `%s` (\"%s\")", path, list ));
203 0 : return 1;
204 0 : }
|