LCOV - code coverage report
Current view: top level - plugins/hs_apps - echo_client.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 417 559 74.6 %
Date: 2023-07-05 22:20:52 Functions: 30 36 83.3 %

          Line data    Source code
       1             : /*
       2             :  * echo_client.c - vpp built-in echo client code
       3             :  *
       4             :  * Copyright (c) 2017-2019 by Cisco and/or its affiliates.
       5             :  * Licensed under the Apache License, Version 2.0 (the "License");
       6             :  * you may not use this file except in compliance with the License.
       7             :  * You may obtain a copy of the License at:
       8             :  *
       9             :  *     http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             :  */
      17             : 
      18             : #include <hs_apps/echo_client.h>
      19             : 
      20             : static ec_main_t ec_main;
      21             : 
      22             : #define EC_DBG (0)
      23             : #define DBG(_fmt, _args...)                                                   \
      24             :   if (EC_DBG)                                                                 \
      25             :   clib_warning (_fmt, ##_args)
      26             : 
      27             : static void
      28          20 : signal_evt_to_cli_i (void *codep)
      29             : {
      30          20 :   ec_main_t *ecm = &ec_main;
      31             :   int code;
      32             : 
      33          20 :   ASSERT (vlib_get_thread_index () == 0);
      34          20 :   code = pointer_to_uword (codep);
      35          20 :   vlib_process_signal_event (ecm->vlib_main, ecm->cli_node_index, code, 0);
      36          20 : }
      37             : 
      38             : static void
      39          20 : signal_evt_to_cli (int code)
      40             : {
      41          20 :   if (vlib_get_thread_index () != 0)
      42           0 :     session_send_rpc_evt_to_thread_force (
      43           0 :       0, signal_evt_to_cli_i, uword_to_pointer ((uword) code, void *));
      44             :   else
      45          20 :     signal_evt_to_cli_i (uword_to_pointer ((uword) code, void *));
      46          20 : }
      47             : 
      48             : static inline ec_worker_t *
      49       43864 : ec_worker_get (u32 thread_index)
      50             : {
      51       43864 :   return vec_elt_at_index (ec_main.wrk, thread_index);
      52             : }
      53             : 
      54             : static inline ec_session_t *
      55         118 : ec_session_alloc (ec_worker_t *wrk)
      56             : {
      57             :   ec_session_t *ecs;
      58             : 
      59         118 :   pool_get_zero (wrk->sessions, ecs);
      60         118 :   ecs->data.session_index = ecs - wrk->sessions;
      61         118 :   ecs->thread_index = wrk->thread_index;
      62             : 
      63         118 :   return ecs;
      64             : }
      65             : 
      66             : static inline ec_session_t *
      67       46334 : ec_session_get (ec_worker_t *wrk, u32 ec_index)
      68             : {
      69       46334 :   return pool_elt_at_index (wrk->sessions, ec_index);
      70             : }
      71             : 
      72             : static void
      73       18255 : send_data_chunk (ec_main_t *ecm, ec_session_t *es)
      74             : {
      75       18255 :   u8 *test_data = ecm->connect_test_data;
      76             :   int test_buf_len, test_buf_offset, rv;
      77             :   u32 bytes_this_chunk;
      78             : 
      79       18255 :   test_buf_len = vec_len (test_data);
      80       18255 :   ASSERT (test_buf_len > 0);
      81       18255 :   test_buf_offset = es->bytes_sent % test_buf_len;
      82       18255 :   bytes_this_chunk =
      83       18255 :     clib_min (test_buf_len - test_buf_offset, es->bytes_to_send);
      84             : 
      85       18255 :   if (!ecm->is_dgram)
      86             :     {
      87       11072 :       if (ecm->no_copy)
      88             :         {
      89           0 :           svm_fifo_t *f = es->data.tx_fifo;
      90           0 :           rv = clib_min (svm_fifo_max_enqueue_prod (f), bytes_this_chunk);
      91           0 :           svm_fifo_enqueue_nocopy (f, rv);
      92           0 :           session_send_io_evt_to_thread_custom (
      93           0 :             &es->vpp_session_index, es->thread_index, SESSION_IO_EVT_TX);
      94             :         }
      95             :       else
      96       11072 :         rv = app_send_stream (&es->data, test_data + test_buf_offset,
      97             :                               bytes_this_chunk, 0);
      98             :     }
      99             :   else
     100             :     {
     101        7183 :       svm_fifo_t *f = es->data.tx_fifo;
     102        7183 :       u32 max_enqueue = svm_fifo_max_enqueue_prod (f);
     103             : 
     104        7183 :       if (max_enqueue < sizeof (session_dgram_hdr_t))
     105           0 :         return;
     106             : 
     107        7183 :       max_enqueue -= sizeof (session_dgram_hdr_t);
     108             : 
     109        7183 :       if (ecm->no_copy)
     110             :         {
     111             :           session_dgram_hdr_t hdr;
     112           0 :           app_session_transport_t *at = &es->data.transport;
     113             : 
     114           0 :           rv = clib_min (max_enqueue, bytes_this_chunk);
     115             : 
     116           0 :           hdr.data_length = rv;
     117           0 :           hdr.data_offset = 0;
     118           0 :           clib_memcpy_fast (&hdr.rmt_ip, &at->rmt_ip,
     119             :                             sizeof (ip46_address_t));
     120           0 :           hdr.is_ip4 = at->is_ip4;
     121           0 :           hdr.rmt_port = at->rmt_port;
     122           0 :           clib_memcpy_fast (&hdr.lcl_ip, &at->lcl_ip,
     123             :                             sizeof (ip46_address_t));
     124           0 :           hdr.lcl_port = at->lcl_port;
     125           0 :           svm_fifo_enqueue (f, sizeof (hdr), (u8 *) & hdr);
     126           0 :           svm_fifo_enqueue_nocopy (f, rv);
     127           0 :           session_send_io_evt_to_thread_custom (
     128           0 :             &es->vpp_session_index, es->thread_index, SESSION_IO_EVT_TX);
     129             :         }
     130             :       else
     131             :         {
     132        7183 :           bytes_this_chunk = clib_min (bytes_this_chunk, max_enqueue);
     133        7183 :           bytes_this_chunk = clib_min (bytes_this_chunk, 1460);
     134        7183 :           rv = app_send_dgram (&es->data, test_data + test_buf_offset,
     135             :                                bytes_this_chunk, 0);
     136             :         }
     137             :     }
     138             : 
     139             :   /* If we managed to enqueue data... */
     140       18255 :   if (rv > 0)
     141             :     {
     142             :       /* Account for it... */
     143       16263 :       es->bytes_to_send -= rv;
     144       16263 :       es->bytes_sent += rv;
     145             : 
     146             :       if (EC_DBG)
     147             :         {
     148             :           ELOG_TYPE_DECLARE (e) =
     149             :             {
     150             :               .format = "tx-enq: xfer %d bytes, sent %u remain %u",
     151             :               .format_args = "i4i4i4",
     152             :             };
     153             :           struct
     154             :           {
     155             :             u32 data[3];
     156             :           } *ed;
     157             :           ed = ELOG_DATA (&vlib_global_main.elog_main, e);
     158             :           ed->data[0] = rv;
     159             :           ed->data[1] = es->bytes_sent;
     160             :           ed->data[2] = es->bytes_to_send;
     161             :         }
     162             :     }
     163             : }
     164             : 
     165             : static void
     166       26760 : receive_data_chunk (ec_worker_t *wrk, ec_session_t *es)
     167             : {
     168       26760 :   ec_main_t *ecm = &ec_main;
     169       26760 :   svm_fifo_t *rx_fifo = es->data.rx_fifo;
     170             :   int n_read, i;
     171             : 
     172       26760 :   if (ecm->test_bytes)
     173             :     {
     174       26160 :       if (!ecm->is_dgram)
     175             :         n_read =
     176       26160 :           app_recv_stream (&es->data, wrk->rx_buf, vec_len (wrk->rx_buf));
     177             :       else
     178             :         n_read =
     179           0 :           app_recv_dgram (&es->data, wrk->rx_buf, vec_len (wrk->rx_buf));
     180             :     }
     181             :   else
     182             :     {
     183         600 :       n_read = svm_fifo_max_dequeue_cons (rx_fifo);
     184         600 :       svm_fifo_dequeue_drop (rx_fifo, n_read);
     185             :     }
     186             : 
     187       26760 :   if (n_read > 0)
     188             :     {
     189             :       if (EC_DBG)
     190             :         {
     191             :           ELOG_TYPE_DECLARE (e) =
     192             :             {
     193             :               .format = "rx-deq: %d bytes",
     194             :               .format_args = "i4",
     195             :             };
     196             :           struct
     197             :           {
     198             :             u32 data[1];
     199             :           } *ed;
     200             :           ed = ELOG_DATA (&vlib_global_main.elog_main, e);
     201             :           ed->data[0] = n_read;
     202             :         }
     203             : 
     204       26760 :       if (ecm->test_bytes)
     205             :         {
     206    33580600 :           for (i = 0; i < n_read; i++)
     207             :             {
     208    33554400 :               if (wrk->rx_buf[i] != ((es->bytes_received + i) & 0xff))
     209             :                 {
     210           0 :                   clib_warning ("read %d error at byte %lld, 0x%x not 0x%x",
     211             :                                 n_read, es->bytes_received + i, wrk->rx_buf[i],
     212             :                                 ((es->bytes_received + i) & 0xff));
     213           0 :                   ecm->test_failed = 1;
     214             :                 }
     215             :             }
     216             :         }
     217       26760 :       ASSERT (n_read <= es->bytes_to_receive);
     218       26760 :       es->bytes_to_receive -= n_read;
     219       26760 :       es->bytes_received += n_read;
     220             :     }
     221       26760 : }
     222             : 
     223             : static uword
     224     6909470 : ec_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     225             : {
     226             :   u32 *conn_indices, *conns_this_batch, nconns_this_batch;
     227     6909470 :   int thread_index = vm->thread_index, i, delete_session;
     228     6909470 :   ec_main_t *ecm = &ec_main;
     229             :   ec_worker_t *wrk;
     230             :   ec_session_t *es;
     231             :   session_t *s;
     232             : 
     233     6909470 :   if (ecm->run_test != EC_RUNNING)
     234     6892490 :     return 0;
     235             : 
     236       16986 :   wrk = ec_worker_get (thread_index);
     237       16986 :   conn_indices = wrk->conn_indices;
     238       16986 :   conns_this_batch = wrk->conns_this_batch;
     239             : 
     240       16986 :   if (((vec_len (conn_indices) == 0) && vec_len (conns_this_batch) == 0))
     241           0 :     return 0;
     242             : 
     243             :   /* Grab another pile of connections */
     244       16986 :   if (PREDICT_FALSE (vec_len (conns_this_batch) == 0))
     245             :     {
     246          10 :       nconns_this_batch =
     247          10 :         clib_min (ecm->connections_per_batch, vec_len (conn_indices));
     248             : 
     249          10 :       ASSERT (nconns_this_batch > 0);
     250          10 :       vec_validate (conns_this_batch, nconns_this_batch - 1);
     251          20 :       clib_memcpy_fast (conns_this_batch,
     252          10 :                         conn_indices + vec_len (conn_indices) -
     253             :                           nconns_this_batch,
     254             :                         nconns_this_batch * sizeof (u32));
     255          10 :       vec_dec_len (conn_indices, nconns_this_batch);
     256             :     }
     257             : 
     258             :   /*
     259             :    * Track progress
     260             :    */
     261       16986 :   if (PREDICT_FALSE (ecm->prev_conns != ecm->connections_per_batch &&
     262             :                      ecm->prev_conns == vec_len (conns_this_batch)))
     263             :     {
     264       16970 :       ecm->repeats++;
     265       16970 :       ecm->prev_conns = vec_len (conns_this_batch);
     266       16970 :       if (ecm->repeats == 500000)
     267             :         {
     268           0 :           clib_warning ("stuck clients");
     269             :         }
     270             :     }
     271             :   else
     272             :     {
     273          16 :       ecm->prev_conns = vec_len (conns_this_batch);
     274          16 :       ecm->repeats = 0;
     275             :     }
     276             : 
     277             :   /*
     278             :    * Handle connections in this batch
     279             :    */
     280       36560 :   for (i = 0; i < vec_len (conns_this_batch); i++)
     281             :     {
     282       19574 :       es = ec_session_get (wrk, conns_this_batch[i]);
     283             : 
     284       19574 :       delete_session = 1;
     285             : 
     286       19574 :       if (es->bytes_to_send > 0)
     287             :         {
     288       18255 :           send_data_chunk (ecm, es);
     289       18255 :           delete_session = 0;
     290             :         }
     291             : 
     292       19574 :       if (es->bytes_to_receive > 0)
     293             :         {
     294       12273 :           delete_session = 0;
     295             :         }
     296             : 
     297       19574 :       if (PREDICT_FALSE (delete_session == 1))
     298             :         {
     299         118 :           clib_atomic_fetch_add (&ecm->tx_total, es->bytes_sent);
     300         118 :           clib_atomic_fetch_add (&ecm->rx_total, es->bytes_received);
     301         118 :           s = session_get_from_handle_if_valid (es->vpp_session_handle);
     302             : 
     303         118 :           if (s)
     304             :             {
     305         118 :               vnet_disconnect_args_t _a, *a = &_a;
     306         118 :               a->handle = session_handle (s);
     307         118 :               a->app_index = ecm->app_index;
     308         118 :               vnet_disconnect_session (a);
     309             : 
     310         118 :               vec_delete (conns_this_batch, 1, i);
     311         118 :               i--;
     312         118 :               clib_atomic_fetch_add (&ecm->ready_connections, -1);
     313             :             }
     314             :           else
     315             :             {
     316           0 :               clib_warning ("session AWOL?");
     317           0 :               vec_delete (conns_this_batch, 1, i);
     318             :             }
     319             : 
     320             :           /* Kick the debug CLI process */
     321         118 :           if (ecm->ready_connections == 0)
     322             :             {
     323          10 :               signal_evt_to_cli (EC_CLI_TEST_DONE);
     324             :             }
     325             :         }
     326             :     }
     327             : 
     328       16986 :   wrk->conn_indices = conn_indices;
     329       16986 :   wrk->conns_this_batch = conns_this_batch;
     330       16986 :   return 0;
     331             : }
     332             : 
     333      129400 : VLIB_REGISTER_NODE (echo_clients_node) = {
     334             :   .function = ec_node_fn,
     335             :   .name = "echo-clients",
     336             :   .type = VLIB_NODE_TYPE_INPUT,
     337             :   .state = VLIB_NODE_STATE_DISABLED,
     338             : };
     339             : 
     340             : static void
     341          10 : ec_reset_runtime_config (ec_main_t *ecm)
     342             : {
     343          10 :   ecm->n_clients = 1;
     344          10 :   ecm->quic_streams = 1;
     345          10 :   ecm->bytes_to_send = 8192;
     346          10 :   ecm->no_return = 0;
     347          10 :   ecm->fifo_size = 64 << 10;
     348          10 :   ecm->connections_per_batch = 1000;
     349          10 :   ecm->private_segment_count = 0;
     350          10 :   ecm->private_segment_size = 256 << 20;
     351          10 :   ecm->no_output = 0;
     352          10 :   ecm->test_bytes = 0;
     353          10 :   ecm->test_failed = 0;
     354          10 :   ecm->tls_engine = CRYPTO_ENGINE_OPENSSL;
     355          10 :   ecm->no_copy = 0;
     356          10 :   ecm->run_test = EC_STARTING;
     357          10 :   ecm->ready_connections = 0;
     358          10 :   ecm->connect_conn_index = 0;
     359          10 :   ecm->rx_total = 0;
     360          10 :   ecm->tx_total = 0;
     361          10 :   ecm->barrier_acq_needed = 0;
     362          10 :   ecm->prealloc_sessions = 0;
     363          10 :   ecm->prealloc_fifos = 0;
     364          10 :   ecm->appns_id = 0;
     365          10 :   ecm->appns_secret = 0;
     366          10 :   ecm->attach_flags = 0;
     367          10 :   ecm->syn_timeout = 20.0;
     368          10 :   ecm->test_timeout = 20.0;
     369          10 :   vec_free (ecm->connect_uri);
     370          10 : }
     371             : 
     372             : static int
     373          10 : ec_init (vlib_main_t *vm)
     374             : {
     375          10 :   ec_main_t *ecm = &ec_main;
     376             :   ec_worker_t *wrk;
     377             :   u32 num_threads;
     378             :   int i;
     379             : 
     380          10 :   ec_reset_runtime_config (ecm);
     381             : 
     382             :   /* Store cli process node index for signaling */
     383          10 :   ecm->cli_node_index = vlib_get_current_process (vm)->node_runtime.node_index;
     384          10 :   ecm->vlib_main = vm;
     385             : 
     386          10 :   if (vlib_num_workers ())
     387             :     {
     388             :       /* The request came over the binary api and the inband cli handler
     389             :        * is not mp_safe. Drop the barrier to make sure the workers are not
     390             :        * blocked.
     391             :        */
     392           0 :       if (vlib_thread_is_main_w_barrier ())
     393             :         {
     394           0 :           ecm->barrier_acq_needed = 1;
     395           0 :           vlib_worker_thread_barrier_release (vm);
     396             :         }
     397             :       /*
     398             :        * There's a good chance that both the client and the server echo
     399             :        * apps will be enabled so make sure the session queue node polls on
     400             :        * the main thread as connections will probably be established on it.
     401             :        */
     402           0 :       vlib_node_set_state (vm, session_queue_node.index,
     403             :                            VLIB_NODE_STATE_POLLING);
     404             :     }
     405             : 
     406             :   /* App init done only once */
     407          10 :   if (ecm->app_is_init)
     408           4 :     return 0;
     409             : 
     410             : 
     411             :   /* Init test data. Big buffer */
     412           6 :   vec_validate (ecm->connect_test_data, 4 * 1024 * 1024 - 1);
     413    25165800 :   for (i = 0; i < vec_len (ecm->connect_test_data); i++)
     414    25165800 :     ecm->connect_test_data[i] = i & 0xff;
     415             : 
     416           6 :   num_threads = 1 /* main thread */ + vlib_num_workers ();
     417           6 :   vec_validate (ecm->wrk, num_threads - 1);
     418          12 :   vec_foreach (wrk, ecm->wrk)
     419             :     {
     420           6 :       vec_validate (wrk->rx_buf, vec_len (ecm->connect_test_data) - 1);
     421           6 :       wrk->thread_index = wrk - ecm->wrk;
     422           6 :       wrk->vpp_event_queue =
     423           6 :         session_main_get_vpp_event_queue (wrk->thread_index);
     424             :     }
     425             : 
     426           6 :   ecm->app_is_init = 1;
     427             : 
     428           6 :   vlib_worker_thread_barrier_sync (vm);
     429           6 :   vnet_session_enable_disable (vm, 1 /* turn on session and transports */);
     430           6 :   vlib_worker_thread_barrier_release (vm);
     431             : 
     432             :   /* Turn on the builtin client input nodes */
     433          12 :   foreach_vlib_main ()
     434           6 :     vlib_node_set_state (this_vlib_main, echo_clients_node.index,
     435             :                          VLIB_NODE_STATE_POLLING);
     436             : 
     437           6 :   return 0;
     438             : }
     439             : 
     440             : static void
     441           0 : ec_prealloc_sessions (ec_main_t *ecm)
     442             : {
     443             :   u32 sessions_per_wrk, n_wrks;
     444             :   ec_worker_t *wrk;
     445             : 
     446           0 :   n_wrks = vlib_num_workers () ? vlib_num_workers () : 1;
     447             : 
     448           0 :   sessions_per_wrk = ecm->n_clients / n_wrks;
     449           0 :   vec_foreach (wrk, ecm->wrk)
     450           0 :     pool_init_fixed (wrk->sessions, 1.1 * sessions_per_wrk);
     451           0 : }
     452             : 
     453             : static void
     454          10 : ec_worker_cleanup (ec_worker_t *wrk)
     455             : {
     456          10 :   pool_free (wrk->sessions);
     457          10 :   vec_free (wrk->conn_indices);
     458          10 :   vec_free (wrk->conns_this_batch);
     459          10 : }
     460             : 
     461             : static void
     462          10 : ec_cleanup (ec_main_t *ecm)
     463             : {
     464             :   ec_worker_t *wrk;
     465             : 
     466          20 :   vec_foreach (wrk, ecm->wrk)
     467          10 :     ec_worker_cleanup (wrk);
     468             : 
     469          10 :   vec_free (ecm->connect_uri);
     470          10 :   vec_free (ecm->appns_id);
     471             : 
     472          10 :   if (ecm->barrier_acq_needed)
     473           0 :     vlib_worker_thread_barrier_sync (ecm->vlib_main);
     474          10 : }
     475             : 
     476             : static int
     477          16 : quic_ec_qsession_connected_callback (u32 app_index, u32 api_context,
     478             :                                      session_t *s, session_error_t err)
     479             : {
     480          16 :   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
     481          16 :   ec_main_t *ecm = &ec_main;
     482          16 :   vnet_connect_args_t *a = 0;
     483             :   session_handle_t handle;
     484             :   u32 stream_n;
     485             :   int rv;
     486             : 
     487             :   DBG ("QUIC Connection handle %d", session_handle (s));
     488             : 
     489          16 :   vec_validate (a, 1);
     490          16 :   a->uri = (char *) ecm->connect_uri;
     491          16 :   if (parse_uri (a->uri, &sep))
     492           0 :     return -1;
     493          16 :   sep.parent_handle = handle = session_handle (s);
     494             : 
     495          32 :   for (stream_n = 0; stream_n < ecm->quic_streams; stream_n++)
     496             :     {
     497          16 :       clib_memset (a, 0, sizeof (*a));
     498          16 :       a->app_index = ecm->app_index;
     499          16 :       a->api_context = -1 - api_context;
     500          16 :       clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
     501             : 
     502             :       DBG ("QUIC opening stream %d", stream_n);
     503          16 :       if ((rv = vnet_connect (a)))
     504             :         {
     505           0 :           clib_error ("Stream session %d opening failed: %d", stream_n, rv);
     506           0 :           return -1;
     507             :         }
     508             :       DBG ("QUIC stream %d connected", stream_n);
     509             :     }
     510             :   /*
     511             :    * 's' is no longer valid, its underlying pool could have been moved in
     512             :    * vnet_connect()
     513             :    */
     514          16 :   vec_free (a);
     515          16 :   return 0;
     516             : }
     517             : 
     518             : static int
     519          32 : quic_ec_session_connected_callback (u32 app_index, u32 api_context,
     520             :                                     session_t *s, session_error_t err)
     521             : {
     522          32 :   ec_main_t *ecm = &ec_main;
     523             :   ec_session_t *es;
     524             :   ec_worker_t *wrk;
     525             :   u32 thread_index;
     526             : 
     527          32 :   if (PREDICT_FALSE (ecm->run_test != EC_STARTING))
     528           0 :     return -1;
     529             : 
     530          32 :   if (err)
     531             :     {
     532           0 :       clib_warning ("connection %d failed!", api_context);
     533           0 :       ecm->run_test = EC_EXITING;
     534           0 :       signal_evt_to_cli (EC_CLI_CONNECTS_FAILED);
     535           0 :       return 0;
     536             :     }
     537             : 
     538          32 :   if (s->listener_handle == SESSION_INVALID_HANDLE)
     539          16 :     return quic_ec_qsession_connected_callback (app_index, api_context, s,
     540             :                                                 err);
     541             :   DBG ("STREAM Connection callback %d", api_context);
     542             : 
     543          16 :   thread_index = s->thread_index;
     544          16 :   ASSERT (thread_index == vlib_get_thread_index ()
     545             :           || session_transport_service_type (s) == TRANSPORT_SERVICE_CL);
     546             : 
     547          16 :   wrk = ec_worker_get (thread_index);
     548             : 
     549             :   /*
     550             :    * Setup session
     551             :    */
     552          16 :   es = ec_session_alloc (wrk);
     553             : 
     554          16 :   es->bytes_to_send = ecm->bytes_to_send;
     555          16 :   es->bytes_to_receive = ecm->no_return ? 0ULL : ecm->bytes_to_send;
     556          16 :   es->data.rx_fifo = s->rx_fifo;
     557          16 :   es->data.rx_fifo->shr->client_session_index = es->data.session_index;
     558          16 :   es->data.tx_fifo = s->tx_fifo;
     559          16 :   es->data.tx_fifo->shr->client_session_index = es->data.session_index;
     560          16 :   es->data.vpp_evt_q = wrk->vpp_event_queue;
     561          16 :   es->vpp_session_handle = session_handle (s);
     562          16 :   es->vpp_session_index = s->session_index;
     563          16 :   s->opaque = es->data.session_index;
     564             : 
     565          16 :   if (ecm->is_dgram)
     566             :     {
     567             :       transport_connection_t *tc;
     568           0 :       tc = session_get_transport (s);
     569           0 :       clib_memcpy_fast (&es->data.transport, tc, sizeof (es->data.transport));
     570           0 :       es->data.is_dgram = 1;
     571             :     }
     572             : 
     573          16 :   vec_add1 (wrk->conn_indices, es->data.session_index);
     574          16 :   clib_atomic_fetch_add (&ecm->ready_connections, 1);
     575          16 :   if (ecm->ready_connections == ecm->expected_connections)
     576             :     {
     577           7 :       ecm->run_test = EC_RUNNING;
     578             :       /* Signal the CLI process that the action is starting... */
     579           7 :       signal_evt_to_cli (EC_CLI_CONNECTS_DONE);
     580             :     }
     581             : 
     582          16 :   return 0;
     583             : }
     584             : 
     585             : static int
     586         102 : ec_session_connected_callback (u32 app_index, u32 api_context, session_t *s,
     587             :                                session_error_t err)
     588             : {
     589         102 :   ec_main_t *ecm = &ec_main;
     590             :   ec_session_t *es;
     591             :   u32 thread_index;
     592             :   ec_worker_t *wrk;
     593             : 
     594         102 :   if (PREDICT_FALSE (ecm->run_test != EC_STARTING))
     595           0 :     return -1;
     596             : 
     597         102 :   if (err)
     598             :     {
     599           0 :       clib_warning ("connection %d failed!", api_context);
     600           0 :       ecm->run_test = EC_EXITING;
     601           0 :       signal_evt_to_cli (EC_CLI_CONNECTS_FAILED);
     602           0 :       return 0;
     603             :     }
     604             : 
     605         102 :   thread_index = s->thread_index;
     606         102 :   ASSERT (thread_index == vlib_get_thread_index ()
     607             :           || session_transport_service_type (s) == TRANSPORT_SERVICE_CL);
     608             : 
     609         102 :   wrk = ec_worker_get (thread_index);
     610             : 
     611             :   /*
     612             :    * Setup session
     613             :    */
     614         102 :   es = ec_session_alloc (wrk);
     615             : 
     616         102 :   es->bytes_to_send = ecm->bytes_to_send;
     617         102 :   es->bytes_to_receive = ecm->no_return ? 0ULL : ecm->bytes_to_send;
     618         102 :   es->data.rx_fifo = s->rx_fifo;
     619         102 :   es->data.rx_fifo->shr->client_session_index = es->data.session_index;
     620         102 :   es->data.tx_fifo = s->tx_fifo;
     621         102 :   es->data.tx_fifo->shr->client_session_index = es->data.session_index;
     622         102 :   es->data.vpp_evt_q = wrk->vpp_event_queue;
     623         102 :   es->vpp_session_handle = session_handle (s);
     624         102 :   es->vpp_session_index = s->session_index;
     625         102 :   s->opaque = es->data.session_index;
     626             : 
     627         102 :   if (ecm->is_dgram)
     628             :     {
     629             :       transport_connection_t *tc;
     630           1 :       tc = session_get_transport (s);
     631           1 :       clib_memcpy_fast (&es->data.transport, tc, sizeof (es->data.transport));
     632           1 :       es->data.is_dgram = 1;
     633             :     }
     634             : 
     635         102 :   vec_add1 (wrk->conn_indices, es->data.session_index);
     636         102 :   clib_atomic_fetch_add (&ecm->ready_connections, 1);
     637         102 :   if (ecm->ready_connections == ecm->expected_connections)
     638             :     {
     639           3 :       ecm->run_test = EC_RUNNING;
     640             :       /* Signal the CLI process that the action is starting... */
     641           3 :       signal_evt_to_cli (EC_CLI_CONNECTS_DONE);
     642             :     }
     643             : 
     644         102 :   return 0;
     645             : }
     646             : 
     647             : static void
     648           0 : ec_session_reset_callback (session_t *s)
     649             : {
     650           0 :   ec_main_t *ecm = &ec_main;
     651           0 :   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
     652             : 
     653           0 :   if (s->session_state == SESSION_STATE_READY)
     654           0 :     clib_warning ("Reset active connection %U", format_session, s, 2);
     655             : 
     656           0 :   a->handle = session_handle (s);
     657           0 :   a->app_index = ecm->app_index;
     658           0 :   vnet_disconnect_session (a);
     659           0 :   return;
     660             : }
     661             : 
     662             : static int
     663           0 : ec_session_accept_callback (session_t *s)
     664             : {
     665           0 :   return 0;
     666             : }
     667             : 
     668             : static void
     669           0 : ec_session_disconnect_callback (session_t *s)
     670             : {
     671           0 :   ec_main_t *ecm = &ec_main;
     672           0 :   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
     673           0 :   a->handle = session_handle (s);
     674           0 :   a->app_index = ecm->app_index;
     675           0 :   vnet_disconnect_session (a);
     676           0 :   return;
     677             : }
     678             : 
     679             : void
     680           0 : ec_session_disconnect (session_t *s)
     681             : {
     682           0 :   ec_main_t *ecm = &ec_main;
     683           0 :   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
     684           0 :   a->handle = session_handle (s);
     685           0 :   a->app_index = ecm->app_index;
     686           0 :   vnet_disconnect_session (a);
     687           0 : }
     688             : 
     689             : static int
     690       26760 : ec_session_rx_callback (session_t *s)
     691             : {
     692       26760 :   ec_main_t *ecm = &ec_main;
     693             :   ec_worker_t *wrk;
     694             :   ec_session_t *es;
     695             : 
     696       26760 :   if (PREDICT_FALSE (ecm->run_test != EC_RUNNING))
     697             :     {
     698           0 :       ec_session_disconnect (s);
     699           0 :       return -1;
     700             :     }
     701             : 
     702       26760 :   wrk = ec_worker_get (s->thread_index);
     703       26760 :   es = ec_session_get (wrk, s->opaque);
     704             : 
     705       26760 :   receive_data_chunk (wrk, es);
     706             : 
     707       26760 :   if (svm_fifo_max_dequeue_cons (s->rx_fifo))
     708             :     {
     709           0 :       if (svm_fifo_set_event (s->rx_fifo))
     710           0 :         session_send_io_evt_to_thread (s->rx_fifo, SESSION_IO_EVT_BUILTIN_RX);
     711             :     }
     712       26760 :   return 0;
     713             : }
     714             : 
     715             : static int
     716          14 : ec_add_segment_callback (u32 app_index, u64 segment_handle)
     717             : {
     718             :   /* New segments may be added */
     719          14 :   return 0;
     720             : }
     721             : 
     722             : static int
     723           0 : ec_del_segment_callback (u32 app_index, u64 segment_handle)
     724             : {
     725           0 :   return 0;
     726             : }
     727             : 
     728             : static session_cb_vft_t ec_cb_vft = {
     729             :   .session_reset_callback = ec_session_reset_callback,
     730             :   .session_connected_callback = ec_session_connected_callback,
     731             :   .session_accept_callback = ec_session_accept_callback,
     732             :   .session_disconnect_callback = ec_session_disconnect_callback,
     733             :   .builtin_app_rx_callback = ec_session_rx_callback,
     734             :   .add_segment_callback = ec_add_segment_callback,
     735             :   .del_segment_callback = ec_del_segment_callback,
     736             : };
     737             : 
     738             : static clib_error_t *
     739          10 : ec_attach ()
     740             : {
     741          10 :   vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
     742          10 :   ec_main_t *ecm = &ec_main;
     743          10 :   vnet_app_attach_args_t _a, *a = &_a;
     744             :   u32 prealloc_fifos;
     745             :   u64 options[18];
     746             :   int rv;
     747             : 
     748          10 :   clib_memset (a, 0, sizeof (*a));
     749          10 :   clib_memset (options, 0, sizeof (options));
     750             : 
     751          10 :   a->api_client_index = ~0;
     752          10 :   a->name = format (0, "echo_client");
     753          10 :   if (ecm->transport_proto == TRANSPORT_PROTO_QUIC)
     754           7 :     ec_cb_vft.session_connected_callback = quic_ec_session_connected_callback;
     755          10 :   a->session_cb_vft = &ec_cb_vft;
     756             : 
     757          10 :   prealloc_fifos = ecm->prealloc_fifos ? ecm->expected_connections : 1;
     758             : 
     759          10 :   options[APP_OPTIONS_ACCEPT_COOKIE] = 0x12345678;
     760          10 :   options[APP_OPTIONS_SEGMENT_SIZE] = ecm->private_segment_size;
     761          10 :   options[APP_OPTIONS_ADD_SEGMENT_SIZE] = ecm->private_segment_size;
     762          10 :   options[APP_OPTIONS_RX_FIFO_SIZE] = ecm->fifo_size;
     763          10 :   options[APP_OPTIONS_TX_FIFO_SIZE] = ecm->fifo_size;
     764          10 :   options[APP_OPTIONS_PRIVATE_SEGMENT_COUNT] = ecm->private_segment_count;
     765          10 :   options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = prealloc_fifos;
     766          10 :   options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
     767          10 :   options[APP_OPTIONS_TLS_ENGINE] = ecm->tls_engine;
     768          10 :   options[APP_OPTIONS_PCT_FIRST_ALLOC] = 100;
     769          10 :   options[APP_OPTIONS_FLAGS] |= ecm->attach_flags;
     770          10 :   if (ecm->appns_id)
     771             :     {
     772          10 :       options[APP_OPTIONS_NAMESPACE_SECRET] = ecm->appns_secret;
     773          10 :       a->namespace_id = ecm->appns_id;
     774             :     }
     775          10 :   a->options = options;
     776             : 
     777          10 :   if ((rv = vnet_application_attach (a)))
     778           0 :     return clib_error_return (0, "attach returned %d", rv);
     779             : 
     780          10 :   ecm->app_index = a->app_index;
     781          10 :   vec_free (a->name);
     782             : 
     783          10 :   clib_memset (ck_pair, 0, sizeof (*ck_pair));
     784          10 :   ck_pair->cert = (u8 *) test_srv_crt_rsa;
     785          10 :   ck_pair->key = (u8 *) test_srv_key_rsa;
     786          10 :   ck_pair->cert_len = test_srv_crt_rsa_len;
     787          10 :   ck_pair->key_len = test_srv_key_rsa_len;
     788          10 :   vnet_app_add_cert_key_pair (ck_pair);
     789          10 :   ecm->ckpair_index = ck_pair->index;
     790             : 
     791          10 :   ecm->test_client_attached = 1;
     792             : 
     793          10 :   return 0;
     794             : }
     795             : 
     796             : static int
     797          10 : ec_detach ()
     798             : {
     799          10 :   ec_main_t *ecm = &ec_main;
     800          10 :   vnet_app_detach_args_t _da, *da = &_da;
     801             :   int rv;
     802             : 
     803          10 :   if (!ecm->test_client_attached)
     804           0 :     return 0;
     805             : 
     806          10 :   da->app_index = ecm->app_index;
     807          10 :   da->api_client_index = ~0;
     808          10 :   rv = vnet_application_detach (da);
     809          10 :   ecm->test_client_attached = 0;
     810          10 :   ecm->app_index = ~0;
     811          10 :   vnet_app_del_cert_key_pair (ecm->ckpair_index);
     812             : 
     813          10 :   return rv;
     814             : }
     815             : 
     816             : static int
     817          10 : ec_transport_needs_crypto (transport_proto_t proto)
     818             : {
     819          10 :   return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
     820             :          proto == TRANSPORT_PROTO_QUIC;
     821             : }
     822             : 
     823             : static int
     824          10 : ec_connect_rpc (void *args)
     825             : {
     826          10 :   ec_main_t *ecm = &ec_main;
     827          10 :   vnet_connect_args_t _a = {}, *a = &_a;
     828             :   int rv, needs_crypto;
     829             :   u32 n_clients, ci;
     830             : 
     831          10 :   n_clients = ecm->n_clients;
     832          10 :   needs_crypto = ec_transport_needs_crypto (ecm->transport_proto);
     833          10 :   clib_memcpy (&a->sep_ext, &ecm->connect_sep, sizeof (ecm->connect_sep));
     834          10 :   a->sep_ext.transport_flags |= TRANSPORT_CFG_F_CONNECTED;
     835          10 :   a->app_index = ecm->app_index;
     836             : 
     837          10 :   ci = ecm->connect_conn_index;
     838             : 
     839         128 :   while (ci < n_clients)
     840             :     {
     841             :       /* Crude pacing for call setups  */
     842         118 :       if (ci - ecm->ready_connections > 128)
     843             :         {
     844           0 :           ecm->connect_conn_index = ci;
     845           0 :           break;
     846             :         }
     847             : 
     848         118 :       a->api_context = ci;
     849         118 :       if (needs_crypto)
     850             :         {
     851          16 :           session_endpoint_alloc_ext_cfg (&a->sep_ext,
     852             :                                           TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
     853          16 :           a->sep_ext.ext_cfg->crypto.ckpair_index = ecm->ckpair_index;
     854             :         }
     855             : 
     856         118 :       rv = vnet_connect (a);
     857             : 
     858         118 :       if (needs_crypto)
     859          16 :         clib_mem_free (a->sep_ext.ext_cfg);
     860             : 
     861         118 :       if (rv)
     862             :         {
     863           0 :           clib_warning ("connect returned: %U", format_session_error, rv);
     864           0 :           ecm->run_test = EC_EXITING;
     865           0 :           signal_evt_to_cli (EC_CLI_CONNECTS_FAILED);
     866           0 :           break;
     867             :         }
     868             : 
     869         118 :       ci += 1;
     870             :     }
     871             : 
     872          10 :   if (ci < ecm->expected_connections && ecm->run_test != EC_EXITING)
     873           0 :     ec_program_connects ();
     874             : 
     875          10 :   return 0;
     876             : }
     877             : 
     878             : void
     879          10 : ec_program_connects (void)
     880             : {
     881          10 :   session_send_rpc_evt_to_thread_force (transport_cl_thread (), ec_connect_rpc,
     882             :                                         0);
     883          10 : }
     884             : 
     885             : #define ec_cli(_fmt, _args...)                                                \
     886             :   if (!ecm->no_output)                                                        \
     887             :   vlib_cli_output (vm, _fmt, ##_args)
     888             : 
     889             : static clib_error_t *
     890          10 : ec_command_fn (vlib_main_t *vm, unformat_input_t *input,
     891             :                vlib_cli_command_t *cmd)
     892             : {
     893          10 :   unformat_input_t _line_input, *line_input = &_line_input;
     894          10 :   char *default_uri = "tcp://6.0.1.1/1234", *transfer_type;
     895          10 :   ec_main_t *ecm = &ec_main;
     896          10 :   uword *event_data = 0, event_type;
     897          10 :   clib_error_t *error = 0;
     898          10 :   int rv, had_config = 1;
     899             :   u64 tmp, total_bytes;
     900             :   f64 delta;
     901             : 
     902          10 :   if (ecm->test_client_attached)
     903           0 :     return clib_error_return (0, "failed: already running!");
     904             : 
     905          10 :   if (ec_init (vm))
     906             :     {
     907           0 :       error = clib_error_return (0, "failed init");
     908           0 :       goto cleanup;
     909             :     }
     910             : 
     911          10 :   if (!unformat_user (input, unformat_line_input, line_input))
     912             :     {
     913           0 :       had_config = 0;
     914           0 :       goto parse_config;
     915             :     }
     916             : 
     917          75 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     918             :     {
     919          65 :       if (unformat (line_input, "uri %s", &ecm->connect_uri))
     920             :         ;
     921          55 :       else if (unformat (line_input, "nclients %d", &ecm->n_clients))
     922             :         ;
     923          53 :       else if (unformat (line_input, "quic-streams %d", &ecm->quic_streams))
     924             :         ;
     925          53 :       else if (unformat (line_input, "mbytes %lld", &tmp))
     926           9 :         ecm->bytes_to_send = tmp << 20;
     927          44 :       else if (unformat (line_input, "gbytes %lld", &tmp))
     928           0 :         ecm->bytes_to_send = tmp << 30;
     929          44 :       else if (unformat (line_input, "bytes %U", unformat_memory_size,
     930             :                          &ecm->bytes_to_send))
     931             :         ;
     932          44 :       else if (unformat (line_input, "test-timeout %f", &ecm->test_timeout))
     933             :         ;
     934          44 :       else if (unformat (line_input, "syn-timeout %f", &ecm->syn_timeout))
     935             :         ;
     936          41 :       else if (unformat (line_input, "no-return"))
     937           1 :         ecm->no_return = 1;
     938          40 :       else if (unformat (line_input, "fifo-size %d", &ecm->fifo_size))
     939          10 :         ecm->fifo_size <<= 10;
     940          30 :       else if (unformat (line_input, "private-segment-count %d",
     941             :                          &ecm->private_segment_count))
     942             :         ;
     943          30 :       else if (unformat (line_input, "private-segment-size %U",
     944             :                          unformat_memory_size, &ecm->private_segment_size))
     945             :         ;
     946          29 :       else if (unformat (line_input, "preallocate-fifos"))
     947           0 :         ecm->prealloc_fifos = 1;
     948          29 :       else if (unformat (line_input, "preallocate-sessions"))
     949           0 :         ecm->prealloc_sessions = 1;
     950          29 :       else if (unformat (line_input, "client-batch %d",
     951             :                          &ecm->connections_per_batch))
     952             :         ;
     953          29 :       else if (unformat (line_input, "appns %_%v%_", &ecm->appns_id))
     954             :         ;
     955          19 :       else if (unformat (line_input, "all-scope"))
     956           0 :         ecm->attach_flags |= (APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE |
     957             :                               APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE);
     958          19 :       else if (unformat (line_input, "local-scope"))
     959           0 :         ecm->attach_flags = APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE;
     960          19 :       else if (unformat (line_input, "global-scope"))
     961           0 :         ecm->attach_flags = APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
     962          19 :       else if (unformat (line_input, "secret %lu", &ecm->appns_secret))
     963             :         ;
     964          19 :       else if (unformat (line_input, "no-output"))
     965          10 :         ecm->no_output = 1;
     966           9 :       else if (unformat (line_input, "test-bytes"))
     967           9 :         ecm->test_bytes = 1;
     968           0 :       else if (unformat (line_input, "tls-engine %d", &ecm->tls_engine))
     969             :         ;
     970             :       else
     971             :         {
     972           0 :           error = clib_error_return (0, "failed: unknown input `%U'",
     973             :                                      format_unformat_error, line_input);
     974           0 :           goto cleanup;
     975             :         }
     976             :     }
     977             : 
     978          10 : parse_config:
     979             : 
     980          10 :   ecm->expected_connections = ecm->n_clients * ecm->quic_streams;
     981             : 
     982          10 :   if (!ecm->connect_uri)
     983             :     {
     984           0 :       clib_warning ("No uri provided. Using default: %s", default_uri);
     985           0 :       ecm->connect_uri = format (0, "%s%c", default_uri, 0);
     986             :     }
     987             : 
     988          10 :   if ((rv = parse_uri ((char *) ecm->connect_uri, &ecm->connect_sep)))
     989             :     {
     990           0 :       error = clib_error_return (0, "Uri parse error: %d", rv);
     991           0 :       goto cleanup;
     992             :     }
     993          10 :   ecm->transport_proto = ecm->connect_sep.transport_proto;
     994          10 :   ecm->is_dgram = (ecm->transport_proto == TRANSPORT_PROTO_UDP);
     995             : 
     996          10 :   if (ecm->prealloc_sessions)
     997           0 :     ec_prealloc_sessions (ecm);
     998             : 
     999          10 :   if ((error = ec_attach ()))
    1000             :     {
    1001           0 :       clib_error_report (error);
    1002           0 :       goto cleanup;
    1003             :     }
    1004             : 
    1005             :   /*
    1006             :    * Start. Fire off connect requests
    1007             :    */
    1008             : 
    1009          10 :   ecm->syn_start_time = vlib_time_now (vm);
    1010          10 :   ec_program_connects ();
    1011             : 
    1012             :   /*
    1013             :    * Park until the sessions come up, or syn_timeout seconds pass
    1014             :    */
    1015             : 
    1016          10 :   vlib_process_wait_for_event_or_clock (vm, ecm->syn_timeout);
    1017          10 :   event_type = vlib_process_get_events (vm, &event_data);
    1018          10 :   switch (event_type)
    1019             :     {
    1020           0 :     case ~0:
    1021           0 :       ec_cli ("Timeout with only %d sessions active...",
    1022             :               ecm->ready_connections);
    1023           0 :       error = clib_error_return (0, "failed: syn timeout with %d sessions",
    1024             :                                  ecm->ready_connections);
    1025           0 :       goto cleanup;
    1026             : 
    1027          10 :     case EC_CLI_CONNECTS_DONE:
    1028          10 :       delta = vlib_time_now (vm) - ecm->syn_start_time;
    1029          10 :       if (delta != 0.0)
    1030          10 :         ec_cli ("%d three-way handshakes in %.2f seconds %.2f/s",
    1031             :                 ecm->n_clients, delta, ((f64) ecm->n_clients) / delta);
    1032          10 :       break;
    1033             : 
    1034           0 :     case EC_CLI_CONNECTS_FAILED:
    1035           0 :       error = clib_error_return (0, "failed: connect returned");
    1036           0 :       goto cleanup;
    1037             : 
    1038           0 :     default:
    1039           0 :       ec_cli ("unexpected event(1): %d", event_type);
    1040           0 :       error = clib_error_return (0, "failed: unexpected event(1): %d",
    1041             :                                  event_type);
    1042           0 :       goto cleanup;
    1043             :     }
    1044             : 
    1045             :   /*
    1046             :    * Wait for the sessions to finish or test_timeout seconds pass
    1047             :    */
    1048          10 :   ecm->test_start_time = vlib_time_now (ecm->vlib_main);
    1049          10 :   ec_cli ("Test started at %.6f", ecm->test_start_time);
    1050          10 :   vlib_process_wait_for_event_or_clock (vm, ecm->test_timeout);
    1051          10 :   event_type = vlib_process_get_events (vm, &event_data);
    1052          10 :   switch (event_type)
    1053             :     {
    1054           0 :     case ~0:
    1055           0 :       ec_cli ("Timeout at %.6f with %d sessions still active...",
    1056             :               vlib_time_now (ecm->vlib_main), ecm->ready_connections);
    1057           0 :       error = clib_error_return (0, "failed: timeout with %d sessions",
    1058             :                                  ecm->ready_connections);
    1059           0 :       goto cleanup;
    1060             : 
    1061          10 :     case EC_CLI_TEST_DONE:
    1062          10 :       ecm->test_end_time = vlib_time_now (vm);
    1063          10 :       ec_cli ("Test finished at %.6f", ecm->test_end_time);
    1064          10 :       break;
    1065             : 
    1066           0 :     default:
    1067           0 :       ec_cli ("unexpected event(2): %d", event_type);
    1068           0 :       error = clib_error_return (0, "failed: unexpected event(2): %d",
    1069             :                                  event_type);
    1070           0 :       goto cleanup;
    1071             :     }
    1072             : 
    1073             :   /*
    1074             :    * Done. Compute stats
    1075             :    */
    1076          10 :   delta = ecm->test_end_time - ecm->test_start_time;
    1077          10 :   if (delta == 0.0)
    1078             :     {
    1079           0 :       ec_cli ("zero delta-t?");
    1080           0 :       error = clib_error_return (0, "failed: zero delta-t");
    1081           0 :       goto cleanup;
    1082             :     }
    1083             : 
    1084          10 :   total_bytes = (ecm->no_return ? ecm->tx_total : ecm->rx_total);
    1085          10 :   transfer_type = ecm->no_return ? "half-duplex" : "full-duplex";
    1086          10 :   ec_cli ("%lld bytes (%lld mbytes, %lld gbytes) in %.2f seconds", total_bytes,
    1087             :           total_bytes / (1ULL << 20), total_bytes / (1ULL << 30), delta);
    1088          10 :   ec_cli ("%.2f bytes/second %s", ((f64) total_bytes) / (delta),
    1089             :           transfer_type);
    1090          10 :   ec_cli ("%.4f gbit/second %s", (((f64) total_bytes * 8.0) / delta / 1e9),
    1091             :           transfer_type);
    1092             : 
    1093          10 :   if (ecm->test_bytes && ecm->test_failed)
    1094           0 :     error = clib_error_return (0, "failed: test bytes");
    1095             : 
    1096          10 : cleanup:
    1097             : 
    1098             :   /*
    1099             :    * Cleanup
    1100             :    */
    1101          10 :   ecm->run_test = EC_EXITING;
    1102          10 :   vlib_process_wait_for_event_or_clock (vm, 10e-3);
    1103             : 
    1104             :   /* Detach the application, so we can use different fifo sizes next time */
    1105          10 :   if (ec_detach ())
    1106             :     {
    1107           0 :       error = clib_error_return (0, "failed: app detach");
    1108           0 :       ec_cli ("WARNING: app detach failed...");
    1109             :     }
    1110             : 
    1111          10 :   ec_cleanup (ecm);
    1112          10 :   if (had_config)
    1113          10 :     unformat_free (line_input);
    1114             : 
    1115          10 :   if (error)
    1116           0 :     ec_cli ("test failed");
    1117             : 
    1118          10 :   return error;
    1119             : }
    1120             : 
    1121      203447 : VLIB_CLI_COMMAND (ec_command, static) = {
    1122             :   .path = "test echo clients",
    1123             :   .short_help =
    1124             :     "test echo clients [nclients %d][[m|g]bytes <bytes>]"
    1125             :     "[test-timeout <time>][syn-timeout <time>][no-return][fifo-size <size>]"
    1126             :     "[private-segment-count <count>][private-segment-size <bytes>[m|g]]"
    1127             :     "[preallocate-fifos][preallocate-sessions][client-batch <batch-size>]"
    1128             :     "[uri <tcp://ip/port>][test-bytes][no-output]",
    1129             :   .function = ec_command_fn,
    1130             :   .is_mp_safe = 1,
    1131             : };
    1132             : 
    1133             : clib_error_t *
    1134         559 : ec_main_init (vlib_main_t *vm)
    1135             : {
    1136         559 :   ec_main_t *ecm = &ec_main;
    1137         559 :   ecm->app_is_init = 0;
    1138         559 :   return 0;
    1139             : }
    1140             : 
    1141        1119 : VLIB_INIT_FUNCTION (ec_main_init);
    1142             : 
    1143             : /*
    1144             :  * fd.io coding-style-patch-verification: ON
    1145             :  *
    1146             :  * Local Variables:
    1147             :  * eval: (c-set-style "gnu")
    1148             :  * End:
    1149             :  */

Generated by: LCOV version 1.14