From: Semir Sionek Date: Wed, 25 Jun 2025 12:16:41 +0000 (+0000) Subject: hsa: measure rtt in echo client X-Git-Tag: v26.02-rc0~173 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F89%2F43289%2F7;p=vpp.git hsa: measure rtt in echo client For TCP, rtt is extracted from transport info. For UDP & echo-bytes option, we're measuring the time between sending & receiving the first packet. When using multiple clients, min/avg/max rtt values are displayed. Type: improvement Change-Id: I617527ca55726571638996dbcff05c2292f0ad18 Signed-off-by: Semir Sionek --- diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go index 53f83542bc3..24ebb6d3d8d 100644 --- a/extras/hs-test/echo_test.go +++ b/extras/hs-test/echo_test.go @@ -8,7 +8,7 @@ import ( ) func init() { - RegisterVethTests(EchoBuiltinTest, EchoBuiltinBandwidthTest, EchoBuiltinEchobytesTest) + RegisterVethTests(EchoBuiltinTest, EchoBuiltinBandwidthTest, EchoBuiltinEchobytesTest, EchoBuiltinRoundtripTest) RegisterSoloVethTests(TcpWithLossTest) RegisterVeth6Tests(TcpWithLoss6Test) } @@ -57,6 +57,34 @@ func EchoBuiltinBandwidthTest(s *VethsSuite) { } } +func EchoBuiltinRoundtripTest(s *VethsSuite) { + regex := regexp.MustCompile(`(\.\d+)ms roundtrip`) + serverVpp := s.Containers.ServerVpp.VppInstance + + serverVpp.Vppctl("test echo server " + + " uri tcp://" + s.Interfaces.Server.Ip4AddressString() + "/" + s.Ports.Port1) + + clientVpp := s.Containers.ClientVpp.VppInstance + + o := clientVpp.Vppctl("test echo client bytes 8m" + + " uri tcp://" + s.Interfaces.Server.Ip4AddressString() + "/" + s.Ports.Port1) + s.Log(o) + s.AssertContains(o, "Test started") + s.AssertContains(o, "Test finished") + if regex.MatchString(o) { + matches := regex.FindStringSubmatch(o) + if len(matches) != 0 { + seconds, _ := strconv.ParseFloat(matches[1], 32) + // Make sure that we are within ms range + s.AssertEqualWithinThreshold(seconds, 0.5, 0.5) + } else { + s.AssertEmpty("invalid echo test client output") + } + } else { + s.AssertEmpty("invalid echo test client output") + } +} + func EchoBuiltinEchobytesTest(s *VethsSuite) { serverVpp := s.Containers.ServerVpp.VppInstance diff --git a/src/plugins/hs_apps/echo_client.c b/src/plugins/hs_apps/echo_client.c index 2d70120b8d8..4bc464b7b8c 100644 --- a/src/plugins/hs_apps/echo_client.c +++ b/src/plugins/hs_apps/echo_client.c @@ -16,6 +16,7 @@ */ #include +#include static ec_main_t ec_main; @@ -76,6 +77,20 @@ ec_session_get (ec_worker_t *wrk, u32 ec_index) return pool_elt_at_index (wrk->sessions, ec_index); } +static void +update_rtt_stats (f64 session_rtt) +{ + ec_main_t *ecm = &ec_main; + clib_spinlock_lock (&ecm->rtt_stats.w_lock); + ecm->rtt_stats.sum_rtt += session_rtt; + ecm->rtt_stats.n_sum++; + if (session_rtt < ecm->rtt_stats.min_rtt) + ecm->rtt_stats.min_rtt = session_rtt; + if (session_rtt > ecm->rtt_stats.max_rtt) + ecm->rtt_stats.max_rtt = session_rtt; + clib_spinlock_unlock (&ecm->rtt_stats.w_lock); +} + static void send_data_chunk (ec_main_t *ecm, ec_session_t *es) { @@ -221,6 +236,12 @@ receive_data_chunk (ec_worker_t *wrk, ec_session_t *es) if (n_read > 0) { + if (ecm->transport_proto == TRANSPORT_PROTO_UDP && ecm->echo_bytes && + (es->rtt_stat & EC_UDP_RTT_RX_FLAG) == 0) + { + es->rtt_stat |= EC_UDP_RTT_RX_FLAG; + update_rtt_stats (vlib_time_now (vlib_get_main ()) - es->send_rtt); + } if (ecm->cfg.verbose) { ELOG_TYPE_DECLARE (e) = @@ -331,6 +352,12 @@ ec_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) if (es->bytes_to_send > 0) { + if (ecm->transport_proto == TRANSPORT_PROTO_UDP && ecm->echo_bytes && + (es->rtt_stat & EC_UDP_RTT_TX_FLAG) == 0) + { + es->send_rtt = time_now; + es->rtt_stat |= EC_UDP_RTT_TX_FLAG; + } send_data_chunk (ecm, es); if (ecm->throughput) es->time_to_send += ecm->pacing_window_len; @@ -350,6 +377,19 @@ ec_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) if (s) { + if (ecm->transport_proto == TRANSPORT_PROTO_TCP) + { + transport_connection_t *tc; + tcp_connection_t *tcpc; + tc = transport_get_connection ( + TRANSPORT_PROTO_TCP, s->connection_index, s->thread_index); + if (PREDICT_TRUE (tc != NULL)) + { + tcpc = tcp_get_connection_from_transport (tc); + update_rtt_stats (tcpc->srtt * TCP_TICK); + } + } + vnet_disconnect_args_t _a, *a = &_a; a->handle = session_handle (s); a->app_index = ecm->app_index; @@ -420,6 +460,10 @@ ec_reset_runtime_config (ec_main_t *ecm) ecm->throughput = 0; ecm->pacing_window_len = 1; ecm->max_chunk_bytes = 128 << 10; + clib_memset (&ecm->rtt_stats, 0, sizeof (ec_rttstat_t)); + ecm->rtt_stats.min_rtt = CLIB_F64_MAX; + if (ecm->rtt_stats.w_lock == NULL) + clib_spinlock_init (&ecm->rtt_stats.w_lock); vec_free (ecm->connect_uri); } @@ -530,6 +574,7 @@ ec_cleanup (ec_main_t *ecm) ecm->pacing_window_len = 1; if (ecm->barrier_acq_needed) vlib_worker_thread_barrier_sync (ecm->vlib_main); + clib_spinlock_free (&ecm->rtt_stats.w_lock); } static int @@ -1392,6 +1437,21 @@ parse_config: /* * Done. Compute stats */ + if (ecm->transport_proto == TRANSPORT_PROTO_TCP || + (ecm->transport_proto == TRANSPORT_PROTO_UDP && ecm->echo_bytes)) + { + /* display rtt stats in milliseconds */ + if (ecm->rtt_stats.n_sum == 1) + ec_cli ("%.05fms roundtrip", ecm->rtt_stats.min_rtt * 1000); + else if (ecm->rtt_stats.n_sum > 1) + ec_cli ("%.05fms/%.05fms/%.05fms min/avg/max roundtrip", + ecm->rtt_stats.min_rtt * 1000, + ecm->rtt_stats.sum_rtt / ecm->rtt_stats.n_sum * 1000, + ecm->rtt_stats.max_rtt * 1000); + else + ec_cli ("error measuring roundtrip time"); + } + delta = ecm->test_end_time - ecm->test_start_time; if (delta == 0.0) { diff --git a/src/plugins/hs_apps/echo_client.h b/src/plugins/hs_apps/echo_client.h index d0eed6bd733..01b807a9466 100644 --- a/src/plugins/hs_apps/echo_client.h +++ b/src/plugins/hs_apps/echo_client.h @@ -22,6 +22,15 @@ #include #include +typedef struct ec_rttstat_ +{ + f64 min_rtt; + f64 max_rtt; + f64 sum_rtt; + u32 n_sum; + clib_spinlock_t w_lock; +} ec_rttstat_t; + typedef struct ec_session_ { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); @@ -38,6 +47,8 @@ typedef struct ec_session_ f64 time_to_send; u64 bytes_paced_target; u64 bytes_paced_current; + f64 send_rtt; + u8 rtt_stat; } ec_session_t; typedef struct ec_worker_ @@ -67,6 +78,7 @@ typedef struct f64 test_end_time; u32 prev_conns; u32 repeats; + ec_rttstat_t rtt_stats; f64 pacing_window_len; /**< Time between data chunk sends when limiting tput */ @@ -141,6 +153,12 @@ typedef enum ec_cli_signal_ EC_CLI_TEST_DONE } ec_cli_signal_t; +typedef enum ec_rtt_stat_ +{ + EC_UDP_RTT_TX_FLAG = 1, + EC_UDP_RTT_RX_FLAG = 2 +} ec_rtt_stat; + void ec_program_connects (void); #endif /* __included_echo_client_h__ */