Line data Source code
1 : #include "configure.h"
2 :
3 : #include <errno.h>
4 : #include <unistd.h>
5 :
6 : #include "fd_ethtool_ioctl.h"
7 : #include "../../../../disco/net/fd_linux_bond.h"
8 :
9 0 : #define NAME "ethtool-channels"
10 :
11 : static int fini_device( char const * device );
12 :
13 : static int
14 0 : enabled( fd_config_t const * config ) {
15 :
16 : /* only enable if network stack is XDP */
17 0 : if( 0!=strcmp( config->net.provider, "xdp" ) ) return 0;
18 :
19 0 : return 1;
20 0 : }
21 :
22 : static void
23 : init_perm( fd_cap_chk_t * chk,
24 0 : fd_config_t const * config FD_PARAM_UNUSED ) {
25 0 : fd_cap_chk_root( chk, NAME, "modify network device configuration with ethtool" );
26 0 : }
27 :
28 : static void
29 : fini_perm( fd_cap_chk_t * chk,
30 0 : fd_config_t const * config FD_PARAM_UNUSED ) {
31 0 : fd_cap_chk_root( chk, NAME, "modify network device configuration with ethtool" );
32 0 : }
33 :
34 : /* FIXME: Centrally define listen port list to avoid this configure
35 : stage from going out of sync with port mappings. */
36 : static uint
37 : get_ports( fd_config_t const * config,
38 0 : ushort * ports ) {
39 0 : uint port_cnt = 0U;
40 :
41 0 : #define ADD_PORT( p ) do { \
42 0 : ushort __port = ( p ); \
43 0 : if( FD_UNLIKELY( __port==0U ) ) break; \
44 0 : int __dupe = 0; \
45 0 : for( uint __p=0U; !__dupe && __p<port_cnt; ++__p ) __dupe = (ports[ __p ]==__port); \
46 0 : if( FD_UNLIKELY( __dupe ) ) break; \
47 0 : ports[ port_cnt ] = __port; \
48 0 : port_cnt++; \
49 0 : } while(0)
50 :
51 0 : ADD_PORT( config->tiles.shred.shred_listen_port );
52 0 : ADD_PORT( config->tiles.quic.quic_transaction_listen_port );
53 0 : ADD_PORT( config->tiles.quic.regular_transaction_listen_port );
54 0 : if( config->is_firedancer ) {
55 0 : ADD_PORT( config->gossip.port );
56 0 : ADD_PORT( config->tiles.repair.repair_intake_listen_port );
57 0 : ADD_PORT( config->tiles.repair.repair_serve_listen_port );
58 0 : ADD_PORT( config->tiles.txsend.txsend_src_port );
59 0 : }
60 0 : #undef ADD_PORT
61 :
62 0 : return port_cnt;
63 0 : }
64 :
65 : /* Attempts to initialize the device in simple or dedicated mode. If
66 : strict is true, FD_LOG_ERR's on failure. Otherwise, returns 1 on
67 : failure. Returns 0 on success. */
68 : static int
69 : init_device( char const * device,
70 : fd_config_t const * config,
71 : int dedicated_mode,
72 : int strict,
73 0 : uint device_cnt ) {
74 0 : FD_TEST( dedicated_mode || strict );
75 :
76 0 : uint const net_tile_cnt = config->layout.net_tile_count;
77 0 : if( FD_UNLIKELY( net_tile_cnt%device_cnt!=0 ) ) {
78 0 : FD_LOG_ERR(( "net tile count %u must be a multiple of the number of slave devices %u (incompatible settings [layout.net_tile_count] and [net.xdp.native_bond])", net_tile_cnt, device_cnt ));
79 0 : }
80 0 : uint const queue_cnt = net_tile_cnt / device_cnt;
81 :
82 0 : fd_ethtool_ioctl_t ioc __attribute__((cleanup(fd_ethtool_ioctl_fini)));
83 0 : if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
84 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
85 :
86 : /* This should happen first, otherwise changing the number of channels may fail */
87 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_set_default( &ioc ) );
88 :
89 0 : uint const num_channels = !dedicated_mode ? queue_cnt : 0 /* maximum allowed */;
90 0 : int ret = fd_ethtool_ioctl_channels_set_num( &ioc, num_channels );
91 0 : if( FD_UNLIKELY( 0!=ret ) ) {
92 0 : if( strict ) {
93 0 : if( FD_LIKELY( ret == EBUSY ) )
94 0 : FD_LOG_ERR(( "error configuring network device (%s), failed to set number of channels. "
95 0 : "This is most commonly caused by an issue with the Intel ice driver on certain versions "
96 0 : "of Ubuntu. If you are using the ice driver, `sudo dmesg | grep %s` contains "
97 0 : "messages about RDMA, and you do not need RDMA, try running `rmmod irdma` and/or "
98 0 : "blacklisting the irdma kernel module.", device, device ));
99 0 : else
100 0 : FD_LOG_ERR(( "error configuring network device (%s), failed to set number of channels", device ));
101 0 : }
102 0 : return 1;
103 0 : }
104 :
105 : /* Some drivers (e.g. igb) put the RXFH table into an incorrect state
106 : after changing the channel count. So in simple mode we reset it
107 : to the default again. */
108 0 : if( !dedicated_mode ) {
109 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_set_default( &ioc ) );
110 :
111 : /* Configure the NIC to include UDP source and destination ports in
112 : the RSS hash for UDP/IPv4 flows. Without this, most NICs default
113 : to hashing only on IP addresses, which causes severe RX queue
114 : imbalance when all traffic targets the same destination IP and
115 : port (e.g. gossip). Not needed in dedicated mode since ntuple
116 : rules bypass RSS for Firedancer traffic. */
117 0 : if( FD_UNLIKELY( 0!=fd_ethtool_ioctl_rxfh_set_flow_hash_udp4( &ioc ) ) )
118 0 : FD_LOG_WARNING(( "failed to set UDP flow hash on device %s, RSS distribution may be poor", device ));
119 0 : }
120 :
121 0 : FD_TEST( 0==fd_ethtool_ioctl_ntuple_clear( &ioc ) );
122 :
123 0 : if( dedicated_mode ) {
124 : /* Some drivers (e.g. ixgbe) reset the RXFH table upon activation
125 : of the ntuple feature, so we do this first. */
126 0 : if( FD_UNLIKELY( 0!=fd_ethtool_ioctl_feature_set( &ioc, FD_ETHTOOL_FEATURE_NTUPLE, 1 ) ) ) {
127 0 : if( strict ) FD_LOG_ERR(( "error configuring network device (%s), failed to enable ntuple feature. Try `net.xdp.rss_queue_mode=\"simple\"`", device ));
128 0 : else return 1;
129 0 : }
130 :
131 : /* Remove a queue from the rxfh table for each net tile. */
132 0 : uint rxfh_queue_cnt;
133 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_queue_cnt( &ioc, &rxfh_queue_cnt ) );
134 0 : if( FD_UNLIKELY( queue_cnt>=rxfh_queue_cnt ) ) {
135 0 : if( strict ) FD_LOG_ERR(( "error configuring network device (%s), too many net tiles %u for queue count %u. "
136 0 : "Try `net.xdp.rss_queue_mode=\"simple\"` or reduce net tile count",
137 0 : device, net_tile_cnt, rxfh_queue_cnt ));
138 0 : else return 1;
139 0 : }
140 0 : if( FD_UNLIKELY( 0!=fd_ethtool_ioctl_rxfh_set_suffix( &ioc, queue_cnt ) ) ) {
141 0 : if( strict ) FD_LOG_ERR(( "error configuring network device (%s), failed to isolate queues. Try `net.xdp.rss_queue_mode=\"simple\"`", device ));
142 0 : else return 1;
143 0 : }
144 :
145 : /* Add a ntuple rule for each listening destination port. If there
146 : are multiple net tiles, create a group of rules for each tile. */
147 0 : int ntuple_error = 0;
148 0 : ushort ports[ 32 ];
149 0 : uint port_cnt = get_ports( config, ports );
150 0 : uint rule_idx = 0;
151 0 : uint const rule_group_cnt = fd_uint_pow2_up( queue_cnt );
152 0 : for( uint r=0U; !ntuple_error && r<rule_group_cnt; r++ ) {
153 0 : for( uint p=0U; !ntuple_error && p<port_cnt; p++ ) {
154 0 : ntuple_error = 0!=fd_ethtool_ioctl_ntuple_set_udp_dport( &ioc, rule_idx++, ports[ p ], r, rule_group_cnt, r%queue_cnt );
155 0 : }
156 0 : }
157 0 : if( FD_UNLIKELY( ntuple_error ) ) {
158 0 : if( strict ) FD_LOG_ERR(( "error configuring network device (%s), failed to install ntuple rules. "
159 0 : "Try `net.xdp.rss_queue_mode=\"simple\"` or `layout.net_tile_count=1`", device ));
160 0 : else return 1;
161 0 : }
162 0 : }
163 :
164 0 : return 0;
165 0 : }
166 :
167 : static void
168 0 : init( fd_config_t const * config ) {
169 0 : int only_dedicated =
170 0 : (0==strcmp( config->net.xdp.rss_queue_mode, "dedicated" ));
171 0 : int try_dedicated = only_dedicated ||
172 0 : (0==strcmp( config->net.xdp.rss_queue_mode, "auto" ) );
173 :
174 : /* if using a bonded device, we need to set channels on the
175 : underlying devices. */
176 0 : int is_bonded = fd_bonding_is_master( config->net.interface );
177 0 : uint device_cnt = 1U;
178 0 : if( is_bonded && config->net.xdp.native_bond ) {
179 0 : device_cnt = fd_bonding_slave_cnt( config->net.interface );
180 0 : }
181 :
182 : /* If the mode was auto, we will try to init in dedicated mode but will
183 : not fail the stage if this is not successful. If the mode was
184 : dedicated, we will require success. */
185 0 : if( try_dedicated ) {
186 0 : int failed = 0;
187 0 : if( is_bonded ) {
188 0 : fd_bonding_slave_iter_t iter_[1];
189 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
190 0 : for( ; !failed && !fd_bonding_slave_iter_done( iter );
191 0 : fd_bonding_slave_iter_next( iter ) ) {
192 0 : failed = init_device( fd_bonding_slave_iter_ele( iter ), config, 1, only_dedicated, device_cnt );
193 0 : }
194 0 : } else {
195 0 : failed = init_device( config->net.interface, config, 1, only_dedicated, device_cnt );
196 0 : }
197 0 : if( !failed ) return;
198 0 : FD_TEST( !only_dedicated );
199 0 : FD_LOG_WARNING(( "error configuring network device (%s), rss_queue_mode \"auto\" attempted"
200 0 : " \"dedicated\" configuration but falling back to \"simple\".", config->net.interface ));
201 : /* Wipe partial dedicated configuration before simple init */
202 0 : if( is_bonded ) {
203 0 : fd_bonding_slave_iter_t iter_[1];
204 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
205 0 : for( ; !fd_bonding_slave_iter_done( iter );
206 0 : fd_bonding_slave_iter_next( iter ) ) {
207 0 : fini_device( fd_bonding_slave_iter_ele( iter ) );
208 0 : }
209 0 : }
210 0 : else {
211 0 : fini_device( config->net.interface );
212 0 : }
213 0 : }
214 :
215 : /* Require success for simple mode, either configured or as fallback */
216 0 : if( is_bonded ) {
217 0 : fd_bonding_slave_iter_t iter_[1];
218 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
219 0 : for( ; !fd_bonding_slave_iter_done( iter );
220 0 : fd_bonding_slave_iter_next( iter ) ) {
221 0 : init_device( fd_bonding_slave_iter_ele( iter ), config, 0, 1, device_cnt );
222 0 : }
223 0 : } else {
224 0 : init_device( config->net.interface, config, 0, 1, device_cnt );
225 0 : }
226 0 : }
227 :
228 : /* Returns whether anything is changed from the default (fini'd) state */
229 : static int
230 0 : check_device_is_modified( char const * device ) {
231 0 : fd_ethtool_ioctl_t ioc __attribute__((cleanup(fd_ethtool_ioctl_fini)));
232 0 : if( FD_UNLIKELY( &ioc!=fd_ethtool_ioctl_init( &ioc, device ) ) )
233 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
234 :
235 0 : fd_ethtool_ioctl_channels_t channels;
236 0 : FD_TEST( 0==fd_ethtool_ioctl_channels_get_num( &ioc, &channels ) );
237 0 : if( channels.current!=channels.max ) return 1;
238 :
239 0 : uint rxfh_queue_cnt;
240 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_queue_cnt( &ioc, &rxfh_queue_cnt ) );
241 :
242 0 : uint rxfh_table[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
243 0 : uint rxfh_table_ele_cnt;
244 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table, &rxfh_table_ele_cnt ) );
245 0 : for( uint j=0U, q=0U; j<rxfh_table_ele_cnt; j++) {
246 0 : if( rxfh_table[ j ]!=q++ ) return 1;
247 0 : if( q>=rxfh_queue_cnt ) q = 0;
248 0 : }
249 :
250 0 : int ntuple_rules_empty;
251 0 : FD_TEST( 0==fd_ethtool_ioctl_ntuple_validate_udp_dport( &ioc, NULL, 0, 0, &ntuple_rules_empty ) );
252 0 : if( !ntuple_rules_empty ) return 1;
253 :
254 0 : return 0;
255 0 : }
256 :
257 : static int
258 : check_device_is_configured( char const * device,
259 : fd_config_t const * config,
260 : int dedicated_mode,
261 0 : uint device_cnt ) {
262 0 : uint const net_tile_cnt = config->layout.net_tile_count;
263 0 : if( FD_UNLIKELY( net_tile_cnt%device_cnt!=0 ) ) {
264 0 : FD_LOG_ERR(( "net tile count %u must be a multiple of the number of slave devices %u (incompatible settings [layout.net_tile_count] and [net.xdp.native_bond])", net_tile_cnt, device_cnt ));
265 0 : }
266 0 : uint const queue_cnt = net_tile_cnt / device_cnt;
267 :
268 0 : fd_ethtool_ioctl_t ioc __attribute__((cleanup(fd_ethtool_ioctl_fini)));
269 0 : if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
270 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
271 :
272 0 : fd_ethtool_ioctl_channels_t channels;
273 0 : FD_TEST( 0==fd_ethtool_ioctl_channels_get_num( &ioc, &channels ) );
274 0 : if( channels.current!=(dedicated_mode ? channels.max : queue_cnt) ) return 0;
275 :
276 0 : uint rxfh_queue_cnt;
277 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_queue_cnt( &ioc, &rxfh_queue_cnt ) );
278 0 : rxfh_queue_cnt = fd_uint_min( rxfh_queue_cnt, channels.current );
279 :
280 0 : uint rxfh_table[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
281 0 : uint rxfh_table_ele_cnt;
282 0 : FD_TEST( 0==fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table, &rxfh_table_ele_cnt ) );
283 0 : int rxfh_error = (dedicated_mode && 0U==rxfh_table_ele_cnt);
284 0 : uint const start_queue = dedicated_mode ? queue_cnt : 0U;
285 0 : for( uint j=0U, q=start_queue; !rxfh_error && j<rxfh_table_ele_cnt; j++) {
286 0 : rxfh_error = (rxfh_table[ j ]!=q++);
287 0 : if( FD_UNLIKELY( q>=rxfh_queue_cnt ) ) q = start_queue;
288 0 : }
289 0 : if( rxfh_error ) return 0;
290 :
291 0 : if( dedicated_mode ) {
292 0 : int ntuple_feature_active;
293 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_test( &ioc, FD_ETHTOOL_FEATURE_NTUPLE, &ntuple_feature_active ) );
294 0 : if( !ntuple_feature_active ) return 0;
295 0 : }
296 :
297 0 : if( !dedicated_mode ) {
298 0 : int ntuple_rules_empty;
299 0 : FD_TEST( 0==fd_ethtool_ioctl_ntuple_validate_udp_dport( &ioc, NULL, 0, 0, &ntuple_rules_empty ) );
300 0 : if( !ntuple_rules_empty ) return 0;
301 0 : } else {
302 0 : int ports_valid;
303 0 : ushort ports[ 32 ];
304 0 : uint port_cnt = get_ports( config, ports );
305 0 : FD_TEST( 0==fd_ethtool_ioctl_ntuple_validate_udp_dport( &ioc, ports, port_cnt, queue_cnt, &ports_valid ));
306 0 : if( !ports_valid ) return 0;
307 0 : }
308 :
309 0 : return 1;
310 0 : }
311 :
312 : static configure_result_t
313 : check( fd_config_t const * config,
314 0 : int check_type FD_PARAM_UNUSED ) {
315 0 : int only_dedicated =
316 0 : (0==strcmp( config->net.xdp.rss_queue_mode, "dedicated" ));
317 0 : int check_dedicated = only_dedicated ||
318 0 : (0==strcmp( config->net.xdp.rss_queue_mode, "auto" ));
319 :
320 0 : int is_bonded = fd_bonding_is_master( config->net.interface );
321 0 : uint device_cnt = 1U;
322 0 : if( is_bonded && config->net.xdp.native_bond ) {
323 0 : device_cnt = fd_bonding_slave_cnt( config->net.interface );
324 0 : }
325 :
326 0 : if( check_dedicated ) {
327 0 : int is_configured = 1;
328 0 : if( is_bonded ) {
329 0 : fd_bonding_slave_iter_t iter_[1];
330 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
331 0 : for( ; is_configured && !fd_bonding_slave_iter_done( iter );
332 0 : fd_bonding_slave_iter_next( iter ) ) {
333 0 : is_configured = check_device_is_configured( fd_bonding_slave_iter_ele( iter ), config, 1, device_cnt );
334 0 : }
335 0 : } else {
336 0 : is_configured = check_device_is_configured( config->net.interface, config, 1, device_cnt );
337 0 : }
338 0 : if( is_configured ) CONFIGURE_OK();
339 0 : }
340 :
341 0 : if( !only_dedicated ) {
342 0 : int is_configured = 1;
343 0 : if( is_bonded ) {
344 0 : fd_bonding_slave_iter_t iter_[1];
345 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
346 0 : for( ; is_configured && !fd_bonding_slave_iter_done( iter );
347 0 : fd_bonding_slave_iter_next( iter ) ) {
348 0 : is_configured = check_device_is_configured( fd_bonding_slave_iter_ele( iter ), config, 0, device_cnt );
349 0 : }
350 0 : } else {
351 0 : is_configured = check_device_is_configured( config->net.interface, config, 0, device_cnt );
352 0 : }
353 0 : if( is_configured ) CONFIGURE_OK();
354 0 : }
355 :
356 0 : int is_modified = 0;
357 0 : if( is_bonded ) {
358 0 : fd_bonding_slave_iter_t iter_[1];
359 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
360 0 : for( ; !is_modified && !fd_bonding_slave_iter_done( iter );
361 0 : fd_bonding_slave_iter_next( iter ) ) {
362 0 : is_modified = check_device_is_modified( fd_bonding_slave_iter_ele( iter ) );
363 0 : }
364 0 : } else {
365 0 : is_modified = check_device_is_modified( config->net.interface );
366 0 : }
367 0 : if( is_modified )
368 0 : PARTIALLY_CONFIGURED( "device `%s` has partial ethtool-channels network configuration", config->net.interface );
369 :
370 0 : NOT_CONFIGURED( "device `%s` missing ethtool-channels network configuration", config->net.interface );
371 0 : }
372 :
373 : static int
374 0 : fini_device( char const * device ) {
375 0 : int error = 0;
376 :
377 0 : fd_ethtool_ioctl_t ioc;
378 0 : if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
379 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
380 :
381 : /* It may be the case for certain devices that the default state is
382 : the same as the init'd state (in simple mode). In this case the
383 : following fini commands will all be noops, which is fine. But we
384 : need to return 0 so that the configure stage logic does not
385 : consider this to be an error. We compare the state before and
386 : after to see if anything was changed by fini. */
387 0 : fd_ethtool_ioctl_channels_t channels_orig;
388 0 : error |= (0!=fd_ethtool_ioctl_channels_get_num( &ioc, &channels_orig ));
389 0 : uint rxfh_table_orig[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
390 0 : uint rxfh_table_orig_ele_cnt;
391 0 : error |= (0!=fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table_orig, &rxfh_table_orig_ele_cnt ));
392 0 : int ntuple_rules_empty_orig;
393 0 : error |= (0!=fd_ethtool_ioctl_ntuple_validate_udp_dport( &ioc, NULL, 0, 0, &ntuple_rules_empty_orig ));
394 0 : if( FD_UNLIKELY( error ) )
395 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to determine initial state", device ));
396 :
397 : /* We leave the ntuple feature flag as-is in fini */
398 0 : error |= (0!=fd_ethtool_ioctl_ntuple_clear( &ioc ));
399 :
400 : /* This should happen first, otherwise changing the number of channels may fail */
401 0 : error |= (0!=fd_ethtool_ioctl_rxfh_set_default( &ioc ));
402 :
403 0 : error |= (0!=fd_ethtool_ioctl_channels_set_num( &ioc, 0 /* max */ ));
404 :
405 : /* Some drivers (i40e) do not always evenly redistribute the RXFH table
406 : when increasing the channel count, so we run this again just in case. */
407 0 : error |= (0!=fd_ethtool_ioctl_rxfh_set_default( &ioc ));
408 :
409 0 : if( FD_UNLIKELY( error ) )
410 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to set to default state", device ));
411 :
412 0 : fd_ethtool_ioctl_channels_t channels_new;
413 0 : error |= (0!=fd_ethtool_ioctl_channels_get_num( &ioc, &channels_new ));
414 0 : uint rxfh_table_new[ FD_ETHTOOL_MAX_RXFH_TABLE_CNT ] = { 0 };
415 0 : uint rxfh_table_new_ele_cnt;
416 0 : error |= (0!=fd_ethtool_ioctl_rxfh_get_table( &ioc, rxfh_table_new, &rxfh_table_new_ele_cnt ));
417 0 : int ntuple_rules_empty_new;
418 0 : error |= (0!=fd_ethtool_ioctl_ntuple_validate_udp_dport( &ioc, NULL, 0, 0, &ntuple_rules_empty_new ));
419 0 : if( FD_UNLIKELY( error ) )
420 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to determine final state", device ));
421 :
422 0 : fd_ethtool_ioctl_fini( &ioc );
423 :
424 0 : int modified = (0!=memcmp( &channels_orig, &channels_new, sizeof(fd_ethtool_ioctl_channels_t) )) ||
425 0 : (rxfh_table_orig_ele_cnt != rxfh_table_new_ele_cnt) ||
426 0 : (0!=memcmp( rxfh_table_orig, rxfh_table_new, rxfh_table_orig_ele_cnt * sizeof(uint) )) ||
427 0 : (ntuple_rules_empty_orig!=ntuple_rules_empty_new);
428 0 : return modified;
429 0 : }
430 :
431 : static int
432 : fini( fd_config_t const * config,
433 0 : int pre_init FD_PARAM_UNUSED ) {
434 0 : int done = 0;
435 0 : if( FD_UNLIKELY( fd_bonding_is_master( config->net.interface ) ) ) {
436 0 : fd_bonding_slave_iter_t iter_[1];
437 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, config->net.interface );
438 0 : for( ; !fd_bonding_slave_iter_done( iter );
439 0 : fd_bonding_slave_iter_next( iter ) ) {
440 0 : done |= fini_device( fd_bonding_slave_iter_ele( iter ) );
441 0 : }
442 0 : } else {
443 0 : done = fini_device( config->net.interface );
444 0 : }
445 0 : return done;
446 0 : }
447 :
448 : configure_stage_t fd_cfg_stage_ethtool_channels = {
449 : .name = NAME,
450 : .always_recreate = 0,
451 : .enabled = enabled,
452 : .init_perm = init_perm,
453 : .fini_perm = fini_perm,
454 : .init = init,
455 : .fini = fini,
456 : .check = check,
457 : };
458 :
459 : #undef NAME
|