Line data Source code
1 : #include "../../../shared/commands/configure/configure.h" /* CONFIGURE_CMD_INIT */
2 : #include "../../../shared/commands/run/run.h" /* fdctl_check_configure */
3 : #include "../../../../disco/net/fd_net_tile.h"
4 : #include "../../../../disco/metrics/fd_metrics.h"
5 : #include "../../../../disco/topo/fd_topob.h"
6 : #include "../../../../disco/topo/fd_cpu_topo.h"
7 : #include "../../../../util/net/fd_ip4.h"
8 : #include "../../../../util/tile/fd_tile_private.h" /* fd_tile_private_cpus_parse */
9 :
10 : #include <stdio.h> /* printf */
11 : #include <unistd.h> /* isatty */
12 : #include <sys/ioctl.h>
13 : #include <poll.h>
14 :
15 : ulong
16 : fdctl_obj_align( fd_topo_t const * topo,
17 : fd_topo_obj_t const * obj );
18 :
19 : ulong
20 : fdctl_obj_footprint( fd_topo_t const * topo,
21 : fd_topo_obj_t const * obj );
22 :
23 : ulong
24 : fdctl_obj_loose( fd_topo_t const * topo,
25 : fd_topo_obj_t const * obj );
26 :
27 : fd_topo_run_tile_t
28 : fdctl_tile_run( fd_topo_tile_t const * tile );
29 :
30 : static void
31 0 : pktgen_topo( config_t * config ) {
32 0 : char const * affinity = config->development.pktgen.affinity;
33 0 : int is_auto_affinity = !strcmp( affinity, "auto" );
34 :
35 0 : ushort parsed_tile_to_cpu[ FD_TILE_MAX ];
36 0 : for( ulong i=0UL; i<FD_TILE_MAX; i++ ) parsed_tile_to_cpu[ i ] = USHORT_MAX;
37 :
38 0 : fd_topo_cpus_t cpus[1];
39 0 : fd_topo_cpus_init( cpus );
40 :
41 0 : ulong affinity_tile_cnt = 0UL;
42 0 : if( FD_LIKELY( !is_auto_affinity ) ) affinity_tile_cnt = fd_tile_private_cpus_parse( affinity, parsed_tile_to_cpu );
43 :
44 0 : ulong tile_to_cpu[ FD_TILE_MAX ] = {0};
45 0 : for( ulong i=0UL; i<affinity_tile_cnt; i++ ) {
46 0 : if( FD_UNLIKELY( parsed_tile_to_cpu[ i ]!=USHORT_MAX && parsed_tile_to_cpu[ i ]>=cpus->cpu_cnt ) )
47 0 : FD_LOG_ERR(( "The CPU affinity string in the configuration file under [development.pktgen.affinity] specifies a CPU index of %hu, but the system "
48 0 : "only has %lu CPUs. You should either change the CPU allocations in the affinity string, or increase the number of CPUs "
49 0 : "in the system.",
50 0 : parsed_tile_to_cpu[ i ], cpus->cpu_cnt ));
51 0 : tile_to_cpu[ i ] = fd_ulong_if( parsed_tile_to_cpu[ i ]==USHORT_MAX, ULONG_MAX, (ulong)parsed_tile_to_cpu[ i ] );
52 0 : }
53 0 : if( FD_LIKELY( !is_auto_affinity ) ) {
54 0 : if( FD_UNLIKELY( affinity_tile_cnt!=4UL ) )
55 0 : FD_LOG_ERR(( "Invalid [development.pktgen.affinity]: must include exactly three CPUs" ));
56 0 : }
57 :
58 : /* Reset topology from scratch */
59 0 : fd_topo_t * topo = &config->topo;
60 0 : fd_topob_new( &config->topo, config->name );
61 0 : topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
62 :
63 0 : fd_topob_wksp( topo, "metric" );
64 0 : fd_topob_wksp( topo, "metric_in" );
65 0 : fd_topos_net_tiles( topo, config->layout.net_tile_count, config->tiles.netlink.max_routes, config->tiles.netlink.max_neighbors, config->development.net.provider, config->tiles.net.interface, config->tiles.net.flush_timeout_micros,
66 0 : config->tiles.net.xdp_rx_queue_size, config->tiles.net.xdp_tx_queue_size, config->tiles.net.xdp_zero_copy, config->tiles.net.xdp_mode, tile_to_cpu );
67 0 : fd_topob_tile( topo, "metric", "metric", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0 );
68 :
69 0 : fd_topob_wksp( topo, "pktgen" );
70 0 : fd_topo_tile_t * pktgen_tile = fd_topob_tile( topo, "pktgen", "pktgen", "pktgen", tile_to_cpu[ topo->tile_cnt ], 0, 0 );
71 0 : if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.pktgen.fake_dst_ip, &pktgen_tile->pktgen.fake_dst_ip ) ) ) {
72 0 : FD_LOG_ERR(( "Invalid [development.pktgen.fake_dst_ip]" ));
73 0 : }
74 0 : fd_topob_link( topo, "pktgen_out", "pktgen", 2048UL, FD_NET_MTU, 1UL );
75 0 : fd_topob_tile_out( topo, "pktgen", 0UL, "pktgen_out", 0UL );
76 0 : fd_topob_tile_in( topo, "net", 0UL, "metric_in", "pktgen_out", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
77 :
78 : /* Create dummy RX link */
79 0 : fd_topos_net_rx_link( topo, "net_quic", 0UL, config->tiles.net.send_buffer_size );
80 0 : fd_topob_tile_out( topo, "net", 0UL, "net_quic", 0UL );
81 0 : fd_topob_tile_in( topo, "pktgen", 0UL, "metric_in", "net_quic", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
82 :
83 0 : fd_topos_net_tile_finish( topo, 0UL );
84 0 : if( FD_UNLIKELY( is_auto_affinity ) ) fd_topob_auto_layout( topo );
85 0 : topo->agave_affinity_cnt = 0;
86 0 : fd_topob_finish( topo, fdctl_obj_align, fdctl_obj_footprint, fdctl_obj_loose );
87 0 : fd_topo_print_log( /* stdout */ 1, topo );
88 0 : }
89 :
90 : void
91 : pktgen_cmd_args( int * pargc,
92 : char *** pargv,
93 0 : args_t * args ) {
94 : /* FIXME add config options here */
95 0 : (void)pargc; (void)pargv; (void)args;
96 0 : }
97 :
98 : /* Hacky: Since the pktgen runs in the same process, use globals to
99 : share state */
100 : extern uint fd_pktgen_active;
101 :
102 : /* render_status prints statistics at the top of the screen.
103 : Should be called at a low rate (~500ms). */
104 :
105 : static void
106 0 : render_status( ulong volatile const * net_metrics ) {
107 0 : fputs( "\0337" /* save cursor position */
108 0 : "\033[H" /* move cursor to (0,0) */
109 0 : "\033[2K\n", /* create an empty line to avoid spamming look back buffer */
110 0 : stdout );
111 0 : printf( "\033[2K" "[Firedancer pktgen] mode=%s\n",
112 0 : FD_VOLATILE_CONST( fd_pktgen_active ) ? "send+recv" : "recv" );
113 :
114 : /* Render packet per second rates */
115 0 : static long ts_last = -1L;
116 0 : static ulong cum_idle_last = 0UL;
117 0 : static ulong cum_tick_last = 0UL;
118 0 : static ulong rx_ok_last = 0UL;
119 0 : static ulong rx_byte_last = 0UL;
120 0 : static ulong rx_drop_last = 0UL;
121 0 : static ulong tx_ok_last = 0UL;
122 0 : static ulong tx_byte_last = 0UL;
123 :
124 0 : static double busy_r = 0.0;
125 0 : static double rx_ok_pps = 0.0;
126 0 : static double rx_bps = 0.0;
127 0 : static double rx_drop_pps = 0.0;
128 0 : static double tx_ok_pps = 0.0;
129 0 : static double tx_bps = 0.0;
130 :
131 0 : if( FD_UNLIKELY( ts_last==-1 ) ) ts_last = fd_log_wallclock();
132 0 : long now = fd_log_wallclock();
133 0 : long dt = now-ts_last;
134 0 : if( dt>(long)10e6 ) {
135 0 : ulong cum_idle_now = net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ];
136 0 : ulong cum_tick_now = cum_idle_now;
137 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_HOUSEKEEPING ) ];
138 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_HOUSEKEEPING ) ];
139 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_HOUSEKEEPING ) ];
140 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_PREFRAG ) ];
141 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_PREFRAG ) ];
142 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
143 0 : /* */ cum_tick_now += net_metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_POSTFRAG ) ];
144 0 : ulong rx_ok_now = net_metrics[ MIDX( COUNTER, NET, RX_PKT_CNT ) ];
145 0 : ulong rx_byte_now = net_metrics[ MIDX( COUNTER, NET, RX_BYTES_TOTAL ) ];
146 0 : ulong rx_drop_now = net_metrics[ MIDX( COUNTER, NET, RX_FILL_BLOCKED_CNT ) ];
147 0 : /* */ rx_drop_now += net_metrics[ MIDX( COUNTER, NET, RX_BACKPRESSURE_CNT ) ];
148 0 : /* */ rx_drop_now += net_metrics[ MIDX( COUNTER, NET, XDP_RX_DROPPED_OTHER ) ];
149 0 : /* */ rx_drop_now += net_metrics[ MIDX( COUNTER, NET, XDP_RX_INVALID_DESCS ) ];
150 0 : /* */ rx_drop_now += net_metrics[ MIDX( COUNTER, NET, XDP_RX_RING_FULL ) ];
151 0 : ulong tx_ok_now = net_metrics[ MIDX( COUNTER, NET, TX_COMPLETE_CNT ) ];
152 0 : ulong tx_byte_now = net_metrics[ MIDX( COUNTER, NET, TX_BYTES_TOTAL ) ];
153 :
154 0 : ulong cum_idle_delta = cum_idle_now-cum_idle_last;
155 0 : ulong cum_tick_delta = cum_tick_now-cum_tick_last;
156 0 : ulong rx_ok_delta = rx_ok_now -rx_ok_last;
157 0 : ulong rx_byte_delta = rx_byte_now -rx_byte_last;
158 0 : ulong rx_drop_delta = rx_drop_now -rx_drop_last;
159 0 : ulong tx_ok_delta = tx_ok_now -tx_ok_last;
160 0 : ulong tx_byte_delta = tx_byte_now -tx_byte_last;
161 :
162 0 : busy_r = 1.0 - ( (double)cum_idle_delta / (double)cum_tick_delta );
163 0 : rx_ok_pps = 1e9*( (double)rx_ok_delta /(double)dt );
164 0 : rx_bps = 8e9*( (double)rx_byte_delta/(double)dt );
165 0 : rx_drop_pps = 1e9*( (double)rx_drop_delta/(double)dt );
166 0 : tx_ok_pps = 1e9*( (double)tx_ok_delta /(double)dt );
167 0 : tx_bps = 8e9*( (double)tx_byte_delta/(double)dt );
168 :
169 0 : ts_last = now;
170 0 : cum_idle_last = cum_idle_now;
171 0 : cum_tick_last = cum_tick_now;
172 0 : rx_ok_last = rx_ok_now;
173 0 : rx_byte_last = rx_byte_now;
174 0 : rx_drop_last = rx_drop_now;
175 0 : tx_ok_last = tx_ok_now;
176 0 : tx_byte_last = tx_byte_now;
177 0 : }
178 :
179 0 : ulong rx_idle = net_metrics[ MIDX( GAUGE, NET, RX_IDLE_CNT ) ];
180 0 : ulong rx_busy = net_metrics[ MIDX( GAUGE, NET, RX_BUSY_CNT ) ];
181 0 : ulong tx_idle = net_metrics[ MIDX( GAUGE, NET, TX_IDLE_CNT ) ];
182 0 : ulong tx_busy = net_metrics[ MIDX( GAUGE, NET, TX_BUSY_CNT ) ];
183 0 : printf( "\033[2K" " Net busy: %.2f%%\n"
184 0 : "\033[2K" " RX ok: %10.3e pps %10.3e bps\n"
185 0 : "\033[2K" " RX drop: %10.3e pps\n"
186 0 : "\033[2K" " TX ok: %10.3e pps %10.3e bps\n"
187 0 : "\033[2K" " RX bufs: %6lu idle %6lu busy\n"
188 0 : "\033[2K" " TX bufs: %6lu idle %6lu busy\n",
189 0 : 100.*busy_r,
190 0 : rx_ok_pps, rx_bps,
191 0 : rx_drop_pps,
192 0 : tx_ok_pps, tx_bps,
193 0 : rx_idle, rx_busy,
194 0 : tx_idle, tx_busy );
195 :
196 0 : fputs( "\0338", stdout ); /* restore cursor position */
197 0 : fflush( stdout );
198 0 : }
199 :
200 : /* FIXME fixup screen on window size changes */
201 :
202 : void
203 : pktgen_cmd_fn( args_t * args FD_PARAM_UNUSED,
204 0 : config_t * config ) {
205 0 : pktgen_topo( config );
206 0 : fd_topo_t * topo = &config->topo;
207 0 : fd_topo_tile_t * net_tile = &topo->tiles[ fd_topo_find_tile( topo, "net", 0UL ) ];
208 0 : fd_topo_tile_t * metric_tile = &topo->tiles[ fd_topo_find_tile( topo, "metric", 0UL ) ];
209 :
210 0 : ushort const listen_port = 9000;
211 0 : net_tile->net.legacy_transaction_listen_port = listen_port;
212 :
213 0 : if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->tiles.metric.prometheus_listen_address, &metric_tile->metric.prometheus_listen_addr ) ) )
214 0 : FD_LOG_ERR(( "failed to parse prometheus listen address `%s`", config->tiles.metric.prometheus_listen_address ));
215 0 : metric_tile->metric.prometheus_listen_port = config->tiles.metric.prometheus_listen_port;
216 :
217 0 : configure_stage( &fd_cfg_stage_sysctl, CONFIGURE_CMD_INIT, config );
218 0 : configure_stage( &fd_cfg_stage_hugetlbfs, CONFIGURE_CMD_INIT, config );
219 0 : configure_stage( &fd_cfg_stage_ethtool_channels, CONFIGURE_CMD_INIT, config );
220 0 : configure_stage( &fd_cfg_stage_ethtool_gro, CONFIGURE_CMD_INIT, config );
221 :
222 0 : fdctl_check_configure( config );
223 : /* FIXME this allocates lots of memory unnecessarily */
224 0 : initialize_workspaces( config );
225 0 : initialize_stacks( config );
226 0 : fdctl_setup_netns( config, 1 );
227 0 : (void)fd_topo_install_xdp( topo );
228 0 : fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_WRITE );
229 :
230 : /* FIXME allow running sandboxed/multiprocess */
231 0 : fd_topo_run_single_process( topo, 2, config->uid, config->gid, fdctl_tile_run, NULL );
232 :
233 0 : ulong volatile const * net_metrics = fd_metrics_tile( net_tile->metrics );
234 :
235 : /* Don't attempt to render TTY */
236 0 : if( !isatty( STDOUT_FILENO ) ) {
237 0 : puts( "stdout is not a tty, not taking commands" );
238 0 : FD_VOLATILE( fd_pktgen_active ) = 1;
239 0 : for(;;) pause();
240 0 : return;
241 0 : }
242 :
243 : /* Clear screen */
244 0 : struct winsize w;
245 0 : if( FD_UNLIKELY( 0!=ioctl( STDOUT_FILENO, TIOCGWINSZ, &w ) ) ) {
246 0 : FD_LOG_WARNING(( "ioctl(STDOUT_FILENO,TIOCGWINSZ) failed" ));
247 0 : } else {
248 0 : for( ulong i=0UL; i<w.ws_row; i++ ) putc( '\n', stdout );
249 0 : }
250 :
251 : /* Simple REPL loop */
252 0 : puts( "Running fddev pktgen" );
253 0 : printf( "XDP socket listening on port %u\n", (uint)listen_port );
254 0 : puts( "Available commands: start, stop, quit" );
255 0 : puts( "" );
256 0 : char input[ 256 ] = {0};
257 0 : for(;;) {
258 0 : render_status( net_metrics );
259 0 : fputs( "pktgen> ", stdout );
260 0 : fflush( stdout );
261 :
262 0 : for(;;) {
263 0 : struct pollfd fds[1] = {{ .fd=STDIN_FILENO, .events=POLLIN }};
264 0 : int poll_res = poll( fds, 1, 500 );
265 0 : if( poll_res==0 ) {
266 0 : render_status( net_metrics );
267 0 : continue;
268 0 : } else if( poll_res>0 ) {
269 0 : break;
270 0 : } else {
271 0 : FD_LOG_ERR(( "poll(STDIN_FILENO) failed" ));
272 0 : break;
273 0 : }
274 0 : }
275 :
276 0 : if( fgets( input, sizeof(input), stdin )==NULL ) {
277 0 : putc( '\n', stdout );
278 0 : break;
279 0 : }
280 0 : input[ strcspn( input, "\n" ) ] = '\0';
281 0 : input[ sizeof(input)-1 ] = '\0';
282 :
283 0 : if( !input[0] ) {
284 : /* No command */
285 0 : } else if( !strcmp( input, "exit" ) || !strcmp( input, "quit" ) ) {
286 0 : break;
287 0 : } else if( !strcmp( input, "start" ) ) {
288 0 : FD_VOLATILE( fd_pktgen_active ) = 1U;
289 0 : } else if( !strcmp( input, "stop" ) ) {
290 0 : FD_VOLATILE( fd_pktgen_active ) = 0U;
291 0 : } else {
292 0 : fputs( "Unknown command\n", stdout );
293 0 : }
294 0 : }
295 0 : puts( "Exiting" );
296 0 : }
|