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=551e46b987e8e3352255a754070ea6cbe76f0654;hpb=2de9c0f92bb486072d8371a24d9b23fd85e1aa80;p=vpp.git diff --git a/src/plugins/hs_apps/echo_client.c b/src/plugins/hs_apps/echo_client.c index 551e46b987e..453cbed3859 100644 --- a/src/plugins/hs_apps/echo_client.c +++ b/src/plugins/hs_apps/echo_client.c @@ -15,38 +15,69 @@ * limitations under the License. */ -#include -#include -#include #include -echo_client_main_t echo_client_main; +static ec_main_t ec_main; -#define ECHO_CLIENT_DBG (0) -#define DBG(_fmt, _args...) \ - if (ECHO_CLIENT_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) { - echo_client_main_t *ecm = &echo_client_main; + 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 * +ec_worker_get (u32 thread_index) +{ + return vec_elt_at_index (ec_main.wrk, thread_index); +} + +static inline ec_session_t * +ec_session_alloc (ec_worker_t *wrk) +{ + ec_session_t *ecs; + + pool_get_zero (wrk->sessions, ecs); + ecs->data.session_index = ecs - wrk->sessions; + ecs->thread_index = wrk->thread_index; + + return ecs; +} + +static inline ec_session_t * +ec_session_get (ec_worker_t *wrk, u32 ec_index) +{ + return pool_elt_at_index (wrk->sessions, ec_index); } static void -send_data_chunk (echo_client_main_t * ecm, eclient_session_t * s) +send_data_chunk (ec_main_t *ecm, ec_session_t *es) { u8 *test_data = ecm->connect_test_data; int test_buf_len, test_buf_offset, rv; @@ -54,38 +85,39 @@ send_data_chunk (echo_client_main_t * ecm, eclient_session_t * s) test_buf_len = vec_len (test_data); ASSERT (test_buf_len > 0); - test_buf_offset = s->bytes_sent % test_buf_len; - bytes_this_chunk = clib_min (test_buf_len - test_buf_offset, - s->bytes_to_send); + test_buf_offset = es->bytes_sent % test_buf_len; + bytes_this_chunk = + clib_min (test_buf_len - test_buf_offset, es->bytes_to_send); if (!ecm->is_dgram) { if (ecm->no_copy) { - svm_fifo_t *f = s->data.tx_fifo; + svm_fifo_t *f = es->data.tx_fifo; rv = clib_min (svm_fifo_max_enqueue_prod (f), bytes_this_chunk); svm_fifo_enqueue_nocopy (f, rv); - session_send_io_evt_to_thread_custom (&f->master_session_index, - s->thread_index, - SESSION_IO_EVT_TX); + session_send_io_evt_to_thread_custom ( + &es->vpp_session_index, es->thread_index, SESSION_IO_EVT_TX); } else - rv = app_send_stream (&s->data, test_data + test_buf_offset, + rv = app_send_stream (&es->data, test_data + test_buf_offset, bytes_this_chunk, 0); } else { + svm_fifo_t *f = es->data.tx_fifo; + u32 max_enqueue = svm_fifo_max_enqueue_prod (f); + + if (max_enqueue < sizeof (session_dgram_hdr_t)) + return; + + max_enqueue -= sizeof (session_dgram_hdr_t); + if (ecm->no_copy) { session_dgram_hdr_t hdr; - svm_fifo_t *f = s->data.tx_fifo; - app_session_transport_t *at = &s->data.transport; - u32 max_enqueue = svm_fifo_max_enqueue_prod (f); - - if (max_enqueue <= sizeof (session_dgram_hdr_t)) - return; + app_session_transport_t *at = &es->data.transport; - max_enqueue -= sizeof (session_dgram_hdr_t); rv = clib_min (max_enqueue, bytes_this_chunk); hdr.data_length = rv; @@ -99,58 +131,59 @@ send_data_chunk (echo_client_main_t * ecm, eclient_session_t * s) hdr.lcl_port = at->lcl_port; svm_fifo_enqueue (f, sizeof (hdr), (u8 *) & hdr); svm_fifo_enqueue_nocopy (f, rv); - session_send_io_evt_to_thread_custom (&f->master_session_index, - s->thread_index, - SESSION_IO_EVT_TX); + session_send_io_evt_to_thread_custom ( + &es->vpp_session_index, es->thread_index, SESSION_IO_EVT_TX); } else - rv = app_send_dgram (&s->data, test_data + test_buf_offset, - bytes_this_chunk, 0); + { + 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); + } } /* If we managed to enqueue data... */ if (rv > 0) { /* Account for it... */ - s->bytes_to_send -= rv; - s->bytes_sent += rv; + es->bytes_to_send -= rv; + es->bytes_sent += rv; - if (ECHO_CLIENT_DBG) + if (ecm->cfg.verbose) { - /* *INDENT-OFF* */ ELOG_TYPE_DECLARE (e) = { .format = "tx-enq: xfer %d bytes, sent %u remain %u", .format_args = "i4i4i4", }; - /* *INDENT-ON* */ struct { u32 data[3]; } *ed; ed = ELOG_DATA (&vlib_global_main.elog_main, e); ed->data[0] = rv; - ed->data[1] = s->bytes_sent; - ed->data[2] = s->bytes_to_send; + ed->data[1] = es->bytes_sent; + ed->data[2] = es->bytes_to_send; } } } static void -receive_data_chunk (echo_client_main_t * ecm, eclient_session_t * s) +receive_data_chunk (ec_worker_t *wrk, ec_session_t *es) { - svm_fifo_t *rx_fifo = s->data.rx_fifo; - u32 thread_index = vlib_get_thread_index (); + ec_main_t *ecm = &ec_main; + 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 = app_recv_stream (&s->data, ecm->rx_buf[thread_index], - vec_len (ecm->rx_buf[thread_index])); + n_read = + app_recv_stream (&es->data, wrk->rx_buf, vec_len (wrk->rx_buf)); else - n_read = app_recv_dgram (&s->data, ecm->rx_buf[thread_index], - vec_len (ecm->rx_buf[thread_index])); + n_read = + app_recv_dgram (&es->data, wrk->rx_buf, vec_len (wrk->rx_buf)); } else { @@ -160,15 +193,13 @@ receive_data_chunk (echo_client_main_t * ecm, eclient_session_t * s) if (n_read > 0) { - if (ECHO_CLIENT_DBG) + if (ecm->cfg.verbose) { - /* *INDENT-OFF* */ ELOG_TYPE_DECLARE (e) = { .format = "rx-deq: %d bytes", .format_args = "i4", }; - /* *INDENT-ON* */ struct { u32 data[1]; @@ -177,102 +208,104 @@ receive_data_chunk (echo_client_main_t * ecm, eclient_session_t * s) ed->data[0] = n_read; } - if (ecm->test_bytes) + if (ecm->cfg.test_bytes) { for (i = 0; i < n_read; i++) { - if (ecm->rx_buf[thread_index][i] - != ((s->bytes_received + i) & 0xff)) + 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, s->bytes_received + i, - ecm->rx_buf[thread_index][i], - ((s->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; } } } - ASSERT (n_read <= s->bytes_to_receive); - s->bytes_to_receive -= n_read; - s->bytes_received += n_read; + ASSERT (n_read <= es->bytes_to_receive); + es->bytes_to_receive -= n_read; + es->bytes_received += n_read; } } static uword -echo_client_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) +ec_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - echo_client_main_t *ecm = &echo_client_main; - int my_thread_index = vlib_get_thread_index (); - eclient_session_t *sp; - int i; - int delete_session; - u32 *connection_indices; - u32 *connections_this_batch; - u32 nconnections_this_batch; - - connection_indices = ecm->connection_index_by_thread[my_thread_index]; - connections_this_batch = - ecm->connections_this_batch_by_thread[my_thread_index]; - - if ((ecm->run_test != ECHO_CLIENTS_RUNNING) || - ((vec_len (connection_indices) == 0) - && vec_len (connections_this_batch) == 0)) + u32 *conn_indices, *conns_this_batch, nconns_this_batch; + int thread_index = vm->thread_index, i, delete_session; + ec_main_t *ecm = &ec_main; + ec_worker_t *wrk; + ec_session_t *es; + session_t *s; + + if (ecm->run_test != EC_RUNNING) + return 0; + + wrk = ec_worker_get (thread_index); + conn_indices = wrk->conn_indices; + conns_this_batch = wrk->conns_this_batch; + + if (((vec_len (conn_indices) == 0) && vec_len (conns_this_batch) == 0)) return 0; /* Grab another pile of connections */ - if (PREDICT_FALSE (vec_len (connections_this_batch) == 0)) + if (PREDICT_FALSE (vec_len (conns_this_batch) == 0)) { - nconnections_this_batch = - clib_min (ecm->connections_per_batch, vec_len (connection_indices)); - - ASSERT (nconnections_this_batch > 0); - vec_validate (connections_this_batch, nconnections_this_batch - 1); - clib_memcpy_fast (connections_this_batch, - connection_indices + vec_len (connection_indices) - - nconnections_this_batch, - nconnections_this_batch * sizeof (u32)); - _vec_len (connection_indices) -= nconnections_this_batch; + nconns_this_batch = + clib_min (ecm->connections_per_batch, vec_len (conn_indices)); + + ASSERT (nconns_this_batch > 0); + vec_validate (conns_this_batch, nconns_this_batch - 1); + clib_memcpy_fast (conns_this_batch, + conn_indices + vec_len (conn_indices) - + nconns_this_batch, + nconns_this_batch * sizeof (u32)); + vec_dec_len (conn_indices, nconns_this_batch); } - if (PREDICT_FALSE (ecm->prev_conns != ecm->connections_per_batch - && ecm->prev_conns == vec_len (connections_this_batch))) + /* + * Track progress + */ + if (PREDICT_FALSE (ecm->prev_conns != ecm->connections_per_batch && + ecm->prev_conns == vec_len (conns_this_batch))) { ecm->repeats++; - ecm->prev_conns = vec_len (connections_this_batch); + ecm->prev_conns = vec_len (conns_this_batch); if (ecm->repeats == 500000) { - clib_warning ("stuck clients"); + ec_err ("stuck clients"); } } else { - ecm->prev_conns = vec_len (connections_this_batch); + ecm->prev_conns = vec_len (conns_this_batch); ecm->repeats = 0; } - for (i = 0; i < vec_len (connections_this_batch); i++) + /* + * Handle connections in this batch + */ + for (i = 0; i < vec_len (conns_this_batch); i++) { - delete_session = 1; + es = ec_session_get (wrk, conns_this_batch[i]); - sp = pool_elt_at_index (ecm->sessions, connections_this_batch[i]); + delete_session = 1; - if (sp->bytes_to_send > 0) + if (es->bytes_to_send > 0) { - send_data_chunk (ecm, sp); + send_data_chunk (ecm, es); delete_session = 0; } - if (sp->bytes_to_receive > 0) + + if (es->bytes_to_receive > 0) { delete_session = 0; } + if (PREDICT_FALSE (delete_session == 1)) { - session_t *s; - - clib_atomic_fetch_add (&ecm->tx_total, sp->bytes_sent); - clib_atomic_fetch_add (&ecm->rx_total, sp->bytes_received); - s = session_get_from_handle_if_valid (sp->vpp_session_handle); + clib_atomic_fetch_add (&ecm->tx_total, es->bytes_sent); + clib_atomic_fetch_add (&ecm->rx_total, es->bytes_received); + s = session_get_from_handle_if_valid (es->vpp_session_handle); if (s) { @@ -281,219 +314,339 @@ echo_client_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, a->app_index = ecm->app_index; vnet_disconnect_session (a); - vec_delete (connections_this_batch, 1, i); + vec_delete (conns_this_batch, 1, i); i--; clib_atomic_fetch_add (&ecm->ready_connections, -1); } else { - clib_warning ("session AWOL?"); - vec_delete (connections_this_batch, 1, i); + ec_err ("session AWOL?"); + vec_delete (conns_this_batch, 1, i); } /* Kick the debug CLI process */ if (ecm->ready_connections == 0) { - signal_evt_to_cli (2); + signal_evt_to_cli (EC_CLI_TEST_DONE); } } } - ecm->connection_index_by_thread[my_thread_index] = connection_indices; - ecm->connections_this_batch_by_thread[my_thread_index] = - connections_this_batch; + wrk->conn_indices = conn_indices; + wrk->conns_this_batch = conns_this_batch; return 0; } -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (echo_clients_node) = -{ - .function = echo_client_node_fn, +VLIB_REGISTER_NODE (echo_clients_node) = { + .function = ec_node_fn, .name = "echo-clients", .type = VLIB_NODE_TYPE_INPUT, .state = VLIB_NODE_STATE_DISABLED, }; -/* *INDENT-ON* */ -static int -create_api_loopback (echo_client_main_t * ecm) +static void +ec_reset_runtime_config (ec_main_t *ecm) { - api_main_t *am = vlibapi_get_main (); - vl_shmem_hdr_t *shmem_hdr; - - shmem_hdr = am->shmem_hdr; - ecm->vl_input_queue = shmem_hdr->vl_input_queue; - ecm->my_client_index = vl_api_memclnt_create_internal ("echo_client", - ecm->vl_input_queue); - return 0; + hs_test_cfg_init (&ecm->cfg); + ecm->n_clients = 1; + ecm->quic_streams = 1; + ecm->bytes_to_send = 8192; + 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->test_failed = 0; + ecm->tls_engine = CRYPTO_ENGINE_OPENSSL; + ecm->no_copy = 0; + ecm->run_test = EC_STARTING; + ecm->ready_connections = 0; + ecm->connect_conn_index = 0; + ecm->rx_total = 0; + ecm->tx_total = 0; + ecm->barrier_acq_needed = 0; + ecm->prealloc_sessions = 0; + ecm->prealloc_fifos = 0; + ecm->appns_id = 0; + ecm->appns_secret = 0; + ecm->attach_flags = 0; + ecm->syn_timeout = 20.0; + ecm->test_timeout = 20.0; + vec_free (ecm->connect_uri); } static int -echo_clients_init (vlib_main_t * vm) +ec_init (vlib_main_t *vm) { - echo_client_main_t *ecm = &echo_client_main; - vlib_thread_main_t *vtm = vlib_get_thread_main (); + ec_main_t *ecm = &ec_main; + ec_worker_t *wrk; u32 num_threads; int i; - if (create_api_loopback (ecm)) - return -1; + ec_reset_runtime_config (ecm); + + /* Store cli process node index for signaling */ + ecm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index; + ecm->vlib_main = vm; + + if (vlib_num_workers ()) + { + /* The request came over the binary api and the inband cli handler + * is not mp_safe. Drop the barrier to make sure the workers are not + * blocked. + */ + if (vlib_thread_is_main_w_barrier ()) + { + ecm->barrier_acq_needed = 1; + vlib_worker_thread_barrier_release (vm); + } + /* + * There's a good chance that both the client and the server echo + * apps will be enabled so make sure the session queue node polls on + * the main thread as connections will probably be established on it. + */ + vlib_node_set_state (vm, session_queue_node.index, + VLIB_NODE_STATE_POLLING); + } + + /* App init done only once */ + if (ecm->app_is_init) + return 0; - num_threads = 1 /* main thread */ + vtm->n_threads; /* Init test data. Big buffer */ vec_validate (ecm->connect_test_data, 4 * 1024 * 1024 - 1); for (i = 0; i < vec_len (ecm->connect_test_data); i++) ecm->connect_test_data[i] = i & 0xff; - vec_validate (ecm->rx_buf, num_threads - 1); - for (i = 0; i < num_threads; i++) - vec_validate (ecm->rx_buf[i], vec_len (ecm->connect_test_data) - 1); + 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); + wrk->thread_index = wrk - ecm->wrk; + wrk->vpp_event_queue = + session_main_get_vpp_event_queue (wrk->thread_index); + } + + ecm->app_is_init = 1; - ecm->is_init = 1; + vlib_worker_thread_barrier_sync (vm); + vnet_session_enable_disable (vm, 1 /* turn on session and transports */); - vec_validate (ecm->connection_index_by_thread, vtm->n_vlib_mains); - vec_validate (ecm->connections_this_batch_by_thread, vtm->n_vlib_mains); - vec_validate (ecm->quic_session_index_by_thread, vtm->n_vlib_mains); - vec_validate (ecm->vpp_event_queue, vtm->n_vlib_mains); + /* Turn on the builtin client input nodes */ + 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; } +static void +ec_prealloc_sessions (ec_main_t *ecm) +{ + u32 sessions_per_wrk, n_wrks; + ec_worker_t *wrk; + + n_wrks = vlib_num_workers () ? vlib_num_workers () : 1; + + sessions_per_wrk = ecm->n_clients / n_wrks; + vec_foreach (wrk, ecm->wrk) + pool_init_fixed (wrk->sessions, 1.1 * sessions_per_wrk); +} + +static void +ec_worker_cleanup (ec_worker_t *wrk) +{ + pool_free (wrk->sessions); + vec_free (wrk->conn_indices); + vec_free (wrk->conns_this_batch); +} + +static void +ec_cleanup (ec_main_t *ecm) +{ + ec_worker_t *wrk; + + vec_foreach (wrk, ecm->wrk) + ec_worker_cleanup (wrk); + + vec_free (ecm->connect_uri); + vec_free (ecm->appns_id); + + if (ecm->barrier_acq_needed) + vlib_worker_thread_barrier_sync (ecm->vlib_main); +} + static int -quic_echo_clients_qsession_connected_callback (u32 app_index, u32 api_context, - session_t * s, u8 is_fail) +quic_ec_qsession_connected_callback (u32 app_index, u32 api_context, + session_t *s, session_error_t err) { - echo_client_main_t *ecm = &echo_client_main; - vnet_connect_args_t *a = 0; - int rv; - u8 thread_index = vlib_get_thread_index (); session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; + ec_main_t *ecm = &ec_main; + vnet_connect_args_t _a, *a = &_a; u32 stream_n; - session_handle_t handle; + 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_add1 (ecm->quic_session_index_by_thread[thread_index], handle); - vec_free (a); return 0; } static int -quic_echo_clients_session_connected_callback (u32 app_index, u32 api_context, - session_t * s, u8 is_fail) +ec_ctrl_send (hs_test_cmd_t cmd) { - echo_client_main_t *ecm = &echo_client_main; - eclient_session_t *session; - u32 session_index; - u8 thread_index; + ec_main_t *ecm = &ec_main; + session_t *s; + int rv; - if (PREDICT_FALSE (ecm->run_test != ECHO_CLIENTS_STARTING)) + 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; +} + +static int +quic_ec_session_connected_callback (u32 app_index, u32 api_context, + session_t *s, session_error_t err) +{ + ec_main_t *ecm = &ec_main; + ec_session_t *es; + 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 (is_fail) + if (err) { - clib_warning ("connection %d failed!", api_context); - ecm->run_test = ECHO_CLIENTS_EXITING; - signal_evt_to_cli (-1); + ec_err ("connection %d failed!", api_context); + ecm->run_test = EC_EXITING; + signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); return 0; } if (s->listener_handle == SESSION_INVALID_HANDLE) - return quic_echo_clients_qsession_connected_callback (app_index, - api_context, s, - is_fail); - DBG ("STREAM Connection callback %d", api_context); + return quic_ec_qsession_connected_callback (app_index, api_context, s, + err); + ec_dbg ("STREAM Connection callback %d", api_context); thread_index = s->thread_index; ASSERT (thread_index == vlib_get_thread_index () || session_transport_service_type (s) == TRANSPORT_SERVICE_CL); - if (!ecm->vpp_event_queue[thread_index]) - ecm->vpp_event_queue[thread_index] = - session_main_get_vpp_event_queue (thread_index); + wrk = ec_worker_get (thread_index); /* * Setup session */ - clib_spinlock_lock_if_init (&ecm->sessions_lock); - pool_get (ecm->sessions, session); - clib_spinlock_unlock_if_init (&ecm->sessions_lock); - - clib_memset (session, 0, sizeof (*session)); - session_index = session - ecm->sessions; - session->bytes_to_send = ecm->bytes_to_send; - session->bytes_to_receive = ecm->no_return ? 0ULL : ecm->bytes_to_send; - session->data.rx_fifo = s->rx_fifo; - session->data.rx_fifo->client_session_index = session_index; - session->data.tx_fifo = s->tx_fifo; - session->data.tx_fifo->client_session_index = session_index; - session->data.vpp_evt_q = ecm->vpp_event_queue[thread_index]; - session->vpp_session_handle = session_handle (s); + es = ec_session_alloc (wrk); + + es->bytes_to_send = 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; + es->data.tx_fifo->shr->client_session_index = es->data.session_index; + es->data.vpp_evt_q = wrk->vpp_event_queue; + es->vpp_session_handle = session_handle (s); + es->vpp_session_index = s->session_index; + s->opaque = es->data.session_index; if (ecm->is_dgram) { transport_connection_t *tc; tc = session_get_transport (s); - clib_memcpy_fast (&session->data.transport, tc, - sizeof (session->data.transport)); - session->data.is_dgram = 1; + clib_memcpy_fast (&es->data.transport, tc, sizeof (es->data.transport)); + es->data.is_dgram = 1; } - vec_add1 (ecm->connection_index_by_thread[thread_index], session_index); + vec_add1 (wrk->conn_indices, es->data.session_index); clib_atomic_fetch_add (&ecm->ready_connections, 1); if (ecm->ready_connections == ecm->expected_connections) { - ecm->run_test = ECHO_CLIENTS_RUNNING; + ecm->run_test = EC_RUNNING; /* Signal the CLI process that the action is starting... */ - signal_evt_to_cli (1); + signal_evt_to_cli (EC_CLI_CONNECTS_DONE); } return 0; } static int -echo_clients_session_connected_callback (u32 app_index, u32 api_context, - session_t * s, u8 is_fail) +ec_session_connected_callback (u32 app_index, u32 api_context, session_t *s, + session_error_t err) { - echo_client_main_t *ecm = &echo_client_main; - eclient_session_t *session; - u32 session_index; - u8 thread_index; + ec_main_t *ecm = &ec_main; + ec_session_t *es; + u32 thread_index; + ec_worker_t *wrk; - if (PREDICT_FALSE (ecm->run_test != ECHO_CLIENTS_STARTING)) + if (PREDICT_FALSE (ecm->run_test != EC_STARTING)) return -1; - if (is_fail) + if (err) { - clib_warning ("connection %d failed!", api_context); - ecm->run_test = ECHO_CLIENTS_EXITING; - signal_evt_to_cli (-1); + 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; } @@ -501,57 +654,55 @@ echo_clients_session_connected_callback (u32 app_index, u32 api_context, ASSERT (thread_index == vlib_get_thread_index () || session_transport_service_type (s) == TRANSPORT_SERVICE_CL); - if (!ecm->vpp_event_queue[thread_index]) - ecm->vpp_event_queue[thread_index] = - session_main_get_vpp_event_queue (thread_index); + if (PREDICT_FALSE (api_context == HS_CTRL_HANDLE)) + return ec_ctrl_session_connected_callback (s); + + wrk = ec_worker_get (thread_index); /* * Setup session */ - clib_spinlock_lock_if_init (&ecm->sessions_lock); - pool_get (ecm->sessions, session); - clib_spinlock_unlock_if_init (&ecm->sessions_lock); - - clib_memset (session, 0, sizeof (*session)); - session_index = session - ecm->sessions; - session->bytes_to_send = ecm->bytes_to_send; - session->bytes_to_receive = ecm->no_return ? 0ULL : ecm->bytes_to_send; - session->data.rx_fifo = s->rx_fifo; - session->data.rx_fifo->client_session_index = session_index; - session->data.tx_fifo = s->tx_fifo; - session->data.tx_fifo->client_session_index = session_index; - session->data.vpp_evt_q = ecm->vpp_event_queue[thread_index]; - session->vpp_session_handle = session_handle (s); + es = ec_session_alloc (wrk); + + es->bytes_to_send = 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; + es->data.tx_fifo->shr->client_session_index = es->data.session_index; + es->data.vpp_evt_q = wrk->vpp_event_queue; + es->vpp_session_handle = session_handle (s); + es->vpp_session_index = s->session_index; + s->opaque = es->data.session_index; if (ecm->is_dgram) { transport_connection_t *tc; tc = session_get_transport (s); - clib_memcpy_fast (&session->data.transport, tc, - sizeof (session->data.transport)); - session->data.is_dgram = 1; + clib_memcpy_fast (&es->data.transport, tc, sizeof (es->data.transport)); + es->data.is_dgram = 1; } - vec_add1 (ecm->connection_index_by_thread[thread_index], session_index); + vec_add1 (wrk->conn_indices, es->data.session_index); clib_atomic_fetch_add (&ecm->ready_connections, 1); if (ecm->ready_connections == ecm->expected_connections) { - ecm->run_test = ECHO_CLIENTS_RUNNING; + ecm->run_test = EC_RUNNING; /* Signal the CLI process that the action is starting... */ - signal_evt_to_cli (1); + signal_evt_to_cli (EC_CLI_CONNECTS_DONE); } return 0; } static void -echo_clients_session_reset_callback (session_t * s) +ec_session_reset_callback (session_t *s) { - echo_client_main_t *ecm = &echo_client_main; + ec_main_t *ecm = &ec_main; 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; @@ -560,16 +711,23 @@ echo_clients_session_reset_callback (session_t * s) } static int -echo_clients_session_create_callback (session_t * s) +ec_session_accept_callback (session_t *s) { return 0; } static void -echo_clients_session_disconnect_callback (session_t * s) +ec_session_disconnect_callback (session_t *s) { - echo_client_main_t *ecm = &echo_client_main; + 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); @@ -577,9 +735,9 @@ echo_clients_session_disconnect_callback (session_t * s) } void -echo_clients_session_disconnect (session_t * s) +ec_session_disconnect (session_t *s) { - echo_client_main_t *ecm = &echo_client_main; + ec_main_t *ecm = &ec_main; vnet_disconnect_args_t _a = { 0 }, *a = &_a; a->handle = session_handle (s); a->app_index = ecm->app_index; @@ -587,74 +745,141 @@ echo_clients_session_disconnect (session_t * s) } static int -echo_clients_rx_callback (session_t * s) +ec_ctrl_session_rx_callback (session_t *s) { - echo_client_main_t *ecm = &echo_client_main; - eclient_session_t *sp; + ec_main_t *ecm = &ec_main; + int rx_bytes; + hs_test_cfg_t cfg = { 0 }; - if (PREDICT_FALSE (ecm->run_test != ECHO_CLIENTS_RUNNING)) + rx_bytes = svm_fifo_dequeue (s->rx_fifo, sizeof (cfg), (u8 *) &cfg); + if (rx_bytes != sizeof (cfg)) { - echo_clients_session_disconnect (s); + ec_err ("invalid cfg length %d (expected %d)", rx_bytes, sizeof (cfg)); + signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); return -1; } - sp = pool_elt_at_index (ecm->sessions, s->rx_fifo->client_session_index); - receive_data_chunk (ecm, sp); + ec_dbg ("control message received:"); + if (ecm->cfg.verbose) + hs_test_cfg_dump (&cfg, 1); - if (svm_fifo_max_dequeue_cons (s->rx_fifo)) + 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) +{ + ec_main_t *ecm = &ec_main; + 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)) { - if (svm_fifo_set_event (s->rx_fifo)) - session_send_io_evt_to_thread (s->rx_fifo, SESSION_IO_EVT_BUILTIN_RX); + ec_session_disconnect (s); + return -1; } + + wrk = ec_worker_get (s->thread_index); + es = ec_session_get (wrk, s->opaque); + + receive_data_chunk (wrk, es); + + if (svm_fifo_max_dequeue_cons (s->rx_fifo)) + session_enqueue_notify (s); + + return 0; +} + +static int +ec_add_segment_callback (u32 app_index, u64 segment_handle) +{ + /* New segments may be added */ return 0; } -int -echo_client_add_segment_callback (u32 client_index, u64 segment_handle) +static int +ec_del_segment_callback (u32 app_index, u64 segment_handle) { - /* New heaps may be added */ return 0; } -/* *INDENT-OFF* */ -static session_cb_vft_t echo_clients = { - .session_reset_callback = echo_clients_session_reset_callback, - .session_connected_callback = echo_clients_session_connected_callback, - .session_accept_callback = echo_clients_session_create_callback, - .session_disconnect_callback = echo_clients_session_disconnect_callback, - .builtin_app_rx_callback = echo_clients_rx_callback, - .add_segment_callback = echo_client_add_segment_callback +static session_cb_vft_t ec_cb_vft = { + .session_reset_callback = ec_session_reset_callback, + .session_connected_callback = ec_session_connected_callback, + .session_accept_callback = ec_session_accept_callback, + .session_disconnect_callback = ec_session_disconnect_callback, + .builtin_app_rx_callback = ec_session_rx_callback, + .add_segment_callback = ec_add_segment_callback, + .del_segment_callback = ec_del_segment_callback, }; -/* *INDENT-ON* */ static clib_error_t * -echo_clients_attach (u8 * appns_id, u64 appns_flags, u64 appns_secret) +ec_attach () { - vnet_app_add_tls_cert_args_t _a_cert, *a_cert = &_a_cert; - vnet_app_add_tls_key_args_t _a_key, *a_key = &_a_key; - u32 prealloc_fifos, segment_size = 256 << 20; - echo_client_main_t *ecm = &echo_client_main; + vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair; + ec_main_t *ecm = &ec_main; vnet_app_attach_args_t _a, *a = &_a; - u64 options[17]; + u32 prealloc_fifos; + u64 options[18]; int rv; clib_memset (a, 0, sizeof (*a)); clib_memset (options, 0, sizeof (options)); - a->api_client_index = ecm->my_client_index; + a->api_client_index = ~0; + a->name = format (0, "echo_client"); if (ecm->transport_proto == TRANSPORT_PROTO_QUIC) - echo_clients.session_connected_callback = - quic_echo_clients_session_connected_callback; - a->session_cb_vft = &echo_clients; + ec_cb_vft.session_connected_callback = quic_ec_session_connected_callback; + a->session_cb_vft = &ec_cb_vft; prealloc_fifos = ecm->prealloc_fifos ? ecm->expected_connections : 1; - if (ecm->private_segment_size) - segment_size = ecm->private_segment_size; - options[APP_OPTIONS_ACCEPT_COOKIE] = 0x12345678; - options[APP_OPTIONS_SEGMENT_SIZE] = segment_size; - options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size; + options[APP_OPTIONS_SEGMENT_SIZE] = ecm->private_segment_size; + options[APP_OPTIONS_ADD_SEGMENT_SIZE] = ecm->private_segment_size; options[APP_OPTIONS_RX_FIFO_SIZE] = ecm->fifo_size; options[APP_OPTIONS_TX_FIFO_SIZE] = ecm->fifo_size; options[APP_OPTIONS_PRIVATE_SEGMENT_COUNT] = ecm->private_segment_count; @@ -662,400 +887,498 @@ echo_clients_attach (u8 * appns_id, u64 appns_flags, u64 appns_secret) options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN; options[APP_OPTIONS_TLS_ENGINE] = ecm->tls_engine; options[APP_OPTIONS_PCT_FIRST_ALLOC] = 100; - if (appns_id) + options[APP_OPTIONS_FLAGS] |= ecm->attach_flags; + if (ecm->appns_id) { - options[APP_OPTIONS_FLAGS] |= appns_flags; - options[APP_OPTIONS_NAMESPACE_SECRET] = appns_secret; + options[APP_OPTIONS_NAMESPACE_SECRET] = ecm->appns_secret; + a->namespace_id = ecm->appns_id; } a->options = options; - a->namespace_id = appns_id; if ((rv = vnet_application_attach (a))) return clib_error_return (0, "attach returned %d", rv); ecm->app_index = a->app_index; + vec_free (a->name); + + clib_memset (ck_pair, 0, sizeof (*ck_pair)); + ck_pair->cert = (u8 *) test_srv_crt_rsa; + ck_pair->key = (u8 *) test_srv_key_rsa; + ck_pair->cert_len = test_srv_crt_rsa_len; + ck_pair->key_len = test_srv_key_rsa_len; + vnet_app_add_cert_key_pair (ck_pair); + ecm->ckpair_index = ck_pair->index; + + ecm->test_client_attached = 1; - clib_memset (a_cert, 0, sizeof (*a_cert)); - a_cert->app_index = a->app_index; - vec_validate (a_cert->cert, test_srv_crt_rsa_len); - clib_memcpy_fast (a_cert->cert, test_srv_crt_rsa, test_srv_crt_rsa_len); - vnet_app_add_tls_cert (a_cert); - - clib_memset (a_key, 0, sizeof (*a_key)); - a_key->app_index = a->app_index; - vec_validate (a_key->key, test_srv_key_rsa_len); - clib_memcpy_fast (a_key->key, test_srv_key_rsa, test_srv_key_rsa_len); - vnet_app_add_tls_key (a_key); return 0; } static int -echo_clients_detach () +ec_detach () { - echo_client_main_t *ecm = &echo_client_main; + ec_main_t *ecm = &ec_main; vnet_app_detach_args_t _da, *da = &_da; int rv; + if (!ecm->test_client_attached) + return 0; + da->app_index = ecm->app_index; da->api_client_index = ~0; rv = vnet_application_detach (da); ecm->test_client_attached = 0; ecm->app_index = ~0; + vnet_app_del_cert_key_pair (ecm->ckpair_index); + return rv; } -static void * -echo_client_thread_fn (void *arg) +static int +ec_transport_needs_crypto (transport_proto_t proto) { - return 0; + return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS || + proto == TRANSPORT_PROTO_QUIC; } -/** Start a transmit thread */ -int -echo_clients_start_tx_pthread (echo_client_main_t * ecm) +static int +ec_connect_rpc (void *args) { - if (ecm->client_thread_handle == 0) + ec_main_t *ecm = &ec_main; + vnet_connect_args_t _a = {}, *a = &_a; + 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; + + while (ci < n_clients) { - int rv = pthread_create (&ecm->client_thread_handle, - NULL /*attr */ , - echo_client_thread_fn, 0); + /* Crude pacing for call setups */ + if (ci - ecm->ready_connections > 128) + { + ecm->connect_conn_index = ci; + break; + } + + a->api_context = ci; + if (needs_crypto) + { + session_endpoint_alloc_ext_cfg (&a->sep_ext, + TRANSPORT_ENDPT_EXT_CFG_CRYPTO); + a->sep_ext.ext_cfg->crypto.ckpair_index = ecm->ckpair_index; + } + + rv = vnet_connect (a); + + if (needs_crypto) + clib_mem_free (a->sep_ext.ext_cfg); + if (rv) { - ecm->client_thread_handle = 0; - return -1; + ec_err ("connect returned: %U", format_session_error, rv); + ecm->run_test = EC_EXITING; + signal_evt_to_cli (EC_CLI_CONNECTS_FAILED); + break; } + + ci += 1; } + + if (ci < ecm->expected_connections && ecm->run_test != EC_EXITING) + ec_program_connects (); + return 0; } -clib_error_t * -echo_clients_connect (vlib_main_t * vm, u32 n_clients) +void +ec_program_connects (void) { - echo_client_main_t *ecm = &echo_client_main; - vnet_connect_args_t _a, *a = &_a; - int i, rv; + session_send_rpc_evt_to_thread_force (transport_cl_thread (), ec_connect_rpc, + 0); +} - clib_memset (a, 0, sizeof (*a)); +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; - for (i = 0; i < n_clients; i++) + rv = vnet_connect (a); + if (rv) { - a->uri = (char *) ecm->connect_uri; - a->api_context = i; - a->app_index = ecm->app_index; - - vlib_worker_thread_barrier_sync (vm); - if ((rv = vnet_connect_uri (a))) - { - vlib_worker_thread_barrier_release (vm); - return clib_error_return (0, "connect returned: %d", rv); - } - vlib_worker_thread_barrier_release (vm); - - /* Crude pacing for call setups */ - if ((i % 16) == 0) - vlib_process_suspend (vm, 100e-6); - ASSERT (i + 1 >= ecm->ready_connections); - while (i + 1 - ecm->ready_connections > 128) - vlib_process_suspend (vm, 1e-3); + 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; } -#define ec_cli_output(_fmt, _args...) \ - if (!ecm->no_output) \ - vlib_cli_output(vm, _fmt, ##_args) +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_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 * -echo_clients_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) +ec_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) { - echo_client_main_t *ecm = &echo_client_main; - vlib_thread_main_t *thread_main = vlib_get_thread_main (); - u64 tmp, total_bytes, appns_flags = 0, appns_secret = 0; - f64 test_timeout = 20.0, syn_timeout = 20.0, delta; - char *default_uri = "tcp://6.0.1.1/1234"; + unformat_input_t _line_input, *line_input = &_line_input; + char *default_uri = "tcp://6.0.1.1/1234", *transfer_type; + ec_main_t *ecm = &ec_main; uword *event_data = 0, event_type; - f64 time_before_connects; - u32 n_clients = 1; - int preallocate_sessions = 0; - char *transfer_type; clib_error_t *error = 0; - u8 *appns_id = 0; - int i; - session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; - int rv; + int rv, had_config = 1; + u64 tmp, total_bytes; + f64 delta; - ecm->quic_streams = 1; - ecm->bytes_to_send = 8192; - ecm->no_return = 0; - ecm->fifo_size = 64 << 10; - ecm->connections_per_batch = 1000; - ecm->private_segment_count = 0; - ecm->private_segment_size = 0; - ecm->no_output = 0; - ecm->test_bytes = 0; - ecm->test_failed = 0; - ecm->vlib_main = vm; - ecm->tls_engine = CRYPTO_ENGINE_OPENSSL; - ecm->no_copy = 0; - ecm->run_test = ECHO_CLIENTS_STARTING; + if (ecm->test_client_attached) + return clib_error_return (0, "failed: already running!"); - if (thread_main->n_vlib_mains > 1) - clib_spinlock_init (&ecm->sessions_lock); - vec_free (ecm->connect_uri); + if (ec_init (vm)) + { + error = clib_error_return (0, "failed init"); + goto cleanup; + } + + if (!unformat_user (input, unformat_line_input, line_input)) + { + had_config = 0; + goto parse_config; + } - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { - if (unformat (input, "uri %s", &ecm->connect_uri)) + if (unformat (line_input, "uri %s", &ecm->connect_uri)) ; - else if (unformat (input, "nclients %d", &n_clients)) + else if (unformat (line_input, "nclients %d", &ecm->n_clients)) ; - else if (unformat (input, "quic-streams %d", &ecm->quic_streams)) + else if (unformat (line_input, "quic-streams %d", &ecm->quic_streams)) ; - else if (unformat (input, "mbytes %lld", &tmp)) + else if (unformat (line_input, "mbytes %lld", &tmp)) ecm->bytes_to_send = tmp << 20; - else if (unformat (input, "gbytes %lld", &tmp)) + else if (unformat (line_input, "gbytes %lld", &tmp)) ecm->bytes_to_send = tmp << 30; - else if (unformat (input, "bytes %lld", &ecm->bytes_to_send)) + else if (unformat (line_input, "bytes %U", unformat_memory_size, + &ecm->bytes_to_send)) + ; + else if (unformat (line_input, "test-timeout %f", &ecm->test_timeout)) ; - else if (unformat (input, "test-timeout %f", &test_timeout)) + else if (unformat (line_input, "syn-timeout %f", &ecm->syn_timeout)) ; - else if (unformat (input, "syn-timeout %f", &syn_timeout)) + 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 (input, "no-return")) - ecm->no_return = 1; - else if (unformat (input, "fifo-size %d", &ecm->fifo_size)) - ecm->fifo_size <<= 10; - else if (unformat (input, "private-segment-count %d", + else if (unformat (line_input, "private-segment-count %d", &ecm->private_segment_count)) ; - else if (unformat (input, "private-segment-size %U", - unformat_memory_size, &tmp)) - { - if (tmp >= 0x100000000ULL) - return clib_error_return - (0, "private segment size %lld (%llu) too large", tmp, tmp); - ecm->private_segment_size = tmp; - } - else if (unformat (input, "preallocate-fifos")) + else if (unformat (line_input, "private-segment-size %U", + unformat_memory_size, &ecm->private_segment_size)) + ; + else if (unformat (line_input, "preallocate-fifos")) ecm->prealloc_fifos = 1; - else if (unformat (input, "preallocate-sessions")) - preallocate_sessions = 1; - else - if (unformat (input, "client-batch %d", &ecm->connections_per_batch)) + else if (unformat (line_input, "preallocate-sessions")) + ecm->prealloc_sessions = 1; + else if (unformat (line_input, "client-batch %d", + &ecm->connections_per_batch)) ; - else if (unformat (input, "appns %_%v%_", &appns_id)) + else if (unformat (line_input, "appns %_%v%_", &ecm->appns_id)) ; - else if (unformat (input, "all-scope")) - appns_flags |= (APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE - | APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE); - else if (unformat (input, "local-scope")) - appns_flags = APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; - else if (unformat (input, "global-scope")) - appns_flags = APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; - else if (unformat (input, "secret %lu", &appns_secret)) + else if (unformat (line_input, "all-scope")) + ecm->attach_flags |= (APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE | + APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE); + else if (unformat (line_input, "local-scope")) + ecm->attach_flags = APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + else if (unformat (line_input, "global-scope")) + ecm->attach_flags = APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + else if (unformat (line_input, "secret %lu", &ecm->appns_secret)) ; - else if (unformat (input, "no-output")) - ecm->no_output = 1; - else if (unformat (input, "test-bytes")) - ecm->test_bytes = 1; - else if (unformat (input, "tls-engine %d", &ecm->tls_engine)) + else if (unformat (line_input, "verbose")) + ecm->cfg.verbose = 1; + else if (unformat (line_input, "test-bytes")) + ecm->cfg.test_bytes = 1; + else if (unformat (line_input, "tls-engine %d", &ecm->tls_engine)) ; else - return clib_error_return (0, "failed: unknown input `%U'", - format_unformat_error, input); + { + error = clib_error_return (0, "failed: unknown input `%U'", + format_unformat_error, line_input); + goto cleanup; + } } - /* Store cli process node index for signalling */ - ecm->cli_node_index = - vlib_get_current_process (vm)->node_runtime.node_index; - - if (ecm->is_init == 0) - { - if (echo_clients_init (vm)) - return clib_error_return (0, "failed init"); - } +parse_config: - - ecm->ready_connections = 0; - ecm->expected_connections = n_clients * ecm->quic_streams; - ecm->rx_total = 0; - ecm->tx_total = 0; + 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); } - if ((rv = parse_uri ((char *) ecm->connect_uri, &sep))) - return clib_error_return (0, "Uri parse error: %d", rv); - ecm->transport_proto = sep.transport_proto; - ecm->is_dgram = (sep.transport_proto == TRANSPORT_PROTO_UDP - || sep.transport_proto == TRANSPORT_PROTO_UDPC); - -#if ECHO_CLIENT_PTHREAD - echo_clients_start_tx_pthread (); -#endif + if ((rv = parse_uri ((char *) ecm->connect_uri, &ecm->connect_sep))) + { + error = clib_error_return (0, "Uri parse error: %d", rv); + goto cleanup; + } + ecm->transport_proto = ecm->connect_sep.transport_proto; + ecm->is_dgram = (ecm->transport_proto == TRANSPORT_PROTO_UDP); - vlib_worker_thread_barrier_sync (vm); - vnet_session_enable_disable (vm, 1 /* turn on session and transports */ ); - vlib_worker_thread_barrier_release (vm); + if (ecm->prealloc_sessions) + ec_prealloc_sessions (ecm); - if (ecm->test_client_attached == 0) + if ((error = ec_attach ())) { - if ((error = echo_clients_attach (appns_id, appns_flags, appns_secret))) - { - vec_free (appns_id); - clib_error_report (error); - return error; - } - vec_free (appns_id); + clib_error_report (error); + goto cleanup; } - ecm->test_client_attached = 1; - /* Turn on the builtin client input nodes */ - for (i = 0; i < thread_main->n_vlib_mains; i++) - vlib_node_set_state (vlib_mains[i], echo_clients_node.index, - VLIB_NODE_STATE_POLLING); + if (ecm->echo_bytes) + ecm->cfg.test = HS_TEST_TYPE_BI; + else + ecm->cfg.test = HS_TEST_TYPE_UNI; - if (preallocate_sessions) - pool_init_fixed (ecm->sessions, 1.1 * n_clients); + ec_ctrl_connect (); + ec_wait_for_signal (EC_CLI_CFG_SYNC); - /* Fire off connect requests */ - time_before_connects = vlib_time_now (vm); - if ((error = echo_clients_connect (vm, n_clients))) + 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 (); - /* Park until the sessions come up, or ten seconds elapse... */ - vlib_process_wait_for_event_or_clock (vm, syn_timeout); + /* + * Park until the sessions come up, or syn_timeout seconds pass + */ + + 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_output ("Timeout with only %d sessions active...", - ecm->ready_connections); + ec_cli ("Timeout with only %d sessions active...", + ecm->ready_connections); error = clib_error_return (0, "failed: syn timeout with %d sessions", ecm->ready_connections); - goto cleanup; + goto stop_test; - case 1: - delta = vlib_time_now (vm) - time_before_connects; + case EC_CLI_CONNECTS_DONE: + delta = vlib_time_now (vm) - ecm->syn_start_time; if (delta != 0.0) - ec_cli_output ("%d three-way handshakes in %.2f seconds %.2f/s", - n_clients, delta, ((f64) n_clients) / delta); - - ecm->test_start_time = vlib_time_now (ecm->vlib_main); - ec_cli_output ("Test started at %.6f", ecm->test_start_time); + ec_cli ("%d three-way handshakes in %.2f seconds %.2f/s", + ecm->n_clients, delta, ((f64) ecm->n_clients) / delta); break; + case EC_CLI_CONNECTS_FAILED: + error = clib_error_return (0, "failed: connect returned"); + goto stop_test; + default: - ec_cli_output ("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; } - /* Now wait for the sessions to finish... */ - vlib_process_wait_for_event_or_clock (vm, test_timeout); + /* + * Wait for the sessions to finish or test_timeout seconds pass + */ + ecm->test_start_time = vlib_time_now (ecm->vlib_main); + ec_cli ("Test started at %.6f", ecm->test_start_time); + vlib_process_wait_for_event_or_clock (vm, ecm->test_timeout); event_type = vlib_process_get_events (vm, &event_data); switch (event_type) { case ~0: - ec_cli_output ("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 2: + case EC_CLI_TEST_DONE: ecm->test_end_time = vlib_time_now (vm); - ec_cli_output ("Test finished at %.6f", ecm->test_end_time); + ec_cli ("Test finished at %.6f", ecm->test_end_time); break; default: - ec_cli_output ("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; } + /* + * Done. Compute stats + */ delta = ecm->test_end_time - ecm->test_start_time; - if (delta != 0.0) + if (delta == 0.0) { - total_bytes = (ecm->no_return ? ecm->tx_total : ecm->rx_total); - transfer_type = ecm->no_return ? "half-duplex" : "full-duplex"; - ec_cli_output ("%lld bytes (%lld mbytes, %lld gbytes) in %.2f seconds", - total_bytes, total_bytes / (1ULL << 20), - total_bytes / (1ULL << 30), delta); - ec_cli_output ("%.2f bytes/second %s", ((f64) total_bytes) / (delta), - transfer_type); - ec_cli_output ("%.4f gbit/second %s", - (((f64) total_bytes * 8.0) / delta / 1e9), - transfer_type); - } - else - { - ec_cli_output ("zero delta-t?"); + ec_cli ("zero delta-t?"); error = clib_error_return (0, "failed: zero delta-t"); - goto cleanup; + goto stop_test; } - if (ecm->test_bytes && ecm->test_failed) + 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), + transfer_type); + ec_cli ("%.4f gbit/second %s", (((f64) total_bytes * 8.0) / delta / 1e9), + transfer_type); + + if (ecm->cfg.test_bytes && ecm->test_failed) error = clib_error_return (0, "failed: test bytes"); -cleanup: - ecm->run_test = ECHO_CLIENTS_EXITING; - vlib_process_wait_for_event_or_clock (vm, 10e-3); - for (i = 0; i < vec_len (ecm->connection_index_by_thread); i++) +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) { - vec_reset_length (ecm->connection_index_by_thread[i]); - vec_reset_length (ecm->connections_this_batch_by_thread[i]); - vec_reset_length (ecm->quic_session_index_by_thread[i]); + ec_cli ("failed to send post sync command"); + goto cleanup; } + ec_wait_for_signal (EC_CLI_CFG_SYNC); - pool_free (ecm->sessions); + /* disconnect control session */ + ec_ctrl_session_disconnect (); + +cleanup: + + ecm->run_test = EC_EXITING; + vlib_process_wait_for_event_or_clock (vm, 10e-3); /* Detach the application, so we can use different fifo sizes next time */ - if (ecm->test_client_attached) + if (ec_detach ()) { - if (echo_clients_detach ()) - { - error = clib_error_return (0, "failed: app detach"); - ec_cli_output ("WARNING: app detach failed..."); - } + error = clib_error_return (0, "failed: app detach"); + ec_cli ("WARNING: app detach failed..."); } + + ec_cleanup (ecm); + if (had_config) + unformat_free (line_input); + if (error) - ec_cli_output ("test failed"); - vec_free (ecm->connect_uri); + ec_cli ("test failed"); + return error; } -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (echo_clients_command, static) = -{ +VLIB_CLI_COMMAND (ec_command, static) = { .path = "test echo clients", - .short_help = "test echo clients [nclients %d][[m|g]bytes ]" - "[test-timeout