Line data Source code
1 : #include "../dev.h"
2 : #include "../../../shared/commands/configure/configure.h" /* CONFIGURE_CMD_INIT */
3 : #include "../../../shared/commands/run/run.h" /* fdctl_check_configure */
4 : #include "../../../../disco/net/fd_net_tile.h"
5 : #include "../../../../disco/metrics/fd_metrics.h"
6 : #include "../../../../disco/topo/fd_topob.h"
7 : #include "../../../../disco/topo/fd_cpu_topo.h"
8 : #include "../../../../util/net/fd_ip4.h"
9 :
10 : #include <stdio.h> /* printf */
11 : #include <unistd.h> /* isatty */
12 : #include <sys/ioctl.h>
13 : #include <poll.h>
14 :
15 : extern fd_topo_obj_callbacks_t * CALLBACKS[];
16 :
17 : fd_topo_run_tile_t
18 : fdctl_tile_run( fd_topo_tile_t const * tile );
19 :
20 : static void
21 0 : pktgen_topo( config_t * config ) {
22 0 : ulong net_tile_cnt = config->layout.net_tile_count;
23 :
24 0 : char const * affinity = config->development.pktgen.affinity;
25 0 : int is_auto_affinity = !strcmp( affinity, "auto" );
26 :
27 0 : ushort parsed_tile_to_cpu[ FD_TILE_MAX ];
28 0 : for( ulong i=0UL; i<FD_TILE_MAX; i++ ) parsed_tile_to_cpu[ i ] = USHORT_MAX;
29 :
30 0 : fd_topo_cpus_t cpus[1];
31 0 : fd_topo_cpus_init( cpus );
32 :
33 0 : ulong affinity_tile_cnt = 0UL;
34 0 : ulong required_tile_cnt = 3UL+net_tile_cnt;
35 0 : if( FD_LIKELY( !is_auto_affinity ) ) affinity_tile_cnt = fd_topob_parse_affinity_cstr( affinity, parsed_tile_to_cpu, 0, 1 );
36 :
37 0 : ulong tile_to_cpu[ FD_TILE_MAX ] = {0};
38 0 : for( ulong i=0UL; i<affinity_tile_cnt; i++ ) {
39 0 : ushort cpu_idx = (ushort)( parsed_tile_to_cpu[ i ] & ~FD_TOPOB_CPU_SHARED );
40 0 : if( FD_UNLIKELY( parsed_tile_to_cpu[ i ]!=USHORT_MAX && cpu_idx>=cpus->cpu_cnt ) )
41 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 "
42 0 : "only has %lu CPUs. You should either change the CPU allocations in the affinity string, or increase the number of CPUs "
43 0 : "in the system.",
44 0 : cpu_idx, cpus->cpu_cnt ));
45 0 : tile_to_cpu[ i ] = fd_ulong_if( parsed_tile_to_cpu[ i ]==USHORT_MAX, ULONG_MAX, (ulong)parsed_tile_to_cpu[ i ] );
46 0 : }
47 0 : if( FD_LIKELY( !is_auto_affinity ) ) {
48 0 : if( FD_UNLIKELY( affinity_tile_cnt!=required_tile_cnt ) )
49 0 : FD_LOG_ERR(( "Invalid [development.pktgen.affinity]: must include exactly %lu CPUs", required_tile_cnt ));
50 0 : }
51 :
52 : /* Reset topology from scratch */
53 0 : fd_topo_t * topo = &config->topo;
54 0 : fd_topob_new( &config->topo, config->name );
55 0 : topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
56 :
57 0 : fd_topob_wksp( topo, "metric" );
58 0 : fd_topob_wksp( topo, "metric_in" );
59 0 : 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 );
60 0 : fd_topob_tile( topo, "metric", "metric", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 1 );
61 :
62 0 : char const * net_tile_name = fd_net_tile_name( config->net.provider );
63 :
64 0 : fd_topob_wksp( topo, "pktgen" );
65 0 : fd_topo_tile_t * pktgen_tile = fd_topob_tile( topo, "pktgen", "pktgen", "pktgen", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 );
66 0 : if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.pktgen.fake_dst_ip, &pktgen_tile->pktgen.fake_dst_ip ) ) ) {
67 0 : FD_LOG_ERR(( "Invalid [development.pktgen.fake_dst_ip]" ));
68 0 : }
69 0 : fd_topob_link( topo, "pktgen_out", "pktgen", 32768UL, FD_NET_MTU, 1UL );
70 0 : fd_topob_tile_out( topo, "pktgen", 0UL, "pktgen_out", 0UL );
71 0 : for( ulong i=0UL; i<net_tile_cnt; i++ ) {
72 0 : fd_topob_tile_in( topo, net_tile_name, i, "metric_in", "pktgen_out", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
73 :
74 : /* Create dummy RX link */
75 0 : fd_topos_net_rx_link( topo, "net_quic", i, config->net.ingress_buffer_size );
76 0 : fd_topob_tile_in( topo, "pktgen", 0UL, "metric_in", "net_quic", i, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
77 :
78 0 : fd_topos_net_tile_finish( topo, i );
79 0 : }
80 :
81 :
82 0 : if( FD_UNLIKELY( is_auto_affinity ) ) fd_topob_auto_layout( topo );
83 0 : fd_topob_waker( topo );
84 0 : fd_topob_finish( topo, CALLBACKS );
85 0 : fd_topo_print_log( /* stdout */ 1, topo );
86 0 : }
87 :
88 : void
89 : pktgen_cmd_args( int * pargc,
90 : char *** pargv,
91 0 : args_t * args ) {
92 : /* FIXME add config options here */
93 0 : (void)pargc; (void)pargv; (void)args;
94 0 : }
95 :
96 : /* Hacky: Since the pktgen runs in the same process, use globals to
97 : share state */
98 : extern uint fd_pktgen_active;
99 :
100 : /* fd_net_stats stores the stats for a single net tile. */
101 :
102 : struct fd_net_stats {
103 : double busy_r;
104 : double rx_ok_pps;
105 : double rx_bps;
106 : double rx_drop_pps;
107 : double tx_ok_pps;
108 : double tx_bps;
109 :
110 : ulong rx_bufs_idle;
111 : ulong rx_bufs_busy;
112 : ulong tx_bufs_idle;
113 : ulong tx_bufs_busy;
114 : };
115 : typedef struct fd_net_stats fd_net_stats_t;
116 :
117 : /* fd_net_metrics_last stores recent key metrics for a single net
118 : tile, used to calculate the next net_stats. */
119 :
120 : struct fd_net_metrics_last {
121 : long ts;
122 : ulong cum_idle;
123 : ulong cum_tick;
124 : ulong rx_ok;
125 : ulong rx_byte;
126 : ulong rx_drop;
127 : ulong tx_ok;
128 : ulong tx_byte;
129 : };
130 : typedef struct fd_net_metrics_last fd_net_metrics_last_t;
131 :
132 : /* get_net_stats computes and returns per net tile stats. */
133 :
134 : static fd_net_stats_t const *
135 : get_net_stats( ulong volatile const * net_metrics[ FD_TOPO_MAX_TILES ],
136 : ulong net_tile_cnt,
137 0 : char const * provider ) {
138 :
139 0 : static fd_net_stats_t net_stats [ FD_TOPO_MAX_TILES ];
140 0 : static fd_net_metrics_last_t net_metrics_last[ FD_TOPO_MAX_TILES ];
141 :
142 0 : long now = fd_log_wallclock();
143 :
144 0 : for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
145 0 : ulong volatile const * metrics = net_metrics [ kind_id ];
146 0 : fd_net_metrics_last_t * last = &net_metrics_last[ kind_id ];
147 0 : fd_net_stats_t * stats = &net_stats [ kind_id ];
148 :
149 0 : if( FD_UNLIKELY( last->ts==0L ) ) last->ts = now;
150 :
151 0 : long dt = now-last->ts;
152 0 : if( dt<=(long)10e6 ) continue;
153 :
154 0 : ulong cum_idle_now = metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG ) ];
155 0 : /* */ cum_idle_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_SLEEPING ) ];
156 0 : ulong cum_tick_now = cum_idle_now;
157 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_SLEEPING ) ];
158 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_HOUSEKEEPING ) ];
159 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_HOUSEKEEPING ) ];
160 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_HOUSEKEEPING ) ];
161 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_PREFRAG ) ];
162 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_PREFRAG ) ];
163 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG ) ];
164 0 : /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_POSTFRAG ) ];
165 :
166 0 : ulong rx_ok_now = 0;
167 0 : ulong rx_byte_now = 0;
168 0 : ulong rx_drop_now = 0;
169 0 : ulong tx_ok_now = 0;
170 0 : ulong tx_byte_now = 0;
171 :
172 0 : if( 0==strcmp( provider, "xdp" ) ) {
173 0 : stats->rx_bufs_idle = metrics[ MIDX( GAUGE, NET, RX_BUFFER_FREE ) ];
174 0 : stats->rx_bufs_busy = metrics[ MIDX( GAUGE, NET, RX_BUFFER_USED ) ];
175 0 : stats->tx_bufs_idle = metrics[ MIDX( GAUGE, NET, TX_BUFFER_FREE ) ];
176 0 : stats->tx_bufs_busy = metrics[ MIDX( GAUGE, NET, TX_BUFFER_USED ) ];
177 :
178 0 : rx_ok_now = metrics[ MIDX( COUNTER, NET, PKT_RX ) ];
179 0 : rx_byte_now = metrics[ MIDX( COUNTER, NET, PKT_RX_BYTES ) ];
180 0 : rx_drop_now = metrics[ MIDX( COUNTER, NET, RX_FILL_RING_STALLED ) ];
181 0 : /* */ rx_drop_now += metrics[ MIDX( COUNTER, NET, XDP_RX_OTHER_DROPPED ) ];
182 0 : /* */ rx_drop_now += metrics[ MIDX( COUNTER, NET, XDP_RX_INVALID_DESCRIPTOR_DROPPED ) ];
183 0 : /* */ rx_drop_now += metrics[ MIDX( COUNTER, NET, XDP_RX_RING_FULL_DROPPED ) ];
184 0 : tx_ok_now = metrics[ MIDX( COUNTER, NET, PKT_TX_COMPLETED ) ];
185 0 : tx_byte_now = metrics[ MIDX( COUNTER, NET, PKT_TX_SUBMITTED_BYTES ) ];
186 :
187 0 : } else if( 0==strcmp( provider, "mlx5" ) ) {
188 0 : stats->rx_bufs_idle = metrics[ MIDX( GAUGE, MLX5, RX_BUFFER_FREE ) ];
189 0 : stats->rx_bufs_busy = metrics[ MIDX( GAUGE, MLX5, RX_BUFFER_USED ) ];
190 0 : stats->tx_bufs_idle = metrics[ MIDX( GAUGE, MLX5, TX_BUFFER_FREE ) ];
191 0 : stats->tx_bufs_busy = metrics[ MIDX( GAUGE, MLX5, TX_BUFFER_USED ) ];
192 :
193 0 : rx_ok_now = metrics[ MIDX( COUNTER, MLX5, PKT_RX ) ];
194 0 : rx_byte_now = metrics[ MIDX( COUNTER, MLX5, PKT_RX_BYTES ) ];
195 0 : rx_drop_now = 0UL;
196 0 : for( ulong r=0UL; r<FD_METRICS_ENUM_PKT_RX_DROP_REASON_CNT; r++ ) rx_drop_now += metrics[ MIDX( COUNTER, MLX5, PKT_RX_DROPPED )+r ];
197 0 : tx_ok_now = metrics[ MIDX( COUNTER, MLX5, PKT_TX_COMPLETED ) ];
198 0 : tx_byte_now = metrics[ MIDX( COUNTER, MLX5, PKT_TX_COMPLETED_BYTES ) ];
199 0 : } else if( 0==strcmp( provider, "iavf" ) ) {
200 0 : stats->rx_bufs_idle = metrics[ MIDX( GAUGE, IAVF, RX_BUFFER_FREE ) ];
201 0 : stats->rx_bufs_busy = metrics[ MIDX( GAUGE, IAVF, RX_BUFFER_USED ) ];
202 0 : stats->tx_bufs_idle = metrics[ MIDX( GAUGE, IAVF, TX_BUFFER_FREE ) ];
203 0 : stats->tx_bufs_busy = metrics[ MIDX( GAUGE, IAVF, TX_BUFFER_USED ) ];
204 :
205 0 : rx_ok_now = metrics[ MIDX( COUNTER, IAVF, PKT_RX ) ];
206 0 : rx_byte_now = metrics[ MIDX( COUNTER, IAVF, PKT_RX_BYTES ) ];
207 0 : rx_drop_now = 0UL;
208 0 : for( ulong r=0UL; r<FD_METRICS_ENUM_PKT_RX_DROP_REASON_CNT; r++ ) rx_drop_now += metrics[ MIDX( COUNTER, IAVF, PKT_RX_DROPPED )+r ];
209 0 : rx_drop_now += metrics[ MIDX( COUNTER, IAVF, PKT_RX_NO_LINK_DROPPED ) ];
210 0 : tx_ok_now = metrics[ MIDX( COUNTER, IAVF, PKT_TX_COMPLETED ) ];
211 0 : tx_byte_now = metrics[ MIDX( COUNTER, IAVF, PKT_TX_COMPLETED_BYTES ) ];
212 0 : }
213 :
214 :
215 0 : ulong cum_idle_delta = cum_idle_now-last->cum_idle;
216 0 : ulong cum_tick_delta = cum_tick_now-last->cum_tick;
217 0 : ulong rx_ok_delta = rx_ok_now -last->rx_ok;
218 0 : ulong rx_byte_delta = rx_byte_now -last->rx_byte;
219 0 : ulong rx_drop_delta = rx_drop_now -last->rx_drop;
220 0 : ulong tx_ok_delta = tx_ok_now -last->tx_ok;
221 0 : ulong tx_byte_delta = tx_byte_now -last->tx_byte;
222 :
223 0 : stats->busy_r = 1.0 - ( (double)cum_idle_delta / (double)cum_tick_delta );
224 0 : stats->rx_ok_pps = 1e9*( (double)rx_ok_delta /(double)dt );
225 0 : stats->rx_bps = 8e9*( (double)rx_byte_delta/(double)dt );
226 0 : stats->rx_drop_pps = 1e9*( (double)rx_drop_delta/(double)dt );
227 0 : stats->tx_ok_pps = 1e9*( (double)tx_ok_delta /(double)dt );
228 0 : stats->tx_bps = 8e9*( (double)tx_byte_delta/(double)dt );
229 :
230 0 : last->ts = now;
231 0 : last->cum_idle = cum_idle_now;
232 0 : last->cum_tick = cum_tick_now;
233 0 : last->rx_ok = rx_ok_now;
234 0 : last->rx_byte = rx_byte_now;
235 0 : last->rx_drop = rx_drop_now;
236 0 : last->tx_ok = tx_ok_now;
237 0 : last->tx_byte = tx_byte_now;
238 0 : }
239 :
240 0 : return net_stats;
241 0 : }
242 :
243 : /* render_status prints statistics at the top of the screen.
244 : Should be called at a low rate (~500ms). */
245 :
246 : static void
247 : render_status( ulong volatile const * net_metrics[ FD_TOPO_MAX_TILES ],
248 : ulong net_tile_cnt,
249 0 : char const * provider ) {
250 0 : fputs( "\0337" /* save cursor position */
251 0 : "\033[H" /* move cursor to (0,0) */
252 0 : "\033[2K\n", /* create an empty line to avoid spamming look back buffer */
253 0 : stdout );
254 0 : printf( "\033[2K" "[Firedancer pktgen] mode=%s\n",
255 0 : FD_VOLATILE_CONST( fd_pktgen_active ) ? "send+recv" : "recv" );
256 :
257 0 : fd_net_stats_t const * stats = get_net_stats( net_metrics, net_tile_cnt, provider );
258 :
259 0 : fd_net_stats_t summary = {0};
260 0 : for( ulong i=0UL; i<net_tile_cnt; i++ ) {
261 0 : summary.busy_r += stats[ i ].busy_r;
262 0 : summary.rx_ok_pps += stats[ i ].rx_ok_pps;
263 0 : summary.rx_bps += stats[ i ].rx_bps;
264 0 : summary.rx_drop_pps += stats[ i ].rx_drop_pps;
265 0 : summary.tx_ok_pps += stats[ i ].tx_ok_pps;
266 0 : summary.tx_bps += stats[ i ].tx_bps;
267 0 : summary.rx_bufs_idle += stats[ i ].rx_bufs_idle;
268 0 : summary.rx_bufs_busy += stats[ i ].rx_bufs_busy;
269 0 : summary.tx_bufs_idle += stats[ i ].tx_bufs_idle;
270 0 : summary.tx_bufs_busy += stats[ i ].tx_bufs_busy;
271 0 : }
272 0 : summary.busy_r /= (double)net_tile_cnt;
273 :
274 0 : printf( "\033[2K" "Overall stats:\n" );
275 :
276 : /* Render packet per second rates */
277 0 : printf( "\033[2K" " Net busy: %.2f%%\n"
278 0 : "\033[2K" " RX ok: %10.3e pps %10.3e bps\n"
279 0 : "\033[2K" " RX drop: %10.3e pps\n"
280 0 : "\033[2K" " TX ok: %10.3e pps %10.3e bps\n"
281 0 : "\033[2K" " RX bufs: %6lu idle %6lu busy\n"
282 0 : "\033[2K" " TX bufs: %6lu idle %6lu busy\n",
283 0 : 100.*summary.busy_r,
284 0 : summary.rx_ok_pps, summary.rx_bps,
285 0 : summary.rx_drop_pps,
286 0 : summary.tx_ok_pps, summary.tx_bps,
287 0 : summary.rx_bufs_idle, summary.rx_bufs_busy,
288 0 : summary.tx_bufs_idle, summary.tx_bufs_busy );
289 :
290 0 : printf( "\033[2K" "Individual net tiles:\n" );
291 :
292 0 : for( ulong i=0UL; i<net_tile_cnt; i++ ) {
293 0 : printf( "\033[2K" " net:%-3lu busy %6.2f%% | RX(ok) %10.3e pps | TX(ok) %10.3e pps\n",
294 0 : i,
295 0 : 100.*stats[ i ].busy_r,
296 0 : stats[ i ].rx_ok_pps,
297 0 : stats[ i ].tx_ok_pps );
298 0 : }
299 :
300 0 : fputs( "\0338", stdout ); /* restore cursor position */
301 0 : fflush( stdout );
302 0 : }
303 :
304 : /* FIXME fixup screen on window size changes */
305 :
306 : void
307 : pktgen_cmd_fn( args_t * args FD_PARAM_UNUSED,
308 0 : config_t * config ) {
309 0 : fd_topo_t * topo = &config->topo;
310 0 : ulong net_tile_cnt = config->layout.net_tile_count;
311 0 : if( FD_UNLIKELY( net_tile_cnt>FD_TOPO_MAX_TILES ) ) {
312 0 : FD_LOG_ERR(( "more net tiles (%lu) than max num tiles allowed (%lu)", net_tile_cnt, FD_TOPO_MAX_TILES ));
313 0 : }
314 :
315 0 : fd_topo_tile_t * metric_tile = &topo->tiles[ fd_topo_find_tile( topo, "metric", 0UL ) ];
316 :
317 0 : fd_topo_tile_t * net_tiles [ FD_TOPO_MAX_TILES ];
318 0 : ulong volatile const * net_metrics[ FD_TOPO_MAX_TILES ];
319 :
320 0 : ushort const listen_port = 9000;
321 0 : config->tiles.quic.regular_transaction_listen_port = listen_port;
322 0 : for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
323 0 : net_tiles[ kind_id ] = &topo->tiles[ fd_topo_find_tile( topo, fd_net_tile_name( config->net.provider ), kind_id ) ];
324 0 : net_tiles[ kind_id ]->net.legacy_transaction_listen_port = listen_port;
325 0 : }
326 :
327 0 : if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->tiles.metric.prometheus_listen_address, &metric_tile->metric.prometheus_listen_addr ) ) )
328 0 : FD_LOG_ERR(( "failed to parse prometheus listen address `%s`", config->tiles.metric.prometheus_listen_address ));
329 0 : metric_tile->metric.prometheus_listen_port = config->tiles.metric.prometheus_listen_port;
330 :
331 0 : configure_stage( &fd_cfg_stage_mlx5, CONFIGURE_CMD_INIT, config );
332 0 : configure_stage( &fd_cfg_stage_iavf, CONFIGURE_CMD_INIT, config );
333 0 : configure_stage( &fd_cfg_stage_sysctl, CONFIGURE_CMD_INIT, config );
334 0 : configure_stage( &fd_cfg_stage_hugetlbfs, CONFIGURE_CMD_INIT, config );
335 0 : configure_stage( &fd_cfg_stage_bonding, CONFIGURE_CMD_INIT, config );
336 0 : configure_stage( &fd_cfg_stage_ethtool_channels, CONFIGURE_CMD_INIT, config );
337 0 : configure_stage( &fd_cfg_stage_ethtool_offloads, CONFIGURE_CMD_INIT, config );
338 0 : configure_stage( &fd_cfg_stage_sysfs_poll, CONFIGURE_CMD_INIT, config );
339 :
340 0 : fdctl_check_configure( config );
341 : /* FIXME this allocates lots of memory unnecessarily */
342 0 : initialize_workspaces( config );
343 0 : initialize_stacks( config );
344 0 : if( 0==strcmp( config->net.provider, "xdp" ) ) {
345 0 : fd_topo_install_xdp_simple( &config->topo, config->net.bind_address_parsed );
346 0 : }
347 0 : fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
348 0 : if( 0==strcmp( config->net.provider, "mlx5" ) ) {
349 0 : fd_topo_install_mlx5( topo, NULL );
350 0 : }
351 0 : if( 0==strcmp( config->net.provider, "iavf" ) ) {
352 0 : fd_topo_install_iavf( topo, NULL );
353 0 : }
354 :
355 : /* FIXME allow running sandboxed/multiprocess */
356 0 : fd_topo_run_single_process( topo, config->uid, config->gid, fdctl_tile_run );
357 :
358 0 : for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
359 0 : net_metrics[ kind_id ] = fd_metrics_tile( net_tiles[ kind_id ]->metrics );
360 0 : }
361 :
362 : /* Don't attempt to render TTY */
363 0 : if( !isatty( STDOUT_FILENO ) ) {
364 0 : puts( "stdout is not a tty, not taking commands" );
365 0 : FD_VOLATILE( fd_pktgen_active ) = 1;
366 0 : for(;;) pause();
367 0 : return;
368 0 : }
369 :
370 : /* Clear screen */
371 0 : struct winsize w;
372 0 : if( FD_UNLIKELY( 0!=ioctl( STDOUT_FILENO, TIOCGWINSZ, &w ) ) ) {
373 0 : FD_LOG_WARNING(( "ioctl(STDOUT_FILENO,TIOCGWINSZ) failed" ));
374 0 : } else {
375 0 : for( ulong i=0UL; i<w.ws_row; i++ ) putc( '\n', stdout );
376 0 : }
377 :
378 : /* Simple REPL loop */
379 0 : puts( "Running firedancer-dev pktgen" );
380 0 : printf( "%s socket listening on port %u\n", config->net.provider, (uint)listen_port );
381 0 : puts( "Available commands: start, stop, quit" );
382 0 : puts( "" );
383 0 : char input[ 256 ] = {0};
384 0 : for(;;) {
385 0 : render_status( net_metrics, net_tile_cnt, config->net.provider );
386 0 : fputs( "pktgen> ", stdout );
387 0 : fflush( stdout );
388 :
389 0 : for(;;) {
390 0 : struct pollfd fds[1] = {{ .fd=STDIN_FILENO, .events=POLLIN }};
391 0 : int poll_res = poll( fds, 1, 500 );
392 0 : if( poll_res==0 ) {
393 0 : render_status( net_metrics, net_tile_cnt, config->net.provider );
394 0 : continue;
395 0 : } else if( poll_res>0 ) {
396 0 : break;
397 0 : } else {
398 0 : FD_LOG_ERR(( "poll(STDIN_FILENO) failed" ));
399 0 : break;
400 0 : }
401 0 : }
402 :
403 0 : if( fgets( input, sizeof(input), stdin )==NULL ) {
404 0 : putc( '\n', stdout );
405 0 : break;
406 0 : }
407 0 : input[ strcspn( input, "\n" ) ] = '\0';
408 0 : input[ sizeof(input)-1 ] = '\0';
409 :
410 0 : if( !input[0] ) {
411 : /* No command */
412 0 : } else if( !strcmp( input, "exit" ) || !strcmp( input, "quit" ) ) {
413 0 : break;
414 0 : } else if( !strcmp( input, "start" ) ) {
415 0 : FD_VOLATILE( fd_pktgen_active ) = 1U;
416 0 : } else if( !strcmp( input, "stop" ) ) {
417 0 : FD_VOLATILE( fd_pktgen_active ) = 0U;
418 0 : } else {
419 0 : fputs( "Unknown command\n", stdout );
420 0 : }
421 0 : }
422 0 : puts( "Exiting" );
423 0 : }
424 :
425 : action_t fd_action_pktgen = {
426 : .name = "pktgen",
427 : .topo = pktgen_topo,
428 : .args = pktgen_cmd_args,
429 : .fn = pktgen_cmd_fn,
430 : .perm = dev_cmd_perm,
431 : .description = "Flood interface with invalid Ethernet frames"
432 : };
|