X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fhs_apps%2Fecho_client.c;h=453cbed3859b65d8d7a32160c41489c4f7746601;hb=81c85142194fec2da6e9e2741cc88cfbcae61aa1;hp=935e016e1f740405fd6bc6d36ae7eba8040fc779;hpb=23c989642c1061e5ebbbe55b6fbde274e0914a90;p=vpp.git diff --git a/src/plugins/hs_apps/echo_client.c b/src/plugins/hs_apps/echo_client.c index 935e016e1f7..453cbed3859 100644 --- a/src/plugins/hs_apps/echo_client.c +++ b/src/plugins/hs_apps/echo_client.c @@ -19,27 +19,37 @@ static ec_main_t ec_main; -#define EC_DBG (0) -#define DBG(_fmt, _args...) \ - if (EC_DBG) \ - clib_warning (_fmt, ##_args) +#define ec_err(_fmt, _args...) clib_warning (_fmt, ##_args); + +#define ec_dbg(_fmt, _args...) \ + do \ + { \ + if (ec_main.cfg.verbose) \ + ec_err (_fmt, ##_args); \ + } \ + while (0) + +#define ec_cli(_fmt, _args...) vlib_cli_output (vm, _fmt, ##_args) static void -signal_evt_to_cli_i (int *code) +signal_evt_to_cli_i (void *codep) { ec_main_t *ecm = &ec_main; + int code; + ASSERT (vlib_get_thread_index () == 0); - vlib_process_signal_event (ecm->vlib_main, ecm->cli_node_index, *code, 0); + code = pointer_to_uword (codep); + vlib_process_signal_event (ecm->vlib_main, ecm->cli_node_index, code, 0); } static void signal_evt_to_cli (int code) { if (vlib_get_thread_index () != 0) - vl_api_rpc_call_main_thread (signal_evt_to_cli_i, (u8 *) & code, - sizeof (code)); + session_send_rpc_evt_to_thread_force ( + 0, signal_evt_to_cli_i, uword_to_pointer ((uword) code, void *)); else - signal_evt_to_cli_i (&code); + signal_evt_to_cli_i (uword_to_pointer ((uword) code, void *)); } static inline ec_worker_t * @@ -127,6 +137,7 @@ send_data_chunk (ec_main_t *ecm, ec_session_t *es) else { bytes_this_chunk = clib_min (bytes_this_chunk, max_enqueue); + bytes_this_chunk = clib_min (bytes_this_chunk, 1460); rv = app_send_dgram (&es->data, test_data + test_buf_offset, bytes_this_chunk, 0); } @@ -139,7 +150,7 @@ send_data_chunk (ec_main_t *ecm, ec_session_t *es) es->bytes_to_send -= rv; es->bytes_sent += rv; - if (EC_DBG) + if (ecm->cfg.verbose) { ELOG_TYPE_DECLARE (e) = { @@ -165,7 +176,7 @@ receive_data_chunk (ec_worker_t *wrk, ec_session_t *es) svm_fifo_t *rx_fifo = es->data.rx_fifo; int n_read, i; - if (ecm->test_bytes) + if (ecm->cfg.test_bytes) { if (!ecm->is_dgram) n_read = @@ -182,7 +193,7 @@ receive_data_chunk (ec_worker_t *wrk, ec_session_t *es) if (n_read > 0) { - if (EC_DBG) + if (ecm->cfg.verbose) { ELOG_TYPE_DECLARE (e) = { @@ -197,15 +208,15 @@ receive_data_chunk (ec_worker_t *wrk, ec_session_t *es) ed->data[0] = n_read; } - if (ecm->test_bytes) + if (ecm->cfg.test_bytes) { for (i = 0; i < n_read; i++) { if (wrk->rx_buf[i] != ((es->bytes_received + i) & 0xff)) { - clib_warning ("read %d error at byte %lld, 0x%x not 0x%x", - n_read, es->bytes_received + i, wrk->rx_buf[i], - ((es->bytes_received + i) & 0xff)); + ec_err ("read %d error at byte %lld, 0x%x not 0x%x", n_read, + es->bytes_received + i, wrk->rx_buf[i], + ((es->bytes_received + i) & 0xff)); ecm->test_failed = 1; } } @@ -261,7 +272,7 @@ ec_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) ecm->prev_conns = vec_len (conns_this_batch); if (ecm->repeats == 500000) { - clib_warning ("stuck clients"); + ec_err ("stuck clients"); } } else @@ -309,7 +320,7 @@ ec_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) } else { - clib_warning ("session AWOL?"); + ec_err ("session AWOL?"); vec_delete (conns_this_batch, 1, i); } @@ -336,16 +347,15 @@ VLIB_REGISTER_NODE (echo_clients_node) = { static void ec_reset_runtime_config (ec_main_t *ecm) { + hs_test_cfg_init (&ecm->cfg); ecm->n_clients = 1; ecm->quic_streams = 1; ecm->bytes_to_send = 8192; - ecm->no_return = 0; + ecm->echo_bytes = 0; ecm->fifo_size = 64 << 10; ecm->connections_per_batch = 1000; ecm->private_segment_count = 0; ecm->private_segment_size = 256 << 20; - ecm->no_output = 0; - ecm->test_bytes = 0; ecm->test_failed = 0; ecm->tls_engine = CRYPTO_ENGINE_OPENSSL; ecm->no_copy = 0; @@ -369,7 +379,6 @@ static int ec_init (vlib_main_t *vm) { ec_main_t *ecm = &ec_main; - vlib_thread_main_t *vtm = vlib_get_thread_main (); ec_worker_t *wrk; u32 num_threads; int i; @@ -410,8 +419,8 @@ ec_init (vlib_main_t *vm) for (i = 0; i < vec_len (ecm->connect_test_data); i++) ecm->connect_test_data[i] = i & 0xff; - num_threads = 1 /* main thread */ + vtm->n_threads; - vec_validate (ecm->wrk, num_threads); + num_threads = 1 /* main thread */ + vlib_num_workers (); + vec_validate (ecm->wrk, num_threads - 1); vec_foreach (wrk, ecm->wrk) { vec_validate (wrk->rx_buf, vec_len (ecm->connect_test_data) - 1); @@ -424,13 +433,14 @@ ec_init (vlib_main_t *vm) vlib_worker_thread_barrier_sync (vm); vnet_session_enable_disable (vm, 1 /* turn on session and transports */); - vlib_worker_thread_barrier_release (vm); /* Turn on the builtin client input nodes */ - for (i = 0; i < vtm->n_vlib_mains; i++) - vlib_node_set_state (vlib_get_main_by_index (i), echo_clients_node.index, + foreach_vlib_main () + vlib_node_set_state (this_vlib_main, echo_clients_node.index, VLIB_NODE_STATE_POLLING); + vlib_worker_thread_barrier_release (vm); + return 0; } @@ -476,39 +486,76 @@ quic_ec_qsession_connected_callback (u32 app_index, u32 api_context, { session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; ec_main_t *ecm = &ec_main; - vnet_connect_args_t *a = 0; - session_handle_t handle; + vnet_connect_args_t _a, *a = &_a; u32 stream_n; int rv; - DBG ("QUIC Connection handle %d", session_handle (s)); + ec_dbg ("QUIC Connection handle %d", session_handle (s)); - vec_validate (a, 1); a->uri = (char *) ecm->connect_uri; if (parse_uri (a->uri, &sep)) return -1; - sep.parent_handle = handle = session_handle (s); + sep.parent_handle = session_handle (s); for (stream_n = 0; stream_n < ecm->quic_streams; stream_n++) { clib_memset (a, 0, sizeof (*a)); a->app_index = ecm->app_index; - a->api_context = -1 - api_context; + a->api_context = -2 - api_context; clib_memcpy (&a->sep_ext, &sep, sizeof (sep)); - DBG ("QUIC opening stream %d", stream_n); + ec_dbg ("QUIC opening stream %d", stream_n); if ((rv = vnet_connect (a))) { clib_error ("Stream session %d opening failed: %d", stream_n, rv); return -1; } - DBG ("QUIC stream %d connected", stream_n); + ec_dbg ("QUIC stream %d connected", stream_n); } - /* - * 's' is no longer valid, its underlying pool could have been moved in - * vnet_connect() - */ - vec_free (a); + return 0; +} + +static int +ec_ctrl_send (hs_test_cmd_t cmd) +{ + ec_main_t *ecm = &ec_main; + session_t *s; + int rv; + + ecm->cfg.cmd = cmd; + if (ecm->ctrl_session_handle == SESSION_INVALID_HANDLE) + { + ec_dbg ("ctrl session went away"); + return -1; + } + + s = session_get_from_handle_if_valid (ecm->ctrl_session_handle); + if (!s) + { + ec_err ("ctrl session not found"); + return -1; + } + + ec_dbg ("sending test paramters to the server.."); + if (ecm->cfg.verbose) + hs_test_cfg_dump (&ecm->cfg, 1); + + rv = svm_fifo_enqueue (s->tx_fifo, sizeof (ecm->cfg), (u8 *) &ecm->cfg); + ASSERT (rv == sizeof (ecm->cfg)); + session_send_io_evt_to_thread (s->tx_fifo, SESSION_IO_EVT_TX); + return 0; +} + +static int +ec_ctrl_session_connected_callback (session_t *s) +{ + ec_main_t *ecm = &ec_main; + + s->opaque = HS_CTRL_HANDLE; + ecm->ctrl_session_handle = session_handle (s); + + /* send test parameters to the server */ + ec_ctrl_send (HS_TEST_CMD_SYNC); return 0; } @@ -521,12 +568,15 @@ quic_ec_session_connected_callback (u32 app_index, u32 api_context, ec_worker_t *wrk; u32 thread_index; + if (PREDICT_FALSE (api_context == HS_CTRL_HANDLE)) + return ec_ctrl_session_connected_callback (s); + if (PREDICT_FALSE (ecm->run_test != EC_STARTING)) return -1; if (err) { - clib_warning ("connection %d failed!", api_context); + ec_err ("connection %d failed!", api_context); ecm->run_test = EC_EXITING; signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); return 0; @@ -535,7 +585,7 @@ quic_ec_session_connected_callback (u32 app_index, u32 api_context, if (s->listener_handle == SESSION_INVALID_HANDLE) return quic_ec_qsession_connected_callback (app_index, api_context, s, err); - DBG ("STREAM Connection callback %d", api_context); + ec_dbg ("STREAM Connection callback %d", api_context); thread_index = s->thread_index; ASSERT (thread_index == vlib_get_thread_index () @@ -549,7 +599,7 @@ quic_ec_session_connected_callback (u32 app_index, u32 api_context, es = ec_session_alloc (wrk); es->bytes_to_send = ecm->bytes_to_send; - es->bytes_to_receive = ecm->no_return ? 0ULL : ecm->bytes_to_send; + es->bytes_to_receive = ecm->echo_bytes ? ecm->bytes_to_send : 0ULL; es->data.rx_fifo = s->rx_fifo; es->data.rx_fifo->shr->client_session_index = es->data.session_index; es->data.tx_fifo = s->tx_fifo; @@ -593,7 +643,8 @@ ec_session_connected_callback (u32 app_index, u32 api_context, session_t *s, if (err) { - clib_warning ("connection %d failed!", api_context); + ec_err ("connection %d failed! %U", api_context, format_session_error, + err); ecm->run_test = EC_EXITING; signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); return 0; @@ -603,6 +654,9 @@ ec_session_connected_callback (u32 app_index, u32 api_context, session_t *s, ASSERT (thread_index == vlib_get_thread_index () || session_transport_service_type (s) == TRANSPORT_SERVICE_CL); + if (PREDICT_FALSE (api_context == HS_CTRL_HANDLE)) + return ec_ctrl_session_connected_callback (s); + wrk = ec_worker_get (thread_index); /* @@ -611,7 +665,7 @@ ec_session_connected_callback (u32 app_index, u32 api_context, session_t *s, es = ec_session_alloc (wrk); es->bytes_to_send = ecm->bytes_to_send; - es->bytes_to_receive = ecm->no_return ? 0ULL : ecm->bytes_to_send; + es->bytes_to_receive = ecm->echo_bytes ? ecm->bytes_to_send : 0ULL; es->data.rx_fifo = s->rx_fifo; es->data.rx_fifo->shr->client_session_index = es->data.session_index; es->data.tx_fifo = s->tx_fifo; @@ -648,7 +702,7 @@ ec_session_reset_callback (session_t *s) vnet_disconnect_args_t _a = { 0 }, *a = &_a; if (s->session_state == SESSION_STATE_READY) - clib_warning ("Reset active connection %U", format_session, s, 2); + ec_err ("Reset active connection %U", format_session, s, 2); a->handle = session_handle (s); a->app_index = ecm->app_index; @@ -667,6 +721,13 @@ ec_session_disconnect_callback (session_t *s) { ec_main_t *ecm = &ec_main; vnet_disconnect_args_t _a = { 0 }, *a = &_a; + + if (session_handle (s) == ecm->ctrl_session_handle) + { + ec_dbg ("ctrl session disconnect"); + ecm->ctrl_session_handle = SESSION_INVALID_HANDLE; + } + a->handle = session_handle (s); a->app_index = ecm->app_index; vnet_disconnect_session (a); @@ -683,6 +744,68 @@ ec_session_disconnect (session_t *s) vnet_disconnect_session (a); } +static int +ec_ctrl_session_rx_callback (session_t *s) +{ + ec_main_t *ecm = &ec_main; + int rx_bytes; + hs_test_cfg_t cfg = { 0 }; + + rx_bytes = svm_fifo_dequeue (s->rx_fifo, sizeof (cfg), (u8 *) &cfg); + if (rx_bytes != sizeof (cfg)) + { + ec_err ("invalid cfg length %d (expected %d)", rx_bytes, sizeof (cfg)); + signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); + return -1; + } + + ec_dbg ("control message received:"); + if (ecm->cfg.verbose) + hs_test_cfg_dump (&cfg, 1); + + switch (cfg.cmd) + { + case HS_TEST_CMD_SYNC: + switch (ecm->run_test) + { + case EC_STARTING: + if (!hs_test_cfg_verify (&cfg, &ecm->cfg)) + { + ec_err ("invalid config received from server!"); + signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); + return -1; + } + signal_evt_to_cli (EC_CLI_CFG_SYNC); + break; + + case EC_RUNNING: + ec_dbg ("test running.."); + break; + + case EC_EXITING: + /* post test sync */ + signal_evt_to_cli (EC_CLI_CFG_SYNC); + break; + + default: + ec_err ("unexpected test state! %d", ecm->run_test); + break; + } + break; + case HS_TEST_CMD_START: + signal_evt_to_cli (EC_CLI_START); + break; + case HS_TEST_CMD_STOP: + signal_evt_to_cli (EC_CLI_STOP); + break; + default: + ec_err ("unexpected cmd! %d", cfg.cmd); + break; + } + + return 0; +} + static int ec_session_rx_callback (session_t *s) { @@ -690,6 +813,9 @@ ec_session_rx_callback (session_t *s) ec_worker_t *wrk; ec_session_t *es; + if (PREDICT_FALSE (s->opaque == HS_CTRL_HANDLE)) + return ec_ctrl_session_rx_callback (s); + if (PREDICT_FALSE (ecm->run_test != EC_RUNNING)) { ec_session_disconnect (s); @@ -702,10 +828,8 @@ ec_session_rx_callback (session_t *s) receive_data_chunk (wrk, es); if (svm_fifo_max_dequeue_cons (s->rx_fifo)) - { - if (svm_fifo_set_event (s->rx_fifo)) - session_send_io_evt_to_thread (s->rx_fifo, SESSION_IO_EVT_BUILTIN_RX); - } + session_enqueue_notify (s); + return 0; } @@ -822,19 +946,17 @@ ec_connect_rpc (void *args) { ec_main_t *ecm = &ec_main; vnet_connect_args_t _a = {}, *a = &_a; - vlib_main_t *vm = vlib_get_main (); int rv, needs_crypto; u32 n_clients, ci; n_clients = ecm->n_clients; needs_crypto = ec_transport_needs_crypto (ecm->transport_proto); clib_memcpy (&a->sep_ext, &ecm->connect_sep, sizeof (ecm->connect_sep)); + a->sep_ext.transport_flags |= TRANSPORT_CFG_F_CONNECTED; a->app_index = ecm->app_index; ci = ecm->connect_conn_index; - vlib_worker_thread_barrier_sync (vm); - while (ci < n_clients) { /* Crude pacing for call setups */ @@ -859,7 +981,7 @@ ec_connect_rpc (void *args) if (rv) { - clib_warning ("connect returned: %U", format_session_error, rv); + ec_err ("connect returned: %U", format_session_error, rv); ecm->run_test = EC_EXITING; signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); break; @@ -868,8 +990,6 @@ ec_connect_rpc (void *args) ci += 1; } - vlib_worker_thread_barrier_release (vm); - if (ci < ecm->expected_connections && ecm->run_test != EC_EXITING) ec_program_connects (); @@ -879,12 +999,93 @@ ec_connect_rpc (void *args) void ec_program_connects (void) { - session_send_rpc_evt_to_thread_force (0, ec_connect_rpc, 0); + session_send_rpc_evt_to_thread_force (transport_cl_thread (), ec_connect_rpc, + 0); +} + +static clib_error_t * +ec_ctrl_connect_rpc () +{ + session_error_t rv; + ec_main_t *ecm = &ec_main; + vnet_connect_args_t _a = {}, *a = &_a; + + a->api_context = HS_CTRL_HANDLE; + ecm->cfg.cmd = HS_TEST_CMD_SYNC; + clib_memcpy (&a->sep_ext, &ecm->connect_sep, sizeof (ecm->connect_sep)); + a->sep_ext.transport_proto = TRANSPORT_PROTO_TCP; + a->app_index = ecm->app_index; + + rv = vnet_connect (a); + if (rv) + { + ec_err ("ctrl connect returned: %U", format_session_error, rv); + ecm->run_test = EC_EXITING; + signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); + } + return 0; +} + +static void +ec_ctrl_connect (void) +{ + session_send_rpc_evt_to_thread_force (transport_cl_thread (), + ec_ctrl_connect_rpc, 0); +} + +static void +ec_ctrl_session_disconnect () +{ + ec_main_t *ecm = &ec_main; + vnet_disconnect_args_t _a, *a = &_a; + session_error_t err; + + a->handle = ecm->ctrl_session_handle; + a->app_index = ecm->app_index; + err = vnet_disconnect_session (a); + if (err) + ec_err ("vnet_disconnect_session: %U", format_session_error, err); +} + +static int +ec_ctrl_test_sync () +{ + ec_main_t *ecm = &ec_main; + ecm->cfg.test = HS_TEST_TYPE_ECHO; + return ec_ctrl_send (HS_TEST_CMD_SYNC); +} + +static int +ec_ctrl_test_start () +{ + return ec_ctrl_send (HS_TEST_CMD_START); +} + +static int +ec_ctrl_test_stop () +{ + return ec_ctrl_send (HS_TEST_CMD_STOP); } -#define ec_cli(_fmt, _args...) \ - if (!ecm->no_output) \ - vlib_cli_output (vm, _fmt, ##_args) +#define ec_wait_for_signal(_sig) \ + vlib_process_wait_for_event_or_clock (vm, ecm->syn_timeout); \ + event_type = vlib_process_get_events (vm, &event_data); \ + switch (event_type) \ + { \ + case ~0: \ + ec_cli ("Timeout while waiting for " #_sig); \ + error = \ + clib_error_return (0, "failed: timeout while waiting for " #_sig); \ + goto cleanup; \ + case _sig: \ + break; \ + default: \ + ec_cli ("unexpected event while waiting for " #_sig ": %d", \ + event_type); \ + error = \ + clib_error_return (0, "failed: unexpected event: %d", event_type); \ + goto cleanup; \ + } static clib_error_t * ec_command_fn (vlib_main_t *vm, unformat_input_t *input, @@ -933,10 +1134,11 @@ ec_command_fn (vlib_main_t *vm, unformat_input_t *input, ; else if (unformat (line_input, "syn-timeout %f", &ecm->syn_timeout)) ; - else if (unformat (line_input, "no-return")) - ecm->no_return = 1; - else if (unformat (line_input, "fifo-size %d", &ecm->fifo_size)) - ecm->fifo_size <<= 10; + else if (unformat (line_input, "echo-bytes")) + ecm->echo_bytes = 1; + else if (unformat (line_input, "fifo-size %U", unformat_memory_size, + &ecm->fifo_size)) + ; else if (unformat (line_input, "private-segment-count %d", &ecm->private_segment_count)) ; @@ -961,10 +1163,10 @@ ec_command_fn (vlib_main_t *vm, unformat_input_t *input, ecm->attach_flags = APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; else if (unformat (line_input, "secret %lu", &ecm->appns_secret)) ; - else if (unformat (line_input, "no-output")) - ecm->no_output = 1; + else if (unformat (line_input, "verbose")) + ecm->cfg.verbose = 1; else if (unformat (line_input, "test-bytes")) - ecm->test_bytes = 1; + ecm->cfg.test_bytes = 1; else if (unformat (line_input, "tls-engine %d", &ecm->tls_engine)) ; else @@ -977,11 +1179,12 @@ ec_command_fn (vlib_main_t *vm, unformat_input_t *input, parse_config: - ecm->expected_connections = ecm->n_clients * ecm->quic_streams; + ecm->cfg.num_test_sessions = ecm->expected_connections = + ecm->n_clients * ecm->quic_streams; if (!ecm->connect_uri) { - clib_warning ("No uri provided. Using default: %s", default_uri); + ec_cli ("No uri provided. Using default: %s", default_uri); ecm->connect_uri = format (0, "%s%c", default_uri, 0); } @@ -1002,10 +1205,28 @@ parse_config: goto cleanup; } + if (ecm->echo_bytes) + ecm->cfg.test = HS_TEST_TYPE_BI; + else + ecm->cfg.test = HS_TEST_TYPE_UNI; + + ec_ctrl_connect (); + ec_wait_for_signal (EC_CLI_CFG_SYNC); + + if (ec_ctrl_test_start () < 0) + { + ec_cli ("failed to send start command"); + goto cleanup; + } + ec_wait_for_signal (EC_CLI_START); + /* * Start. Fire off connect requests */ + /* update data port */ + ecm->connect_sep.port = hs_make_data_port (ecm->connect_sep.port); + ecm->syn_start_time = vlib_time_now (vm); ec_program_connects (); @@ -1022,7 +1243,7 @@ parse_config: ecm->ready_connections); error = clib_error_return (0, "failed: syn timeout with %d sessions", ecm->ready_connections); - goto cleanup; + goto stop_test; case EC_CLI_CONNECTS_DONE: delta = vlib_time_now (vm) - ecm->syn_start_time; @@ -1033,13 +1254,13 @@ parse_config: case EC_CLI_CONNECTS_FAILED: error = clib_error_return (0, "failed: connect returned"); - goto cleanup; + goto stop_test; default: - ec_cli ("unexpected event(1): %d", event_type); - error = clib_error_return (0, "failed: unexpected event(1): %d", - event_type); - goto cleanup; + ec_cli ("unexpected event(2): %d", event_type); + error = + clib_error_return (0, "failed: unexpected event(2): %d", event_type); + goto stop_test; } /* @@ -1052,11 +1273,11 @@ parse_config: switch (event_type) { case ~0: - ec_cli ("Timeout with %d sessions still active...", - ecm->ready_connections); + ec_cli ("Timeout at %.6f with %d sessions still active...", + vlib_time_now (ecm->vlib_main), ecm->ready_connections); error = clib_error_return (0, "failed: timeout with %d sessions", ecm->ready_connections); - goto cleanup; + goto stop_test; case EC_CLI_TEST_DONE: ecm->test_end_time = vlib_time_now (vm); @@ -1064,10 +1285,10 @@ parse_config: break; default: - ec_cli ("unexpected event(2): %d", event_type); - error = clib_error_return (0, "failed: unexpected event(2): %d", - event_type); - goto cleanup; + ec_cli ("unexpected event(3): %d", event_type); + error = + clib_error_return (0, "failed: unexpected event(3): %d", event_type); + goto stop_test; } /* @@ -1078,11 +1299,11 @@ parse_config: { ec_cli ("zero delta-t?"); error = clib_error_return (0, "failed: zero delta-t"); - goto cleanup; + goto stop_test; } - total_bytes = (ecm->no_return ? ecm->tx_total : ecm->rx_total); - transfer_type = ecm->no_return ? "half-duplex" : "full-duplex"; + total_bytes = (ecm->echo_bytes ? ecm->rx_total : ecm->tx_total); + transfer_type = ecm->echo_bytes ? "full-duplex" : "half-duplex"; ec_cli ("%lld bytes (%lld mbytes, %lld gbytes) in %.2f seconds", total_bytes, total_bytes / (1ULL << 20), total_bytes / (1ULL << 30), delta); ec_cli ("%.2f bytes/second %s", ((f64) total_bytes) / (delta), @@ -1090,14 +1311,33 @@ parse_config: ec_cli ("%.4f gbit/second %s", (((f64) total_bytes * 8.0) / delta / 1e9), transfer_type); - if (ecm->test_bytes && ecm->test_failed) + if (ecm->cfg.test_bytes && ecm->test_failed) error = clib_error_return (0, "failed: test bytes"); +stop_test: + ecm->run_test = EC_EXITING; + + /* send stop test command to the server */ + if (ec_ctrl_test_stop () < 0) + { + ec_cli ("failed to send stop command"); + goto cleanup; + } + ec_wait_for_signal (EC_CLI_STOP); + + /* post test sync */ + if (ec_ctrl_test_sync () < 0) + { + ec_cli ("failed to send post sync command"); + goto cleanup; + } + ec_wait_for_signal (EC_CLI_CFG_SYNC); + + /* disconnect control session */ + ec_ctrl_session_disconnect (); + cleanup: - /* - * Cleanup - */ ecm->run_test = EC_EXITING; vlib_process_wait_for_event_or_clock (vm, 10e-3); @@ -1122,10 +1362,10 @@ VLIB_CLI_COMMAND (ec_command, static) = { .path = "test echo clients", .short_help = "test echo clients [nclients %d][[m|g]bytes ]" - "[test-timeout