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

Generated by: LCOV version 1.14