LCOV - code coverage report
Current view: top level - app/shared_dev/commands/pktgen - pktgen.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 268 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 5 0.0 %

          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, 0, 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 :     }
     200             : 
     201             : 
     202           0 :     ulong cum_idle_delta = cum_idle_now-last->cum_idle;
     203           0 :     ulong cum_tick_delta = cum_tick_now-last->cum_tick;
     204           0 :     ulong rx_ok_delta    = rx_ok_now   -last->rx_ok;
     205           0 :     ulong rx_byte_delta  = rx_byte_now -last->rx_byte;
     206           0 :     ulong rx_drop_delta  = rx_drop_now -last->rx_drop;
     207           0 :     ulong tx_ok_delta    = tx_ok_now   -last->tx_ok;
     208           0 :     ulong tx_byte_delta  = tx_byte_now -last->tx_byte;
     209             : 
     210           0 :     stats->busy_r      = 1.0 - ( (double)cum_idle_delta / (double)cum_tick_delta );
     211           0 :     stats->rx_ok_pps   = 1e9*( (double)rx_ok_delta  /(double)dt );
     212           0 :     stats->rx_bps      = 8e9*( (double)rx_byte_delta/(double)dt );
     213           0 :     stats->rx_drop_pps = 1e9*( (double)rx_drop_delta/(double)dt );
     214           0 :     stats->tx_ok_pps   = 1e9*( (double)tx_ok_delta  /(double)dt );
     215           0 :     stats->tx_bps      = 8e9*( (double)tx_byte_delta/(double)dt );
     216             : 
     217           0 :     last->ts       = now;
     218           0 :     last->cum_idle = cum_idle_now;
     219           0 :     last->cum_tick = cum_tick_now;
     220           0 :     last->rx_ok    = rx_ok_now;
     221           0 :     last->rx_byte  = rx_byte_now;
     222           0 :     last->rx_drop  = rx_drop_now;
     223           0 :     last->tx_ok    = tx_ok_now;
     224           0 :     last->tx_byte  = tx_byte_now;
     225           0 :   }
     226             : 
     227           0 :   return net_stats;
     228           0 : }
     229             : 
     230             : /* render_status prints statistics at the top of the screen.
     231             :    Should be called at a low rate (~500ms). */
     232             : 
     233             : static void
     234             : render_status( ulong volatile const * net_metrics[ FD_TOPO_MAX_TILES ],
     235             :                ulong                  net_tile_cnt,
     236           0 :                char const *           provider ) {
     237           0 :   fputs( "\0337"      /* save cursor position */
     238           0 :          "\033[H"     /* move cursor to (0,0) */
     239           0 :          "\033[2K\n", /* create an empty line to avoid spamming look back buffer */
     240           0 :          stdout );
     241           0 :   printf( "\033[2K" "[Firedancer pktgen] mode=%s\n",
     242           0 :           FD_VOLATILE_CONST( fd_pktgen_active ) ? "send+recv" : "recv" );
     243             : 
     244           0 :   fd_net_stats_t const * stats = get_net_stats( net_metrics, net_tile_cnt, provider );
     245             : 
     246           0 :   fd_net_stats_t summary = {0};
     247           0 :   for( ulong i=0UL; i<net_tile_cnt; i++ ) {
     248           0 :     summary.busy_r       += stats[ i ].busy_r;
     249           0 :     summary.rx_ok_pps    += stats[ i ].rx_ok_pps;
     250           0 :     summary.rx_bps       += stats[ i ].rx_bps;
     251           0 :     summary.rx_drop_pps  += stats[ i ].rx_drop_pps;
     252           0 :     summary.tx_ok_pps    += stats[ i ].tx_ok_pps;
     253           0 :     summary.tx_bps       += stats[ i ].tx_bps;
     254           0 :     summary.rx_bufs_idle += stats[ i ].rx_bufs_idle;
     255           0 :     summary.rx_bufs_busy += stats[ i ].rx_bufs_busy;
     256           0 :     summary.tx_bufs_idle += stats[ i ].tx_bufs_idle;
     257           0 :     summary.tx_bufs_busy += stats[ i ].tx_bufs_busy;
     258           0 :   }
     259           0 :   summary.busy_r /= (double)net_tile_cnt;
     260             : 
     261           0 :   printf( "\033[2K" "Overall stats:\n" );
     262             : 
     263             :   /* Render packet per second rates */
     264           0 :   printf( "\033[2K" "  Net busy: %.2f%%\n"
     265           0 :           "\033[2K" "  RX ok:   %10.3e pps %10.3e bps\n"
     266           0 :           "\033[2K" "  RX drop: %10.3e pps\n"
     267           0 :           "\033[2K" "  TX ok:   %10.3e pps %10.3e bps\n"
     268           0 :           "\033[2K" "  RX bufs: %6lu idle %6lu busy\n"
     269           0 :           "\033[2K" "  TX bufs: %6lu idle %6lu busy\n",
     270           0 :           100.*summary.busy_r,
     271           0 :           summary.rx_ok_pps,    summary.rx_bps,
     272           0 :           summary.rx_drop_pps,
     273           0 :           summary.tx_ok_pps,    summary.tx_bps,
     274           0 :           summary.rx_bufs_idle, summary.rx_bufs_busy,
     275           0 :           summary.tx_bufs_idle, summary.tx_bufs_busy );
     276             : 
     277           0 :   printf( "\033[2K" "Individual net tiles:\n" );
     278             : 
     279           0 :   for( ulong i=0UL; i<net_tile_cnt; i++ ) {
     280           0 :     printf( "\033[2K" "  net:%-3lu busy %6.2f%% | RX(ok) %10.3e pps | TX(ok) %10.3e pps\n",
     281           0 :             i,
     282           0 :             100.*stats[ i ].busy_r,
     283           0 :             stats[ i ].rx_ok_pps,
     284           0 :             stats[ i ].tx_ok_pps );
     285           0 :   }
     286             : 
     287           0 :   fputs( "\0338", stdout ); /* restore cursor position */
     288           0 :   fflush( stdout );
     289           0 : }
     290             : 
     291             : /* FIXME fixup screen on window size changes */
     292             : 
     293             : void
     294             : pktgen_cmd_fn( args_t *   args FD_PARAM_UNUSED,
     295           0 :                config_t * config ) {
     296           0 :   fd_topo_t *      topo         = &config->topo;
     297           0 :   ulong            net_tile_cnt = config->layout.net_tile_count;
     298           0 :   if( FD_UNLIKELY( net_tile_cnt>FD_TOPO_MAX_TILES ) ) {
     299           0 :     FD_LOG_ERR(( "more net tiles (%lu) than max num tiles allowed (%lu)", net_tile_cnt, FD_TOPO_MAX_TILES ));
     300           0 :   }
     301             : 
     302           0 :   fd_topo_tile_t * metric_tile  = &topo->tiles[ fd_topo_find_tile( topo, "metric", 0UL ) ];
     303             : 
     304           0 :   fd_topo_tile_t       * net_tiles  [ FD_TOPO_MAX_TILES ];
     305           0 :   ulong volatile const * net_metrics[ FD_TOPO_MAX_TILES ];
     306             : 
     307           0 :   ushort const listen_port = 9000;
     308           0 :   config->tiles.quic.regular_transaction_listen_port = listen_port;
     309           0 :   for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
     310           0 :     net_tiles[ kind_id ] = &topo->tiles[ fd_topo_find_tile( topo, fd_net_tile_name( config->net.provider ), kind_id ) ];
     311           0 :     net_tiles[ kind_id ]->net.legacy_transaction_listen_port = listen_port;
     312           0 :   }
     313             : 
     314           0 :   if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->tiles.metric.prometheus_listen_address, &metric_tile->metric.prometheus_listen_addr ) ) )
     315           0 :     FD_LOG_ERR(( "failed to parse prometheus listen address `%s`", config->tiles.metric.prometheus_listen_address ));
     316           0 :   metric_tile->metric.prometheus_listen_port = config->tiles.metric.prometheus_listen_port;
     317             : 
     318           0 :   configure_stage( &fd_cfg_stage_mlx5,             CONFIGURE_CMD_INIT, config );
     319           0 :   configure_stage( &fd_cfg_stage_sysctl,           CONFIGURE_CMD_INIT, config );
     320           0 :   configure_stage( &fd_cfg_stage_hugetlbfs,        CONFIGURE_CMD_INIT, config );
     321           0 :   configure_stage( &fd_cfg_stage_bonding,          CONFIGURE_CMD_INIT, config );
     322           0 :   configure_stage( &fd_cfg_stage_ethtool_channels, CONFIGURE_CMD_INIT, config );
     323           0 :   configure_stage( &fd_cfg_stage_ethtool_offloads, CONFIGURE_CMD_INIT, config );
     324           0 :   configure_stage( &fd_cfg_stage_sysfs_poll,       CONFIGURE_CMD_INIT, config );
     325             : 
     326           0 :   fdctl_check_configure( config );
     327             :   /* FIXME this allocates lots of memory unnecessarily */
     328           0 :   initialize_workspaces( config );
     329           0 :   initialize_stacks( config );
     330           0 :   if( 0==strcmp( config->net.provider, "xdp" ) ) {
     331           0 :     fd_topo_install_xdp_simple( &config->topo, config->net.bind_address_parsed );
     332           0 :   }
     333           0 :   fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     334           0 :   if( 0==strcmp( config->net.provider, "mlx5" ) ) {
     335           0 :     fd_topo_install_mlx5( topo, NULL );
     336           0 :   }
     337             : 
     338             :   /* FIXME allow running sandboxed/multiprocess */
     339           0 :   fd_topo_run_single_process( topo, config->uid, config->gid, fdctl_tile_run );
     340             : 
     341           0 :   for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
     342           0 :     net_metrics[ kind_id ] = fd_metrics_tile( net_tiles[ kind_id ]->metrics );
     343           0 :   }
     344             : 
     345             :   /* Don't attempt to render TTY */
     346           0 :   if( !isatty( STDOUT_FILENO ) ) {
     347           0 :     puts( "stdout is not a tty, not taking commands" );
     348           0 :     FD_VOLATILE( fd_pktgen_active ) = 1;
     349           0 :     for(;;) pause();
     350           0 :     return;
     351           0 :   }
     352             : 
     353             :   /* Clear screen */
     354           0 :   struct winsize w;
     355           0 :   if( FD_UNLIKELY( 0!=ioctl( STDOUT_FILENO, TIOCGWINSZ, &w ) ) ) {
     356           0 :     FD_LOG_WARNING(( "ioctl(STDOUT_FILENO,TIOCGWINSZ) failed" ));
     357           0 :   } else {
     358           0 :     for( ulong i=0UL; i<w.ws_row; i++ ) putc( '\n', stdout );
     359           0 :   }
     360             : 
     361             :   /* Simple REPL loop */
     362           0 :   puts( "Running firedancer-dev pktgen" );
     363           0 :   printf( "%s socket listening on port %u\n", config->net.provider, (uint)listen_port );
     364           0 :   puts( "Available commands: start, stop, quit" );
     365           0 :   puts( "" );
     366           0 :   char input[ 256 ] = {0};
     367           0 :   for(;;) {
     368           0 :     render_status( net_metrics, net_tile_cnt, config->net.provider );
     369           0 :     fputs( "pktgen> ", stdout );
     370           0 :     fflush( stdout );
     371             : 
     372           0 :     for(;;) {
     373           0 :       struct pollfd fds[1] = {{ .fd=STDIN_FILENO, .events=POLLIN }};
     374           0 :       int poll_res = poll( fds, 1, 500 );
     375           0 :       if( poll_res==0 ) {
     376           0 :         render_status( net_metrics, net_tile_cnt, config->net.provider );
     377           0 :         continue;
     378           0 :       } else if( poll_res>0 ) {
     379           0 :         break;
     380           0 :       } else {
     381           0 :         FD_LOG_ERR(( "poll(STDIN_FILENO) failed" ));
     382           0 :         break;
     383           0 :       }
     384           0 :     }
     385             : 
     386           0 :     if( fgets( input, sizeof(input), stdin )==NULL ) {
     387           0 :       putc( '\n', stdout );
     388           0 :       break;
     389           0 :     }
     390           0 :     input[ strcspn( input, "\n" ) ] = '\0';
     391           0 :     input[ sizeof(input)-1        ] = '\0';
     392             : 
     393           0 :     if( !input[0] ) {
     394             :       /* No command */
     395           0 :     } else if( !strcmp( input, "exit" ) || !strcmp( input, "quit" ) ) {
     396           0 :       break;
     397           0 :     } else if( !strcmp( input, "start" ) ) {
     398           0 :       FD_VOLATILE( fd_pktgen_active ) = 1U;
     399           0 :     } else if( !strcmp( input, "stop" ) ) {
     400           0 :       FD_VOLATILE( fd_pktgen_active ) = 0U;
     401           0 :     } else {
     402           0 :       fputs( "Unknown command\n", stdout );
     403           0 :     }
     404           0 :   }
     405           0 :   puts( "Exiting" );
     406           0 : }
     407             : 
     408             : action_t fd_action_pktgen = {
     409             :   .name        = "pktgen",
     410             :   .topo        = pktgen_topo,
     411             :   .args        = pktgen_cmd_args,
     412             :   .fn          = pktgen_cmd_fn,
     413             :   .perm        = dev_cmd_perm,
     414             :   .description = "Flood interface with invalid Ethernet frames"
     415             : };

Generated by: LCOV version 1.14