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 267 0.0 %
Date: 2026-09-17 04:28:31 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, 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, 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, 0 );
      83           0 :   topo->agave_affinity_cnt = 0;
      84           0 :   fd_topob_waker( topo );
      85           0 :   fd_topob_finish( topo, CALLBACKS );
      86           0 :   fd_topo_print_log( /* stdout */ 1, topo );
      87           0 : }
      88             : 
      89             : void
      90             : pktgen_cmd_args( int *    pargc,
      91             :                  char *** pargv,
      92           0 :                  args_t * args ) {
      93             :   /* FIXME add config options here */
      94           0 :   (void)pargc; (void)pargv; (void)args;
      95           0 : }
      96             : 
      97             : /* Hacky: Since the pktgen runs in the same process, use globals to
      98             :    share state */
      99             : extern uint fd_pktgen_active;
     100             : 
     101             : /* fd_net_stats stores the stats for a single net tile. */
     102             : 
     103             : struct fd_net_stats {
     104             :   double busy_r;
     105             :   double rx_ok_pps;
     106             :   double rx_bps;
     107             :   double rx_drop_pps;
     108             :   double tx_ok_pps;
     109             :   double tx_bps;
     110             : 
     111             :   ulong  rx_bufs_idle;
     112             :   ulong  rx_bufs_busy;
     113             :   ulong  tx_bufs_idle;
     114             :   ulong  tx_bufs_busy;
     115             : };
     116             : typedef struct fd_net_stats fd_net_stats_t;
     117             : 
     118             : /* fd_net_metrics_last stores recent key metrics for a single net
     119             :    tile, used to calculate the next net_stats. */
     120             : 
     121             : struct fd_net_metrics_last {
     122             :   long  ts;
     123             :   ulong cum_idle;
     124             :   ulong cum_tick;
     125             :   ulong rx_ok;
     126             :   ulong rx_byte;
     127             :   ulong rx_drop;
     128             :   ulong tx_ok;
     129             :   ulong tx_byte;
     130             : };
     131             : typedef struct fd_net_metrics_last fd_net_metrics_last_t;
     132             : 
     133             : /* get_net_stats computes and returns per net tile stats. */
     134             : 
     135             : static fd_net_stats_t const *
     136             : get_net_stats( ulong volatile const * net_metrics[ FD_TOPO_MAX_TILES ],
     137             :                ulong                  net_tile_cnt,
     138           0 :                char const *           provider ) {
     139             : 
     140           0 :   static fd_net_stats_t        net_stats       [ FD_TOPO_MAX_TILES ];
     141           0 :   static fd_net_metrics_last_t net_metrics_last[ FD_TOPO_MAX_TILES ];
     142             : 
     143           0 :   long now = fd_log_wallclock();
     144             : 
     145           0 :   for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
     146           0 :     ulong volatile const *  metrics = net_metrics      [ kind_id ];
     147           0 :     fd_net_metrics_last_t * last    = &net_metrics_last[ kind_id ];
     148           0 :     fd_net_stats_t *        stats   = &net_stats       [ kind_id ];
     149             : 
     150           0 :     if( FD_UNLIKELY( last->ts==0L ) ) last->ts = now;
     151             : 
     152           0 :     long dt = now-last->ts;
     153           0 :     if( dt<=(long)10e6 ) continue;
     154             : 
     155           0 :     ulong cum_idle_now  = metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_POSTFRAG        ) ];
     156           0 :     ulong cum_tick_now  = cum_idle_now;
     157           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_HOUSEKEEPING    ) ];
     158           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_HOUSEKEEPING   ) ];
     159           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_HOUSEKEEPING ) ];
     160           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_CAUGHT_UP_PREFRAG         ) ];
     161           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_PREFRAG        ) ];
     162           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_BACKPRESSURE_PREFRAG      ) ];
     163           0 :     /* */ cum_tick_now += metrics[ MIDX( COUNTER, TILE, REGIME_DURATION_NANOS_PROCESSING_POSTFRAG       ) ];
     164             : 
     165           0 :     ulong rx_ok_now   = 0;
     166           0 :     ulong rx_byte_now = 0;
     167           0 :     ulong rx_drop_now = 0;
     168           0 :     ulong tx_ok_now   = 0;
     169           0 :     ulong tx_byte_now = 0;
     170             : 
     171           0 :     if( 0==strcmp( provider, "xdp" ) ) {
     172           0 :       stats->rx_bufs_idle = metrics[ MIDX( GAUGE, NET, RX_BUFFER_IDLE ) ];
     173           0 :       stats->rx_bufs_busy = metrics[ MIDX( GAUGE, NET, RX_BUFFER_BUSY ) ];
     174           0 :       stats->tx_bufs_idle = metrics[ MIDX( GAUGE, NET, TX_BUFFER_IDLE ) ];
     175           0 :       stats->tx_bufs_busy = metrics[ MIDX( GAUGE, NET, TX_BUFFER_BUSY ) ];
     176             : 
     177           0 :       rx_ok_now     = metrics[ MIDX( COUNTER, NET, PKT_RX                    ) ];
     178           0 :       rx_byte_now   = metrics[ MIDX( COUNTER, NET, PKT_RX_BYTES              ) ];
     179           0 :       rx_drop_now   = metrics[ MIDX( COUNTER, NET, PKT_RX_FILL_RING_FULL     ) ];
     180           0 :       /* */ rx_drop_now  += metrics[ MIDX( COUNTER, NET, PKT_RX_BACKPRESSURE       ) ];
     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 ) ];
     183           0 :       /* */ rx_drop_now  += metrics[ MIDX( COUNTER, NET, XDP_RX_RING_FULL          ) ];
     184           0 :       tx_ok_now     = metrics[ MIDX( COUNTER, NET, PKT_TX_COMPLETED          ) ];
     185           0 :       tx_byte_now   = metrics[ MIDX( COUNTER, NET, PKT_TX_BYTES              ) ];
     186             : 
     187           0 :     } else if( 0==strcmp( provider, "mlx5" ) ) {
     188           0 :       stats->rx_bufs_idle = metrics[ MIDX( GAUGE, MLX5, RX_BUFFER_IDLE ) ];
     189           0 :       stats->rx_bufs_busy = metrics[ MIDX( GAUGE, MLX5, RX_BUFFER_BUSY ) ];
     190           0 :       stats->tx_bufs_idle = metrics[ MIDX( GAUGE, MLX5, TX_BUFFER_IDLE ) ];
     191           0 :       stats->tx_bufs_busy = metrics[ MIDX( GAUGE, MLX5, TX_BUFFER_BUSY ) ];
     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   = metrics[ MIDX( COUNTER, MLX5, PKT_RX_MALFORMED  ) ];
     196           0 :       /* */ rx_drop_now += metrics[ MIDX( COUNTER, MLX5, PKT_RX_ROUTE_FAIL ) ];
     197           0 :       tx_ok_now     = metrics[ MIDX( COUNTER, MLX5, PKT_TX_COMPLETED ) ];
     198           0 :       tx_byte_now   = metrics[ MIDX( COUNTER, MLX5, PKT_TX_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_sysctl,           CONFIGURE_CMD_INIT, config );
     319           0 :   configure_stage( &fd_cfg_stage_hugetlbfs,        CONFIGURE_CMD_INIT, config );
     320           0 :   configure_stage( &fd_cfg_stage_bonding,          CONFIGURE_CMD_INIT, config );
     321           0 :   configure_stage( &fd_cfg_stage_ethtool_channels, CONFIGURE_CMD_INIT, config );
     322           0 :   configure_stage( &fd_cfg_stage_ethtool_offloads, CONFIGURE_CMD_INIT, config );
     323           0 :   configure_stage( &fd_cfg_stage_sysfs_poll,       CONFIGURE_CMD_INIT, config );
     324             : 
     325           0 :   fdctl_check_configure( config );
     326             :   /* FIXME this allocates lots of memory unnecessarily */
     327           0 :   initialize_workspaces( config );
     328           0 :   initialize_stacks( config );
     329           0 :   if( 0==strcmp( config->net.provider, "xdp" ) ) {
     330           0 :     fd_topo_install_xdp_simple( &config->topo, config->net.bind_address_parsed );
     331           0 :   }
     332           0 :   fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
     333           0 :   if( 0==strcmp( config->net.provider, "mlx5" ) ) {
     334           0 :     fd_topo_install_mlx5( topo, NULL );
     335           0 :   }
     336             : 
     337             :   /* FIXME allow running sandboxed/multiprocess */
     338           0 :   fd_topo_run_single_process( topo, 2, config->uid, config->gid, fdctl_tile_run );
     339             : 
     340           0 :   for( ulong kind_id=0UL; kind_id<net_tile_cnt; kind_id++ ) {
     341           0 :     net_metrics[ kind_id ] = fd_metrics_tile( net_tiles[ kind_id ]->metrics );
     342           0 :   }
     343             : 
     344             :   /* Don't attempt to render TTY */
     345           0 :   if( !isatty( STDOUT_FILENO ) ) {
     346           0 :     puts( "stdout is not a tty, not taking commands" );
     347           0 :     FD_VOLATILE( fd_pktgen_active ) = 1;
     348           0 :     for(;;) pause();
     349           0 :     return;
     350           0 :   }
     351             : 
     352             :   /* Clear screen */
     353           0 :   struct winsize w;
     354           0 :   if( FD_UNLIKELY( 0!=ioctl( STDOUT_FILENO, TIOCGWINSZ, &w ) ) ) {
     355           0 :     FD_LOG_WARNING(( "ioctl(STDOUT_FILENO,TIOCGWINSZ) failed" ));
     356           0 :   } else {
     357           0 :     for( ulong i=0UL; i<w.ws_row; i++ ) putc( '\n', stdout );
     358           0 :   }
     359             : 
     360             :   /* Simple REPL loop */
     361           0 :   puts( "Running fddev pktgen" );
     362           0 :   printf( "%s socket listening on port %u\n", config->net.provider, (uint)listen_port );
     363           0 :   puts( "Available commands: start, stop, quit" );
     364           0 :   puts( "" );
     365           0 :   char input[ 256 ] = {0};
     366           0 :   for(;;) {
     367           0 :     render_status( net_metrics, net_tile_cnt, config->net.provider );
     368           0 :     fputs( "pktgen> ", stdout );
     369           0 :     fflush( stdout );
     370             : 
     371           0 :     for(;;) {
     372           0 :       struct pollfd fds[1] = {{ .fd=STDIN_FILENO, .events=POLLIN }};
     373           0 :       int poll_res = poll( fds, 1, 500 );
     374           0 :       if( poll_res==0 ) {
     375           0 :         render_status( net_metrics, net_tile_cnt, config->net.provider );
     376           0 :         continue;
     377           0 :       } else if( poll_res>0 ) {
     378           0 :         break;
     379           0 :       } else {
     380           0 :         FD_LOG_ERR(( "poll(STDIN_FILENO) failed" ));
     381           0 :         break;
     382           0 :       }
     383           0 :     }
     384             : 
     385           0 :     if( fgets( input, sizeof(input), stdin )==NULL ) {
     386           0 :       putc( '\n', stdout );
     387           0 :       break;
     388           0 :     }
     389           0 :     input[ strcspn( input, "\n" ) ] = '\0';
     390           0 :     input[ sizeof(input)-1        ] = '\0';
     391             : 
     392           0 :     if( !input[0] ) {
     393             :       /* No command */
     394           0 :     } else if( !strcmp( input, "exit" ) || !strcmp( input, "quit" ) ) {
     395           0 :       break;
     396           0 :     } else if( !strcmp( input, "start" ) ) {
     397           0 :       FD_VOLATILE( fd_pktgen_active ) = 1U;
     398           0 :     } else if( !strcmp( input, "stop" ) ) {
     399           0 :       FD_VOLATILE( fd_pktgen_active ) = 0U;
     400           0 :     } else {
     401           0 :       fputs( "Unknown command\n", stdout );
     402           0 :     }
     403           0 :   }
     404           0 :   puts( "Exiting" );
     405           0 : }
     406             : 
     407             : action_t fd_action_pktgen = {
     408             :   .name        = "pktgen",
     409             :   .topo        = pktgen_topo,
     410             :   .args        = pktgen_cmd_args,
     411             :   .fn          = pktgen_cmd_fn,
     412             :   .perm        = dev_cmd_perm,
     413             :   .description = "Flood interface with invalid Ethernet frames"
     414             : };

Generated by: LCOV version 1.14