Skip to content

Commit f30a65f

Browse files
send_test: mini-topo to exercise send tile
1 parent 000afb7 commit f30a65f

File tree

9 files changed

+556
-34
lines changed

9 files changed

+556
-34
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ifdef FD_HAS_INT128
2+
$(call add-objs,send_test,fd_firedancer_dev)
3+
endif
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
send_test is a firedancer-dev command that tests the send tile.
3+
It uses the net, send, metrics, and sign tiles, just like in prod.
4+
The main test function writes contact info to the gossip_send link,
5+
stake info to the stake_out link, and triggers mock votes on the
6+
tower_send link.
7+
8+
It takes two required arguments:
9+
--gossip-file: the path to the gossip file
10+
--stake-file: the path to the stake file
11+
These two files should include lines from the 'solana gossip' and
12+
'solana validators' commands, respectively. It is recommended to run
13+
with a known good subset of nodes while tuning the send tile.
14+
*/
15+
#include "../../../shared/commands/configure/configure.h"
16+
#include "../../../shared/commands/run/run.h" /* initialize_workspaces */
17+
#include "../../../shared/fd_config.h" /* config_t */
18+
#include "../../../../disco/topo/fd_topob.h"
19+
#include "../../../../disco/topo/fd_cpu_topo.h" /* fd_topo_cpus_t */
20+
#include "../../../../util/tile/fd_tile_private.h"
21+
#include "../../../../disco/net/fd_net_tile.h" /* fd_topos_net_tiles */
22+
#include "../../../../flamenco/leaders/fd_leaders_base.h" /* FD_STAKE_OUT_MTU */
23+
#include "../../../../disco/pack/fd_microblock.h" /* fd_txn_p_t */
24+
#include "../../../../app/firedancer/topology.h" /* fd_topo_configure_tile */
25+
#include "../../../../disco/keyguard/fd_keyload.h"
26+
27+
#include "send_test_helpers.c"
28+
29+
extern fd_topo_obj_callbacks_t * CALLBACKS[];
30+
31+
fd_topo_run_tile_t
32+
fdctl_tile_run( fd_topo_tile_t const * tile );
33+
34+
static void
35+
send_test_topo( config_t * config ) {
36+
37+
ulong const net_tile_cnt = config->layout.net_tile_count;
38+
ulong const ingress_buf_sz = config->net.ingress_buffer_size;
39+
40+
/* Setup topology */
41+
fd_topo_t * topo = fd_topob_new( &config->topo, config->name );
42+
topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
43+
44+
/* tile wksps */
45+
fd_topob_wksp( topo, "metric_in" );
46+
fd_topob_wksp( topo, "metric" );
47+
fd_topob_wksp( topo, "sign" );
48+
fd_topob_wksp( topo, "send" );
49+
50+
/* wksps for real links */
51+
fd_topob_wksp( topo, "send_net" );
52+
fd_topob_wksp( topo, "sign_send" );
53+
fd_topob_wksp( topo, "send_sign" );
54+
55+
/* wksps for mock links */
56+
fd_topob_wksp( topo, "gossip_send" );
57+
fd_topob_wksp( topo, "stake_out" );
58+
fd_topob_wksp( topo, "tower_send" );
59+
fd_topob_wksp( topo, "send_txns" );
60+
61+
ulong tile_to_cpu[ FD_TILE_MAX ] = {0};
62+
ushort parsed_tile_to_cpu[ FD_TILE_MAX ];
63+
for( ulong i=0UL; i<FD_TILE_MAX; i++ ) parsed_tile_to_cpu[ i ] = USHORT_MAX;
64+
65+
fd_topo_cpus_t cpus[1];
66+
fd_topo_cpus_init( cpus );
67+
68+
ulong affinity_tile_cnt = 0UL;
69+
if( FD_LIKELY( strcmp( config->layout.affinity, "auto" ) ) ) affinity_tile_cnt = fd_tile_private_cpus_parse( config->layout.affinity, parsed_tile_to_cpu );
70+
71+
for( ulong i=0UL; i<affinity_tile_cnt; i++ ) {
72+
if( FD_UNLIKELY( parsed_tile_to_cpu[ i ]!=USHORT_MAX && parsed_tile_to_cpu[ i ]>=cpus->cpu_cnt ) )
73+
FD_LOG_ERR(( "The CPU affinity string in the configuration file under [layout.affinity] specifies a CPU index of %hu, but the system "
74+
"only has %lu CPUs. You should either change the CPU allocations in the affinity string, or increase the number of CPUs "
75+
"in the system.",
76+
parsed_tile_to_cpu[ i ], cpus->cpu_cnt ));
77+
tile_to_cpu[ i ] = fd_ulong_if( parsed_tile_to_cpu[ i ]==USHORT_MAX, ULONG_MAX, (ulong)parsed_tile_to_cpu[ i ] );
78+
}
79+
80+
#define FOR(cnt) for( ulong i=0UL; i<cnt; i++ )
81+
82+
/* tiles */
83+
fd_topos_net_tiles( topo, net_tile_cnt, &config->net, config->tiles.netlink.max_routes, config->tiles.netlink.max_peer_routes, config->tiles.netlink.max_neighbors, tile_to_cpu );
84+
fd_topob_tile( topo, "metric", "metric", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 );
85+
fd_topob_tile( topo, "send", "send", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 );
86+
fd_topob_tile( topo, "sign", "sign", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 1 );
87+
88+
/* real links */
89+
FOR(net_tile_cnt) fd_topos_net_rx_link( topo, "net_send", i, ingress_buf_sz );
90+
91+
FOR(net_tile_cnt) fd_topob_link( topo, "send_net", "send_net", ingress_buf_sz, FD_NET_MTU, 1UL );
92+
/**/ fd_topob_link( topo, "send_sign", "send_sign", 128UL, FD_TXN_MTU, 1UL );
93+
/**/ fd_topob_link( topo, "sign_send", "sign_send", 128UL, 64UL, 1UL );
94+
95+
/* mock links */
96+
fd_topob_link( topo, "gossip_send", "gossip_send", 128UL, 40200UL * 38UL, 1UL )
97+
->permit_no_producers = 1;
98+
fd_topob_link( topo, "stake_out", "stake_out", 128UL, FD_STAKE_OUT_MTU, 1UL )
99+
->permit_no_producers = 1;
100+
fd_topob_link( topo, "tower_send", "tower_send", 65536UL, sizeof(fd_txn_p_t), 1UL )
101+
->permit_no_producers = 1;
102+
fd_topob_link( topo, "send_txns", "send_txns", 128UL, 40200UL * 38UL, 1UL )
103+
->permit_no_consumers = 1;
104+
105+
/* attach mock links */
106+
fd_topob_tile_in( topo, "send", 0UL, "metric_in", "gossip_send", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
107+
fd_topob_tile_in( topo, "send", 0UL, "metric_in", "stake_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
108+
fd_topob_tile_in( topo, "send", 0UL, "metric_in", "tower_send", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
109+
110+
/* attach real links */
111+
fd_topos_tile_in_net( topo, "metric_in", "send_net", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
112+
fd_topob_tile_in ( topo, "send", 0UL, "metric_in", "net_send", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
113+
114+
fd_topob_tile_out( topo, "send", 0UL, "send_net", 0UL );
115+
116+
/* unpolled links have to be last! */
117+
fd_topob_tile_in ( topo, "sign", 0UL, "metric_in", "send_sign", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
118+
fd_topob_tile_in ( topo, "send", 0UL, "metric_in", "sign_send", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_UNPOLLED );
119+
fd_topob_tile_out( topo, "send", 0UL, "send_sign", 0UL );
120+
fd_topob_tile_out( topo, "sign", 0UL, "sign_send", 0UL );
121+
fd_topob_tile_out( topo, "send", 0UL, "send_txns", 0UL );
122+
123+
FOR(net_tile_cnt) fd_topos_net_tile_finish( topo, i );
124+
125+
for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
126+
fd_topo_tile_t * tile = &topo->tiles[ i ];
127+
if( !fd_topo_configure_tile( tile, config ) ) {
128+
FD_LOG_ERR(( "unknown tile name %lu `%s`", i, tile->name ));
129+
}
130+
}
131+
132+
/* Finish topology setup */
133+
if( FD_UNLIKELY( !strcmp( config->layout.affinity, "auto" ) ) ) fd_topob_auto_layout( topo, 0 );
134+
fd_topob_finish( topo, CALLBACKS );
135+
}
136+
137+
struct {
138+
char gossip_file[256];
139+
char stake_file[256];
140+
} send_test_args = {0};
141+
142+
static void
143+
send_test_cmd_args( int * pargc,
144+
char *** pargv,
145+
args_t * args FD_PARAM_UNUSED ) {
146+
char ** _pargv = *pargv;
147+
int _pargc = *pargc;
148+
int found_gossip = 0;
149+
int found_stake = 0;
150+
151+
/* Extract our arguments */
152+
for( int i = 0; i < _pargc - 1; i++ ) {
153+
if( !strcmp( _pargv[i], "--gossip-file" ) ) {
154+
strncpy( send_test_args.gossip_file, _pargv[i+1], sizeof(send_test_args.gossip_file) - 1 );
155+
found_gossip = 1;
156+
} else if( !strcmp( _pargv[i], "--stake-file" ) ) {
157+
strncpy( send_test_args.stake_file, _pargv[i+1], sizeof(send_test_args.stake_file) - 1 );
158+
found_stake = 1;
159+
}
160+
}
161+
162+
/* Remove our arguments from argv */
163+
int write_idx = 0;
164+
for( int read_idx = 0; read_idx < _pargc; read_idx++ ) {
165+
if( read_idx < _pargc - 1 &&
166+
(!strcmp( _pargv[read_idx], "--gossip-file" ) || !strcmp( _pargv[read_idx], "--stake-file" )) ) {
167+
read_idx++; /* Skip the argument value too */
168+
} else {
169+
_pargv[write_idx++] = _pargv[read_idx];
170+
}
171+
}
172+
*pargc = write_idx;
173+
174+
if( !found_gossip ) FD_LOG_ERR(( "--gossip-file is required" ));
175+
if( !found_stake ) FD_LOG_ERR(( "--stake-file is required" ));
176+
}
177+
178+
179+
static void
180+
init( send_test_ctx_t * ctx, config_t * config ) {
181+
fd_topo_t * topo = &config->topo;
182+
ctx->topo = topo;
183+
ctx->config = config;
184+
185+
/* Copy file paths from send_test_args */
186+
fd_memcpy( ctx->gossip_file, send_test_args.gossip_file, sizeof(ctx->gossip_file) );
187+
fd_memcpy( ctx->stake_file, send_test_args.stake_file, sizeof(ctx->stake_file ) );
188+
189+
ctx->identity_key [ 0 ] = *(fd_pubkey_t const *)(fd_keyload_load( config->paths.identity_key, /* pubkey only: */ 1 ) );
190+
ctx->vote_acct_addr[ 0 ] = *(fd_pubkey_t const *)(fd_keyload_load( config->paths.vote_account, /* pubkey only: */ 1 ) );
191+
192+
ctx->out_links[ MOCK_CI_IDX ] = setup_test_out_link( topo, "gossip_send" );
193+
ctx->out_links[ MOCK_STAKE_IDX ] = setup_test_out_link( topo, "stake_out" );
194+
ctx->out_links[ MOCK_TRIGGER_IDX ] = setup_test_out_link( topo, "tower_send" );
195+
196+
ctx->out_fns [ MOCK_CI_IDX ] = send_test_ci;
197+
ctx->out_fns [ MOCK_STAKE_IDX ] = send_test_stake;
198+
ctx->out_fns [ MOCK_TRIGGER_IDX ] = send_test_trigger;
199+
200+
ctx->last_evt [ MOCK_CI_IDX ] = 0;
201+
ctx->last_evt [ MOCK_STAKE_IDX ] = 0;
202+
ctx->last_evt [ MOCK_TRIGGER_IDX ] = 0;
203+
204+
ctx->delay [ MOCK_CI_IDX ] = 5e9L;
205+
ctx->delay [ MOCK_STAKE_IDX ] = 172800e9L;
206+
ctx->delay [ MOCK_TRIGGER_IDX ] = 400e6L;
207+
208+
encode_vote( ctx, ctx->txn_buf );
209+
210+
/* send first epoch of stake info */
211+
send_test_stake( ctx, &ctx->out_links[ MOCK_STAKE_IDX ] );
212+
}
213+
static void
214+
send_test_main_loop( send_test_ctx_t * ctx ) {
215+
for(;;) {
216+
long now = fd_tickcount();
217+
for( ulong i=0UL; i<MOCK_CNT; i++ ) {
218+
if( ctx->last_evt[ i ] + ctx->delay[ i ] <= now ) {
219+
send_test_out_t * out = &ctx->out_links[ i ];
220+
ctx->out_fns [ i ]( ctx, out );
221+
ctx->last_evt[ i ] = now;
222+
}
223+
}
224+
}
225+
}
226+
227+
static void
228+
send_test_cmd_fn( args_t * args ,
229+
config_t * config ) {
230+
send_test_topo( config );
231+
232+
configure_stage( &fd_cfg_stage_sysctl, CONFIGURE_CMD_INIT, config );
233+
configure_stage( &fd_cfg_stage_hugetlbfs, CONFIGURE_CMD_INIT, config );
234+
configure_stage( &fd_cfg_stage_ethtool_channels, CONFIGURE_CMD_INIT, config );
235+
configure_stage( &fd_cfg_stage_ethtool_gro, CONFIGURE_CMD_INIT, config );
236+
configure_stage( &fd_cfg_stage_ethtool_loopback, CONFIGURE_CMD_INIT, config );
237+
238+
fd_topo_print_log( 0, &config->topo );
239+
240+
run_firedancer_init( config, !args->dev.no_init_workspaces );
241+
fdctl_setup_netns( config, 1 );
242+
243+
if( 0==strcmp( config->net.provider, "xdp" ) ) fd_topo_install_xdp( &config->topo, config->net.bind_address_parsed );
244+
245+
fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE );
246+
fd_topo_run_single_process( &config->topo, 2, config->uid, config->gid, fdctl_tile_run );
247+
248+
send_test_ctx_t ctx = {0};
249+
init( &ctx, config );
250+
send_test_main_loop( &ctx );
251+
}
252+
253+
static void
254+
configure_stage_perm( configure_stage_t const * stage,
255+
fd_cap_chk_t * chk,
256+
config_t const * config ) {
257+
int enabled = !stage->enabled || stage->enabled( config );
258+
if( enabled && stage->check( config ).result != CONFIGURE_OK )
259+
if( stage->init_perm ) stage->init_perm( chk, config );
260+
}
261+
262+
static void
263+
send_test_cmd_perm( args_t * args FD_PARAM_UNUSED,
264+
fd_cap_chk_t * chk,
265+
config_t const * config ) {
266+
configure_stage_perm( &fd_cfg_stage_sysctl, chk, config );
267+
configure_stage_perm( &fd_cfg_stage_hugetlbfs, chk, config );
268+
configure_stage_perm( &fd_cfg_stage_ethtool_channels, chk, config );
269+
configure_stage_perm( &fd_cfg_stage_ethtool_gro, chk, config );
270+
configure_stage_perm( &fd_cfg_stage_ethtool_loopback, chk, config );
271+
}
272+
273+
action_t fd_action_send_test = {
274+
.name = "send_test",
275+
.args = send_test_cmd_args,
276+
.fn = send_test_cmd_fn,
277+
.perm = send_test_cmd_perm,
278+
};

0 commit comments

Comments
 (0)