Line data Source code
1 : #include "fd_config_auto.h"
2 : #include "../../disco/net/fd_linux_bond.h"
3 : #include "../../disco/net/fd_net_tile.h"
4 :
5 : #include <stdlib.h> /* strtoul */
6 : #include <limits.h>
7 : #include <errno.h>
8 : #include <string.h>
9 : #include <unistd.h>
10 : #include <sys/utsname.h>
11 :
12 30 : #define PROVIDER_CNT ( 1UL)
13 75 : #define FEAT_CNT_MAX ( 8UL)
14 84 : #define NET_DRIVER_CNT_MAX (16UL)
15 :
16 : struct fd_auto_info {
17 : /* System */
18 : ulong linux_major;
19 : ulong linux_minor;
20 :
21 : /* Networking */
22 : char driver[ NAME_SZ ];
23 : int is_virtual_if;
24 : int is_bonded_if;
25 : uint bonded_if_slave_count;
26 : };
27 : typedef struct fd_auto_info fd_auto_info_t;
28 :
29 : struct fd_auto_driver_rq {
30 : char const * name;
31 : ulong min_linux_major;
32 : ulong min_linux_minor;
33 :
34 : /* 0 = no max linux version */
35 : ulong max_linux_major;
36 : ulong max_linux_minor;
37 : };
38 : typedef struct fd_auto_driver_rq fd_auto_driver_rq_t;
39 :
40 : struct fd_auto_feat {
41 : char const * name;
42 : fd_auto_driver_rq_t supported_drivers[ NET_DRIVER_CNT_MAX ];
43 :
44 : /* Checks if networking feature is auto and sets the default */
45 : int (*is_feat_auto)( fd_config_t * config );
46 : int (*check )( fd_config_t const * config,
47 : fd_auto_info_t const * info );
48 : void (*apply )( fd_config_t * config );
49 : };
50 : typedef struct fd_auto_feat fd_auto_feat_t;
51 :
52 : struct fd_auto_provider {
53 : char const * name;
54 : /* Later features observe earlier features updates to config,
55 : so the order is important. e.g. ZC checks xdp_mode is DRV
56 : so DRV needs to come before ZC in the feats ordering. */
57 : fd_auto_feat_t feats[ FEAT_CNT_MAX ];
58 :
59 : int (*check)( fd_config_t const * config,
60 : fd_auto_info_t const * info );
61 : };
62 : typedef struct fd_auto_provider fd_auto_provider_t;
63 :
64 : /* XDP checks/apply */
65 :
66 : static int
67 : check_xdp_auto( fd_config_t const * config,
68 15 : fd_auto_info_t const * info FD_PARAM_UNUSED ) {
69 15 : if( strcmp( config->net.provider, "xdp" ) ) return 0;
70 12 : return 1;
71 15 : }
72 :
73 : static int
74 15 : is_xdp_native_bond_auto( fd_config_t * config ) {
75 15 : if( 2!=config->net.xdp.native_bond ) return 0;
76 : /* Set to default */
77 15 : config->net.xdp.native_bond = 0;
78 15 : return 1;
79 15 : }
80 :
81 : static int
82 : xdp_native_bond_check( fd_config_t const * config,
83 6 : fd_auto_info_t const * info ) {
84 6 : if( !info->is_bonded_if ) return 0;
85 :
86 3 : if( 0==info->bonded_if_slave_count || info->bonded_if_slave_count>FD_NET_BOND_SLAVE_MAX ) return 0;
87 :
88 : /* net_tile_count must be a multiple of the bonded interface's slave count */
89 3 : if( 0!=config->layout.net_tile_count%info->bonded_if_slave_count ) return 0;
90 :
91 3 : return 1;
92 3 : }
93 :
94 : static void
95 3 : xdp_native_bond_apply( fd_config_t * config ) {
96 3 : config->net.xdp.native_bond = 1;
97 3 : }
98 :
99 : static int
100 15 : is_xdp_mode_auto( fd_config_t * config ) {
101 15 : if( strcmp( config->net.xdp.xdp_mode, "auto" ) ) return 0;
102 : /* Set to default */
103 15 : fd_memcpy( config->net.xdp.xdp_mode, "skb", 4 );
104 15 : return 1;
105 15 : }
106 :
107 : static int
108 : xdp_drv_check( fd_config_t const * config,
109 6 : fd_auto_info_t const * info ) {
110 6 : if( info->is_virtual_if &&
111 6 : !config->net.xdp.native_bond ) return 0;
112 6 : return 1;
113 6 : }
114 :
115 : static void
116 6 : xdp_drv_apply( fd_config_t * config ) {
117 6 : fd_memcpy( config->net.xdp.xdp_mode, "drv", 4 );
118 6 : }
119 :
120 : static int
121 15 : is_xdp_prefbusy_auto( fd_config_t * config ) {
122 15 : if( strcmp( config->net.xdp.poll_mode, "auto" ) ) return 0;
123 : /* Set to default */
124 15 : fd_memcpy( config->net.xdp.poll_mode, "softirq", 8 );
125 15 : return 1;
126 15 : }
127 :
128 : static int
129 : xdp_prefbusy_check( fd_config_t const * config,
130 6 : fd_auto_info_t const * info FD_PARAM_UNUSED ) {
131 6 : if( strcmp( config->net.xdp.xdp_mode, "drv") ) return 0;
132 6 : return 1;
133 6 : }
134 :
135 : static void
136 6 : xdp_prefbusy_apply( fd_config_t * config ) {
137 6 : fd_memcpy( config->net.xdp.poll_mode, "prefbusy", 9 );
138 6 : }
139 :
140 : static int
141 15 : is_xdp_zc_auto( fd_config_t * config ) {
142 15 : if( 2!=config->net.xdp.xdp_zero_copy ) return 0;
143 : /* Set to default */
144 15 : config->net.xdp.xdp_zero_copy = 0;
145 15 : return 1;
146 15 : }
147 :
148 : static int
149 : xdp_zc_check( fd_config_t const * config FD_PARAM_UNUSED,
150 6 : fd_auto_info_t const * info FD_PARAM_UNUSED ) {
151 : /* zc turned off for initial config auto rollout just to
152 : be safe. */
153 6 : return 0;
154 6 : }
155 :
156 : static void
157 0 : xdp_zc_apply( fd_config_t * config ) {
158 0 : config->net.xdp.xdp_zero_copy = 1;
159 0 : }
160 :
161 : /* Each feature's supported Linux version matrix per driver decided
162 : based on fd-linux-version-testbed test results. */
163 : static const fd_auto_provider_t NET_PROVIDERS[] = {
164 : {
165 : .name = "XDP",
166 : .check = check_xdp_auto,
167 : .feats = {
168 : {
169 : .name = "Native Bond",
170 : .supported_drivers = { { "mlx5_core", 5, 15, 0, 0} },
171 : .is_feat_auto = is_xdp_native_bond_auto,
172 : .check = xdp_native_bond_check,
173 : .apply = xdp_native_bond_apply
174 : },
175 : {
176 : .name = "DRV",
177 : .supported_drivers = {
178 : { "mlx5_core", 5, 15, 6, 3 },
179 : { "mlx5_core", 6, 5, 0, 0 },
180 : },
181 : .is_feat_auto = is_xdp_mode_auto,
182 : .check = xdp_drv_check,
183 : .apply = xdp_drv_apply,
184 : },
185 : {
186 : .name = "Prefbusy",
187 : .supported_drivers = { { "mlx5_core", 5, 15, 0, 0 } },
188 : .is_feat_auto = is_xdp_prefbusy_auto,
189 : .check = xdp_prefbusy_check,
190 : .apply = xdp_prefbusy_apply
191 : },
192 : {
193 : .name = "Zero Copy",
194 : .supported_drivers = { { "mlx5_core", 6, 1, 0, 0 } },
195 : .is_feat_auto = is_xdp_zc_auto,
196 : .check = xdp_zc_check,
197 : .apply = xdp_zc_apply
198 : }
199 : }
200 : }
201 : };
202 :
203 : static void
204 0 : scrape_system( fd_auto_info_t * info ) {
205 0 : struct utsname utsname;
206 0 : if( FD_UNLIKELY( -1==uname( &utsname ) ) ) {
207 0 : FD_LOG_WARNING(( "uname() failed (%i-%s), auto config skipping kernel version detection",
208 0 : errno, fd_io_strerror( errno ) ));
209 0 : return;
210 0 : }
211 :
212 : /* utsname.release is typically "major.minor.patch-distrojunk",
213 : e.g. "6.8.0-45-generic" or "5.14.0-362.8.1.el9_3.x86_64". We
214 : only care about major.minor. A missing ".minor" is treated as
215 : minor 0. An unparseable release leaves {0,0}, which fails all
216 : version requirements (fail-soft). */
217 :
218 0 : char const * cur = utsname.release;
219 0 : char * end = NULL;
220 :
221 0 : ulong major = strtoul( cur, &end, 10 );
222 0 : if( FD_UNLIKELY( end==cur ) ) {
223 0 : FD_LOG_WARNING(( "unrecognized kernel release `%s`, auto config skipping kernel version detection",
224 0 : utsname.release ));
225 0 : return;
226 0 : }
227 :
228 0 : ulong minor = 0UL;
229 0 : if( FD_LIKELY( end[0]=='.' ) ) {
230 0 : cur = end+1;
231 0 : minor = strtoul( cur, &end, 10 );
232 0 : if( FD_UNLIKELY( end==cur ) ) minor = 0UL; /* tolerate "6." */
233 0 : }
234 :
235 0 : info->linux_major = major;
236 0 : info->linux_minor = minor;
237 0 : }
238 :
239 : static void
240 : scrape_driver( char * driver,
241 0 : char const * if_name ) {
242 0 : char path[ PATH_MAX ], target[ PATH_MAX ];
243 0 : driver[0] = '\0';
244 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/device/driver", if_name ) );
245 :
246 0 : long n = readlink( path, target, PATH_MAX-1 );
247 0 : if( FD_UNLIKELY( n<0L ) ) return;
248 0 : target[ n ] = '\0'; /* readlink does not NUL-terminate */
249 :
250 0 : char const * base = strrchr( target, '/' );
251 0 : fd_cstr_ncpy( driver, base ? base+1 : target, NAME_SZ );
252 0 : }
253 :
254 : static void
255 : scrape_networking( fd_auto_info_t * info,
256 0 : char const * if_name ) {
257 : /* Check for virtual interface */
258 0 : char path[ PATH_MAX ];
259 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/device", if_name ) );
260 0 : info->is_virtual_if = ( -1==access( path, F_OK ) );
261 :
262 : /* Check for bond */
263 0 : info->is_bonded_if = fd_bonding_is_master( if_name );
264 0 : if( info->is_bonded_if ) info->bonded_if_slave_count = (uint)fd_bonding_slave_cnt( if_name );
265 :
266 : /* Get driver name. A bond master reports "n/a" so no driver specific
267 : config is applied. If all slaves share a driver name then reports
268 : that instead. */
269 0 : if( info->is_bonded_if ) {
270 0 : fd_bonding_slave_iter_t iter_[1];
271 0 : for( fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, if_name );
272 0 : !fd_bonding_slave_iter_done( iter );
273 0 : fd_bonding_slave_iter_next ( iter ) ) {
274 :
275 0 : char slave_driver[ NAME_SZ ];
276 0 : scrape_driver( slave_driver, fd_bonding_slave_iter_ele( iter ) );
277 0 : if( FD_UNLIKELY( !slave_driver[0] ||
278 0 : ( info->driver[0] && strcmp( info->driver, slave_driver ) ) ) ) {
279 0 : fd_memcpy( info->driver, "n/a", 4 );
280 0 : break;
281 0 : }
282 0 : fd_cstr_ncpy( info->driver, slave_driver, NAME_SZ );
283 0 : }
284 0 : } else {
285 0 : scrape_driver( info->driver, if_name );
286 0 : }
287 0 : }
288 :
289 : static fd_auto_info_t
290 0 : fd_auto_scrape_info( fd_config_t const * config ) {
291 0 : fd_auto_info_t info = {0};
292 :
293 0 : scrape_system ( &info );
294 0 : scrape_networking( &info, config->net.interface );
295 :
296 0 : return info;
297 0 : }
298 :
299 : static int
300 : fd_auto_check_driver( fd_auto_info_t const * info,
301 48 : fd_auto_driver_rq_t const * supported_drivers ) {
302 : /* Check for driver requirements. */
303 48 : if( !supported_drivers[0].name ) return 1;
304 :
305 84 : for( ulong i=0UL; i<NET_DRIVER_CNT_MAX; i++ ) {
306 84 : fd_auto_driver_rq_t const * req = &supported_drivers[ i ];
307 84 : if( !req->name ) return 0;
308 :
309 60 : if( 0==strcmp( info->driver, req->name ) ) {
310 : /* Check system linux version is within bounds of min and max
311 : of what this driver req requires. */
312 45 : if( req->max_linux_major > 0UL ) {
313 9 : if( info->linux_major>req->max_linux_major ||
314 9 : ( info->linux_major==req->max_linux_major &&
315 6 : info->linux_minor>req->max_linux_minor ) ) continue;
316 9 : }
317 :
318 :
319 39 : if( info->linux_major>req->min_linux_major ||
320 39 : ( info->linux_major==req->min_linux_major &&
321 24 : info->linux_minor>=req->min_linux_minor ) ) return 1;
322 39 : }
323 60 : }
324 :
325 0 : return 0;
326 48 : }
327 :
328 : /* fd_auto_net resolves any "auto" fields in the networking
329 : configuration. The resulting configuration is optimized for the system
330 : but won't always maximally optimize, this is to reduce risk of failures. */
331 : static void
332 : fd_auto_net( fd_config_t * config,
333 15 : fd_auto_info_t const * info ) {
334 : /* Providers are in order of priority with first being the highest */
335 15 : int is_provider_set = 0;
336 30 : for( ulong p=0UL; p<PROVIDER_CNT; p++ ) {
337 15 : fd_auto_provider_t const * provider = &NET_PROVIDERS[p];
338 :
339 : /* Check provider requirements */
340 15 : int is_chosen_provider = !is_provider_set && ( !provider->check || provider->check( config, info ) );
341 15 : if( is_chosen_provider ) is_provider_set = 1;
342 :
343 75 : for( ulong f=0UL; f<FEAT_CNT_MAX; f++ ) {
344 75 : fd_auto_feat_t const * feat = &provider->feats[f];
345 75 : if( !feat->name ) break;
346 :
347 : /* Checks feature is set to "auto", if so replaces "auto" with
348 : default and goes onto the next checks */
349 60 : if( !feat->is_feat_auto( config ) ) continue;
350 :
351 60 : if( !is_chosen_provider ||
352 60 : !strcmp( config->net.auto_level, "minimal" ) ) continue;
353 :
354 : /* Feature's driver/Linux version requirements */
355 48 : if( !fd_auto_check_driver( info, feat->supported_drivers ) ) continue;
356 :
357 : /* Feature's other requirements */
358 24 : if( feat->check && !feat->check( config, info ) ) continue;
359 :
360 15 : if( FD_UNLIKELY( !feat->apply ) ) {
361 0 : FD_LOG_ERR(( "Applying auto configure net feature %s failed since no apply function was found.", feat->name ));
362 0 : }
363 15 : feat->apply( config );
364 15 : }
365 15 : }
366 15 : }
367 :
368 : void
369 0 : fd_config_auto( fd_config_t * config ) {
370 0 : fd_auto_info_t info = fd_auto_scrape_info( config );
371 0 : fd_auto_net( config, &info );
372 :
373 0 : fd_cstr_printf( config->auto_config_log, sizeof(config->auto_config_log), NULL,
374 0 : "network auto configure level=%s, provider=%s xdp_mode=%s poll_mode=%s zero_copy=%d native_bond=%d (driver=%s kernel=%lu.%lu virtual_if=%d bonded_if=%d slaves=%u, net_tile_cnt=%u)",
375 0 : config->net.auto_level,
376 0 : config->net.provider,
377 0 : config->net.xdp.xdp_mode,
378 0 : config->net.xdp.poll_mode,
379 0 : config->net.xdp.xdp_zero_copy,
380 0 : config->net.xdp.native_bond,
381 0 : info.driver[0] ? info.driver : "unknown",
382 0 : info.linux_major, info.linux_minor,
383 0 : info.is_virtual_if,
384 0 : info.is_bonded_if,
385 0 : info.bonded_if_slave_count,
386 0 : config->layout.net_tile_count );
387 0 : }
|