Line data Source code
1 : /* This stage configures the OS to support effective preferred busy
2 : polling, allowing for significantly improved network stack (XDP)
3 : reliability under high load if enabled on a well supported driver. */
4 :
5 : #include "configure.h"
6 :
7 0 : #define NAME "sysfs-poll"
8 :
9 : #include "../../../platform/fd_file_util.h"
10 : #include "../../../../disco/net/fd_net_tile.h"
11 : #include "../../../../disco/net/fd_linux_bond.h"
12 :
13 : #include <string.h> /* strcmp */
14 : #include <unistd.h> /* access */
15 : #include <errno.h>
16 : #include <net/if.h> /* IF_NAMESIZE */
17 : #include <linux/capability.h>
18 :
19 : /* Values for NAPI_DEFER_HARD_IRQS and GRO_FLUSH_TIMEOUT
20 : chosen based on napibusy configuration tested and shown
21 : in https://lwn.net/Articles/997491/ Linux patch cover letter.
22 : Chosen over fullbusy since for Firedancer the values of
23 : fullbusy are unnecessarily high and could cause some extra
24 : latency to regular non-Firedancer traffic. */
25 0 : #define NAPI_DEFER_HARD_IRQS 100U
26 0 : #define GRO_FLUSH_TIMEOUT 200000U
27 :
28 : static char const setting_napi_defer_hard_irqs[] = "napi_defer_hard_irqs";
29 : static char const setting_gro_flush_timeout[] = "gro_flush_timeout";
30 :
31 : static int
32 0 : enabled( config_t const * config ) {
33 0 : return !strcmp( config->net.provider, "xdp" )
34 0 : && !strcmp( config->net.xdp.poll_mode, "prefbusy" );
35 0 : }
36 :
37 : /* get_interfaces returns the number of underlying interfaces in use,
38 : and fills the 'interfaces' parameter with the underlying interfaces'
39 : names. Abstracts handling bonds vs standard interfaces. */
40 : static ulong
41 : get_interfaces( char const * device,
42 : int native_bond,
43 0 : char interfaces[ FD_NET_BOND_SLAVE_MAX ][ IF_NAMESIZE ] ) {
44 0 : ulong num_interfaces = 0UL;
45 :
46 0 : if( native_bond && fd_bonding_is_master( device ) ) {
47 0 : fd_bonding_slave_iter_t iter_[1];
48 0 : fd_bonding_slave_iter_t * iter = fd_bonding_slave_iter_init( iter_, device );
49 :
50 0 : for( ; !fd_bonding_slave_iter_done( iter ); fd_bonding_slave_iter_next( iter ) ) {
51 0 : if( FD_UNLIKELY( num_interfaces >= FD_NET_BOND_SLAVE_MAX ) ) {
52 0 : FD_LOG_ERR(( "bond `%s` has more than %u slaves, this is more than allowed in Firedancer.", device, FD_NET_BOND_SLAVE_MAX ));
53 0 : }
54 0 : fd_cstr_ncpy( interfaces[ num_interfaces++ ], fd_bonding_slave_iter_ele( iter ), IF_NAMESIZE );
55 0 : }
56 :
57 0 : if( FD_UNLIKELY( !num_interfaces ) ) {
58 0 : FD_LOG_ERR(( "bond `%s` has no slave interfaces, must have at least one.", device ));
59 0 : }
60 :
61 0 : } else {
62 0 : fd_cstr_ncpy( interfaces[ num_interfaces++ ], device, IF_NAMESIZE );
63 0 : }
64 :
65 0 : return num_interfaces;
66 0 : }
67 :
68 : static void
69 : init_perm( fd_cap_chk_t * chk,
70 0 : config_t const * config FD_PARAM_UNUSED ) {
71 0 : fd_cap_chk_cap( chk, NAME, CAP_NET_ADMIN, "configure preferred busy polling via `/sys/class/net/*/{napi_defer_hard_irqs, gro_flush_timeout}`" );
72 0 : }
73 :
74 : static void
75 : sysfs_net_set( char const * device,
76 : char const * setting,
77 : ulong value,
78 0 : int native_bond ) {
79 0 : char interfaces[ FD_NET_BOND_SLAVE_MAX ][ IF_NAMESIZE ];
80 0 : ulong num_interfaces = get_interfaces( device, native_bond, interfaces );
81 :
82 0 : for( ulong i=0UL; i<num_interfaces; i++ ) {
83 0 : char path[ PATH_MAX ];
84 0 : fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/%s", interfaces[ i ], setting );
85 :
86 0 : FD_LOG_NOTICE(( "%sRUN: `echo \"%lu\" > %s`%s", fd_log_style_dim(), value, path , fd_log_style_normal() ));
87 0 : if( FD_UNLIKELY( -1==fd_file_util_write_uint( path, (uint)value ) ) ) {
88 0 : FD_LOG_ERR(( "could not write to `%s` (%i-%s), 'prefbusy' poll mode may not be supported for this network configuration, consider switching back 'poll_mode' within your config to 'softirq' mode.", path, errno, fd_io_strerror( errno ) ));
89 0 : }
90 0 : }
91 0 : }
92 :
93 : static void
94 0 : init( config_t const * config ) {
95 0 : sysfs_net_set( config->net.interface, setting_napi_defer_hard_irqs, NAPI_DEFER_HARD_IRQS, config->net.xdp.native_bond );
96 :
97 0 : sysfs_net_set( config->net.interface, setting_gro_flush_timeout, GRO_FLUSH_TIMEOUT, config->net.xdp.native_bond );
98 0 : }
99 :
100 : static int
101 : fini( config_t const * config,
102 0 : int pre_init FD_PARAM_UNUSED ) {
103 0 : sysfs_net_set( config->net.interface, setting_napi_defer_hard_irqs, 0U, config->net.xdp.native_bond );
104 0 : sysfs_net_set( config->net.interface, setting_gro_flush_timeout, 0U, config->net.xdp.native_bond );
105 0 : return 1;
106 0 : }
107 :
108 : static configure_result_t
109 : check( config_t const * config,
110 0 : int check_type FD_PARAM_UNUSED ) {
111 0 : char interfaces[ FD_NET_BOND_SLAVE_MAX ][ IF_NAMESIZE ];
112 0 : ulong num_interfaces = get_interfaces( config->net.interface, config->net.xdp.native_bond, interfaces );
113 :
114 0 : for( ulong i=0UL; i<num_interfaces; i++ ) {
115 0 : char path[ PATH_MAX ];
116 0 : uint value;
117 0 : fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/%s", interfaces[ i ], setting_napi_defer_hard_irqs );
118 :
119 : /* Check NAPI_DEFER_HARD_IRQS value set correctly. */
120 :
121 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( path, &value ) ) ) {
122 0 : FD_LOG_ERR(( "could not read `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
123 0 : }
124 :
125 0 : if( FD_UNLIKELY( NAPI_DEFER_HARD_IRQS != value ) ) {
126 0 : NOT_CONFIGURED( "kernel parameter `%s` is set to %u, not the expected value of %u.", path, value, NAPI_DEFER_HARD_IRQS );
127 0 : }
128 :
129 : /* Check GRO_FLUSH_TIMEOUT value set correctly. */
130 :
131 0 : fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/%s", interfaces[ i ], setting_gro_flush_timeout );
132 :
133 0 : if( FD_UNLIKELY( -1==fd_file_util_read_uint( path, &value ) ) ) {
134 0 : FD_LOG_ERR(( "could not read `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) ));
135 0 : }
136 :
137 0 : if( FD_UNLIKELY( GRO_FLUSH_TIMEOUT != value ) ) {
138 0 : NOT_CONFIGURED( "kernel parameter `%s` is set to %u, not the expected value of %u.", path, value, GRO_FLUSH_TIMEOUT );
139 0 : }
140 0 : }
141 :
142 0 : CONFIGURE_OK();
143 0 : }
144 :
145 : configure_stage_t fd_cfg_stage_sysfs_poll = {
146 : .name = NAME,
147 : .enabled = enabled,
148 : .init_perm = init_perm,
149 : .fini_perm = init_perm,
150 : .init = init,
151 : .fini = fini,
152 : .check = check,
153 : };
|