LCOV - code coverage report
Current view: top level - plugins/hs_apps/vcl - vcl_test_client.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 336 728 46.2 %
Date: 2023-07-05 22:20:52 Functions: 19 34 55.9 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017-2021 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : #include <unistd.h>
      17             : #include <errno.h>
      18             : #include <stdlib.h>
      19             : #include <ctype.h>
      20             : #include <sys/types.h>
      21             : #include <sys/socket.h>
      22             : #include <stdio.h>
      23             : #include <time.h>
      24             : #include <arpa/inet.h>
      25             : #include <hs_apps/vcl/vcl_test.h>
      26             : #include <pthread.h>
      27             : #include <signal.h>
      28             : 
      29             : typedef struct vtc_worker_ vcl_test_client_worker_t;
      30             : typedef int (vtc_worker_run_fn) (vcl_test_client_worker_t *wrk);
      31             : 
      32             : struct vtc_worker_
      33             : {
      34             :   vcl_test_session_t *sessions;
      35             :   vcl_test_session_t *qsessions;
      36             :   uint32_t n_sessions;
      37             :   uint32_t wrk_index;
      38             :   union
      39             :   {
      40             :     struct
      41             :     {
      42             :       fd_set wr_fdset;
      43             :       fd_set rd_fdset;
      44             :       int max_fd_index;
      45             :     };
      46             :     struct
      47             :     {
      48             :       uint32_t epoll_sh;
      49             :       struct epoll_event ep_evts[VCL_TEST_CFG_MAX_EPOLL_EVENTS];
      50             :       vcl_test_session_t *next_to_send;
      51             :     };
      52             :   };
      53             :   pthread_t thread_handle;
      54             :   vtc_worker_run_fn *wrk_run_fn;
      55             :   vcl_test_cfg_t cfg;
      56             : };
      57             : 
      58             : typedef struct
      59             : {
      60             :   vcl_test_client_worker_t *workers;
      61             :   vcl_test_session_t ctrl_session;
      62             :   vppcom_endpt_t server_endpt;
      63             :   uint32_t cfg_seq_num;
      64             :   uint8_t dump_cfg;
      65             :   vcl_test_t post_test;
      66             :   uint8_t proto;
      67             :   uint8_t incremental_stats;
      68             :   uint32_t n_workers;
      69             :   volatile int active_workers;
      70             :   volatile int test_running;
      71             :   union
      72             :   {
      73             :     struct in_addr v4;
      74             :     struct in6_addr v6;
      75             :   } server_addr;
      76             : } vcl_test_client_main_t;
      77             : 
      78             : vcl_test_client_main_t vcl_client_main;
      79             : 
      80             : #define vtc_min(a, b) (a < b ? a : b)
      81             : #define vtc_max(a, b) (a > b ? a : b)
      82             : 
      83             : vcl_test_main_t vcl_test_main;
      84             : 
      85             : static int
      86          33 : vtc_cfg_sync (vcl_test_session_t * ts)
      87             : {
      88          33 :   vcl_test_cfg_t *rx_cfg = (vcl_test_cfg_t *) ts->rxbuf;
      89             :   int rx_bytes, tx_bytes;
      90             : 
      91          33 :   vt_atomic_add (&ts->cfg.seq_num, 1);
      92          33 :   if (ts->cfg.verbose)
      93             :     {
      94           0 :       vtinf ("(fd %d): Sending config to server.", ts->fd);
      95           0 :       vcl_test_cfg_dump (&ts->cfg, 1 /* is_client */ );
      96             :     }
      97          33 :   tx_bytes = ts->write (ts, &ts->cfg, sizeof (ts->cfg));
      98          33 :   if (tx_bytes < 0)
      99             :     {
     100           0 :       vtwrn ("(fd %d): write test cfg failed (%d)!", ts->fd, tx_bytes);
     101           0 :       return tx_bytes;
     102             :     }
     103             : 
     104          33 :   rx_bytes = ts->read (ts, ts->rxbuf, sizeof (vcl_test_cfg_t));
     105          33 :   if (rx_bytes < 0)
     106           0 :     return rx_bytes;
     107             : 
     108          33 :   if (rx_cfg->magic != VCL_TEST_CFG_CTRL_MAGIC)
     109             :     {
     110           0 :       vtwrn ("(fd %d): Bad server reply cfg -- aborting!", ts->fd);
     111           0 :       return -1;
     112             :     }
     113          33 :   if ((rx_bytes != sizeof (vcl_test_cfg_t))
     114          33 :       || !vcl_test_cfg_verify (rx_cfg, &ts->cfg))
     115             :     {
     116           0 :       vtwrn ("(fd %d): Invalid config received from server!", ts->fd);
     117           0 :       if (rx_bytes != sizeof (vcl_test_cfg_t))
     118             :         {
     119           0 :           vtinf ("\tRx bytes %d != cfg size %lu", rx_bytes,
     120             :                  sizeof (vcl_test_cfg_t));
     121             :         }
     122             :       else
     123             :         {
     124           0 :           vcl_test_cfg_dump (rx_cfg, 1 /* is_client */ );
     125           0 :           vtinf ("(fd %d): Valid config sent to server.", ts->fd);
     126           0 :           vcl_test_cfg_dump (&ts->cfg, 1 /* is_client */ );
     127             :         }
     128           0 :       return -1;
     129             :     }
     130          33 :   if (ts->cfg.verbose)
     131             :     {
     132           0 :       vtinf ("(fd %d): Got config back from server.", ts->fd);
     133           0 :       vcl_test_cfg_dump (rx_cfg, 1 /* is_client */ );
     134             :     }
     135             : 
     136          33 :   return 0;
     137             : }
     138             : 
     139             : static int
     140           7 : vtc_worker_alloc_sessions (vcl_test_client_worker_t *wrk)
     141             : {
     142             :   vcl_test_session_t *ts;
     143             :   uint32_t n_test_sessions;
     144             :   struct timespec now;
     145             :   int i, j;
     146             : 
     147           7 :   n_test_sessions = wrk->cfg.num_test_sessions;
     148           7 :   if (n_test_sessions < 1)
     149             :     {
     150           0 :       errno = EINVAL;
     151           0 :       return -1;
     152             :     }
     153             : 
     154           7 :   if (wrk->n_sessions >= n_test_sessions)
     155           0 :     goto done;
     156             : 
     157           7 :   if (wrk->n_sessions)
     158           0 :     wrk->sessions = realloc (wrk->sessions,
     159             :                              n_test_sessions * sizeof (vcl_test_session_t));
     160             :   else
     161           7 :     wrk->sessions = calloc (n_test_sessions, sizeof (vcl_test_session_t));
     162             : 
     163           7 :   if (!wrk->sessions)
     164             :     {
     165           0 :       vterr ("failed to alloc sessions", -errno);
     166           0 :       return errno;
     167             :     }
     168             : 
     169           7 :   clock_gettime (CLOCK_REALTIME, &now);
     170             : 
     171          21 :   for (i = 0; i < n_test_sessions; i++)
     172             :     {
     173          14 :       ts = &wrk->sessions[i];
     174          14 :       memset (ts, 0, sizeof (*ts));
     175          14 :       ts->session_index = i;
     176          14 :       ts->old_stats.stop = now;
     177          14 :       ts->cfg = wrk->cfg;
     178          14 :       vcl_test_session_buf_alloc (ts);
     179             : 
     180          14 :       switch (ts->cfg.test)
     181             :         {
     182          14 :         case VCL_TEST_TYPE_UNI:
     183             :         case VCL_TEST_TYPE_BI:
     184      107910 :           for (j = 0; j < ts->txbuf_size; j++)
     185      107896 :             ts->txbuf[j] = j & 0xff;
     186          14 :           break;
     187           0 :         default:
     188           0 :           break;
     189             :         }
     190             :     }
     191           7 :   wrk->n_sessions = n_test_sessions;
     192             : 
     193           7 : done:
     194             : 
     195           7 :   vtinf ("All test sessions (%d) initialized!", n_test_sessions);
     196             : 
     197           7 :   return 0;
     198             : }
     199             : 
     200             : static int
     201           7 : vtc_worker_init (vcl_test_client_worker_t * wrk)
     202             : {
     203           7 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     204             :   int rv;
     205             : 
     206           7 :   __wrk_index = wrk->wrk_index;
     207             : 
     208           7 :   vtinf ("Initializing worker %u ...", wrk->wrk_index);
     209             : 
     210           7 :   if (wrk->wrk_index)
     211             :     {
     212           0 :       if (vppcom_worker_register ())
     213             :         {
     214           0 :           vtwrn ("failed to register worker");
     215           0 :           return -1;
     216             :         }
     217           0 :       vt_atomic_add (&vcm->active_workers, 1);
     218             :     }
     219           7 :   rv = vtc_worker_alloc_sessions (wrk);
     220           7 :   if (rv)
     221             :     {
     222           0 :       vterr ("vtc_worker_alloc_sessions ()", rv);
     223           0 :       return rv;
     224             :     }
     225             : 
     226           7 :   return 0;
     227             : }
     228             : 
     229             : static int stats_lock = 0;
     230             : 
     231             : static void
     232           7 : vtc_accumulate_stats (vcl_test_client_worker_t * wrk,
     233             :                       vcl_test_session_t * ctrl)
     234             : {
     235             :   vcl_test_session_t *ts;
     236             :   static char buf[64];
     237           7 :   int i, show_rx = 0;
     238             : 
     239           7 :   while (__sync_lock_test_and_set (&stats_lock, 1))
     240             :     ;
     241             : 
     242           7 :   if (ctrl->cfg.test == VCL_TEST_TYPE_BI
     243           5 :       || ctrl->cfg.test == VCL_TEST_TYPE_ECHO)
     244           2 :     show_rx = 1;
     245             : 
     246          21 :   for (i = 0; i < wrk->cfg.num_test_sessions; i++)
     247             :     {
     248          14 :       ts = &wrk->sessions[i];
     249          14 :       ts->stats.start = ctrl->stats.start;
     250             : 
     251          14 :       if (ctrl->cfg.verbose > 1)
     252             :         {
     253           0 :           snprintf (buf, sizeof (buf), "CLIENT (fd %d) RESULTS", ts->fd);
     254           0 :           vcl_test_stats_dump (buf, &ts->stats, show_rx, 1 /* show tx */ ,
     255           0 :                                ctrl->cfg.verbose);
     256             :         }
     257             : 
     258          14 :       vcl_test_stats_accumulate (&ctrl->stats, &ts->stats);
     259          14 :       if (vcl_comp_tspec (&ctrl->stats.stop, &ts->stats.stop) < 0)
     260           9 :         ctrl->stats.stop = ts->stats.stop;
     261             :     }
     262             : 
     263           7 :   __sync_lock_release (&stats_lock);
     264           7 : }
     265             : 
     266             : static void
     267           7 : vtc_worker_sessions_exit (vcl_test_client_worker_t * wrk)
     268             : {
     269             :   vcl_test_session_t *ts;
     270             :   int i;
     271             : 
     272          21 :   for (i = 0; i < wrk->cfg.num_test_sessions; i++)
     273             :     {
     274          14 :       ts = &wrk->sessions[i];
     275          14 :       vppcom_session_close (ts->fd);
     276          14 :       vcl_test_session_buf_free (ts);
     277             :     }
     278             : 
     279           7 :   wrk->n_sessions = 0;
     280           7 : }
     281             : 
     282             : static void
     283           0 : vtc_inc_stats_check (vcl_test_session_t *ts)
     284             : {
     285             :   /* Avoid checking time too often because of syscall cost */
     286           0 :   if (ts->stats.tx_bytes - ts->old_stats.tx_bytes < 1 << 20)
     287           0 :     return;
     288             : 
     289           0 :   clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
     290           0 :   if (vcl_test_time_diff (&ts->old_stats.stop, &ts->stats.stop) > 1)
     291             :     {
     292           0 :       vcl_test_stats_dump_inc (ts, 0 /* is_rx */);
     293           0 :       ts->old_stats = ts->stats;
     294             :     }
     295             : }
     296             : 
     297             : static void
     298           7 : vtc_worker_start_transfer (vcl_test_client_worker_t *wrk)
     299             : {
     300           7 :   vtinf ("Worker %u starting transfer ...", wrk->wrk_index);
     301             : 
     302           7 :   if (wrk->wrk_index == 0)
     303             :     {
     304           7 :       vcl_test_client_main_t *vcm = &vcl_client_main;
     305           7 :       vcl_test_session_t *ctrl = &vcm->ctrl_session;
     306             : 
     307           7 :       clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
     308             :     }
     309           7 : }
     310             : 
     311             : static int
     312           0 : vtc_session_check_is_done (vcl_test_session_t *ts, uint8_t check_rx)
     313             : {
     314           0 :   if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes) ||
     315           0 :       (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
     316             :     {
     317           0 :       clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
     318           0 :       ts->is_done = 1;
     319           0 :       return 1;
     320             :     }
     321           0 :   return 0;
     322             : }
     323             : 
     324             : static int
     325           7 : vtc_worker_connect_sessions_select (vcl_test_client_worker_t *wrk)
     326             : {
     327           7 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     328           7 :   vcl_test_main_t *vt = &vcl_test_main;
     329             :   const vcl_test_proto_vft_t *tp;
     330             :   vcl_test_session_t *ts;
     331             :   uint32_t sidx;
     332             :   int i, rv;
     333             : 
     334           7 :   tp = vt->protos[vcm->proto];
     335             : 
     336           7 :   FD_ZERO (&wrk->wr_fdset);
     337           7 :   FD_ZERO (&wrk->rd_fdset);
     338             : 
     339          21 :   for (i = 0; i < wrk->cfg.num_test_sessions; i++)
     340             :     {
     341          14 :       ts = &wrk->sessions[i];
     342             : 
     343          14 :       rv = tp->open (&wrk->sessions[i], &vcm->server_endpt);
     344          14 :       if (rv < 0)
     345           0 :         return rv;
     346             : 
     347          14 :       FD_SET (vppcom_session_index (ts->fd), &wrk->wr_fdset);
     348          14 :       FD_SET (vppcom_session_index (ts->fd), &wrk->rd_fdset);
     349          14 :       sidx = vppcom_session_index (ts->fd);
     350          14 :       wrk->max_fd_index = vtc_max (sidx, wrk->max_fd_index);
     351             :     }
     352           7 :   wrk->max_fd_index += 1;
     353             : 
     354           7 :   vtinf ("All test sessions (%d) connected!", wrk->cfg.num_test_sessions);
     355             : 
     356           7 :   return 0;
     357             : }
     358             : 
     359             : static int
     360           7 : vtc_worker_run_select (vcl_test_client_worker_t *wrk)
     361             : {
     362           7 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     363           7 :   fd_set _wfdset, *wfdset = &_wfdset;
     364           7 :   fd_set _rfdset, *rfdset = &_rfdset;
     365             :   uint32_t n_active_sessions;
     366             :   vcl_test_session_t *ts;
     367           7 :   int i, rv, check_rx = 0;
     368             : 
     369           7 :   rv = vtc_worker_connect_sessions_select (wrk);
     370           7 :   if (rv)
     371             :     {
     372           0 :       vterr ("vtc_worker_connect_sessions()", rv);
     373           0 :       return rv;
     374             :     }
     375             : 
     376           7 :   check_rx = wrk->cfg.test != VCL_TEST_TYPE_UNI;
     377           7 :   n_active_sessions = wrk->cfg.num_test_sessions;
     378             : 
     379           7 :   vtc_worker_start_transfer (wrk);
     380             : 
     381      419184 :   while (n_active_sessions && vcm->test_running)
     382             :     {
     383      419177 :       _wfdset = wrk->wr_fdset;
     384      419177 :       _rfdset = wrk->rd_fdset;
     385             : 
     386      419177 :       rv = vppcom_select (wrk->max_fd_index, (unsigned long *) rfdset,
     387             :                           (unsigned long *) wfdset, NULL, 0);
     388      419177 :       if (rv < 0)
     389             :         {
     390           0 :           vterr ("vppcom_select()", rv);
     391           0 :           break;
     392             :         }
     393      419177 :       else if (rv == 0)
     394      365212 :         continue;
     395             : 
     396      170176 :       for (i = 0; i < wrk->cfg.num_test_sessions; i++)
     397             :         {
     398      116211 :           ts = &wrk->sessions[i];
     399      116211 :           if (ts->is_done)
     400       35946 :             continue;
     401             : 
     402       80265 :           if (FD_ISSET (vppcom_session_index (ts->fd), rfdset) &&
     403        5445 :               ts->stats.rx_bytes < ts->cfg.total_bytes)
     404             :             {
     405        5445 :               rv = ts->read (ts, ts->rxbuf, ts->rxbuf_size);
     406        5445 :               if (rv < 0)
     407           0 :                 break;
     408             :             }
     409             : 
     410       80265 :           if (FD_ISSET (vppcom_session_index (ts->fd), wfdset) &&
     411       36446 :               ts->stats.tx_bytes < ts->cfg.total_bytes)
     412             :             {
     413       14597 :               rv = ts->write (ts, ts->txbuf, ts->cfg.txbuf_size);
     414       14597 :               if (rv < 0)
     415             :                 {
     416           0 :                   vtwrn ("vppcom_test_write (%d) failed -- aborting test",
     417             :                          ts->fd);
     418           0 :                   break;
     419             :                 }
     420       14597 :               if (vcm->incremental_stats)
     421           0 :                 vtc_inc_stats_check (ts);
     422             :             }
     423       80265 :           if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes) ||
     424       63585 :               (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
     425             :             {
     426          14 :               clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
     427          14 :               ts->is_done = 1;
     428          14 :               n_active_sessions--;
     429             :             }
     430             :         }
     431             :     }
     432             : 
     433           7 :   return 0;
     434             : }
     435             : 
     436             : static void
     437           0 : vtc_worker_epoll_send_add (vcl_test_client_worker_t *wrk,
     438             :                            vcl_test_session_t *ts)
     439             : {
     440           0 :   if (!wrk->next_to_send)
     441             :     {
     442           0 :       wrk->next_to_send = ts;
     443             :     }
     444             :   else
     445             :     {
     446           0 :       ts->next = wrk->next_to_send;
     447           0 :       wrk->next_to_send = ts->next;
     448             :     }
     449           0 : }
     450             : 
     451             : static void
     452           0 : vtc_worker_epoll_send_del (vcl_test_client_worker_t *wrk,
     453             :                            vcl_test_session_t *ts, vcl_test_session_t *prev)
     454             : {
     455           0 :   if (!prev)
     456             :     {
     457           0 :       wrk->next_to_send = ts->next;
     458             :     }
     459             :   else
     460             :     {
     461           0 :       prev->next = ts->next;
     462             :     }
     463           0 : }
     464             : 
     465             : static int
     466           0 : vtc_worker_connect_sessions_epoll (vcl_test_client_worker_t *wrk)
     467             : {
     468           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     469           0 :   vcl_test_main_t *vt = &vcl_test_main;
     470             :   const vcl_test_proto_vft_t *tp;
     471             :   struct timespec start, end;
     472           0 :   uint32_t n_connected = 0;
     473             :   vcl_test_session_t *ts;
     474             :   struct epoll_event ev;
     475           0 :   int i, ci = 0, rv, n_ev;
     476             :   double diff;
     477             : 
     478           0 :   tp = vt->protos[vcm->proto];
     479           0 :   wrk->epoll_sh = vppcom_epoll_create ();
     480             : 
     481           0 :   ev.events = EPOLLET | EPOLLOUT;
     482             : 
     483           0 :   clock_gettime (CLOCK_REALTIME, &start);
     484             : 
     485           0 :   while (n_connected < wrk->cfg.num_test_sessions)
     486             :     {
     487             :       /*
     488             :        * Try to connect more sessions if under pending threshold
     489             :        */
     490           0 :       while ((ci - n_connected) < 16 && ci < wrk->cfg.num_test_sessions)
     491             :         {
     492           0 :           ts = &wrk->sessions[ci];
     493           0 :           ts->noblk_connect = 1;
     494           0 :           rv = tp->open (&wrk->sessions[ci], &vcm->server_endpt);
     495           0 :           if (rv < 0)
     496             :             {
     497           0 :               vtwrn ("open: %d", rv);
     498           0 :               return rv;
     499             :             }
     500             : 
     501           0 :           ev.data.u64 = ci;
     502           0 :           rv = vppcom_epoll_ctl (wrk->epoll_sh, EPOLL_CTL_ADD, ts->fd, &ev);
     503           0 :           if (rv < 0)
     504             :             {
     505           0 :               vtwrn ("vppcom_epoll_ctl: %d", rv);
     506           0 :               return rv;
     507             :             }
     508           0 :           ci += 1;
     509             :         }
     510             : 
     511             :       /*
     512             :        * Handle connected events
     513             :        */
     514             :       n_ev =
     515           0 :         vppcom_epoll_wait (wrk->epoll_sh, wrk->ep_evts,
     516             :                            VCL_TEST_CFG_MAX_EPOLL_EVENTS, 0 /* timeout */);
     517           0 :       if (n_ev < 0)
     518             :         {
     519           0 :           vterr ("vppcom_epoll_wait() returned", n_ev);
     520           0 :           return -1;
     521             :         }
     522           0 :       else if (n_ev == 0)
     523             :         {
     524           0 :           continue;
     525             :         }
     526             : 
     527           0 :       for (i = 0; i < n_ev; i++)
     528             :         {
     529           0 :           ts = &wrk->sessions[wrk->ep_evts[i].data.u32];
     530           0 :           if (!(wrk->ep_evts[i].events & EPOLLOUT))
     531             :             {
     532           0 :               vtwrn ("connect failed");
     533           0 :               return -1;
     534             :             }
     535           0 :           if (ts->is_open)
     536             :             {
     537           0 :               vtwrn ("connection already open?");
     538           0 :               return -1;
     539             :             }
     540           0 :           ts->is_open = 1;
     541           0 :           n_connected += 1;
     542             :         }
     543             :     }
     544             : 
     545           0 :   clock_gettime (CLOCK_REALTIME, &end);
     546             : 
     547           0 :   diff = vcl_test_time_diff (&start, &end);
     548           0 :   vtinf ("Connected (%u) connected in %.2f seconds (%u CPS)!",
     549             :          wrk->cfg.num_test_sessions, diff,
     550             :          (uint32_t) ((double) wrk->cfg.num_test_sessions / diff));
     551             : 
     552           0 :   ev.events = EPOLLET | EPOLLIN | EPOLLOUT;
     553             : 
     554           0 :   for (i = 0; i < wrk->cfg.num_test_sessions; i++)
     555             :     {
     556           0 :       ts = &wrk->sessions[i];
     557             : 
     558             :       /* No data to be sent */
     559           0 :       if (ts->cfg.total_bytes == 0)
     560             :         {
     561           0 :           n_connected -= 1;
     562           0 :           clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
     563           0 :           ts->is_done = 1;
     564           0 :           continue;
     565             :         }
     566             : 
     567           0 :       ev.data.u64 = i;
     568           0 :       rv = vppcom_epoll_ctl (wrk->epoll_sh, EPOLL_CTL_MOD, ts->fd, &ev);
     569           0 :       if (rv < 0)
     570             :         {
     571           0 :           vtwrn ("vppcom_epoll_ctl: %d", rv);
     572           0 :           return rv;
     573             :         }
     574           0 :       vtc_worker_epoll_send_add (wrk, ts);
     575             :     }
     576             : 
     577           0 :   return n_connected;
     578             : }
     579             : 
     580             : static int
     581           0 : vtc_worker_run_epoll (vcl_test_client_worker_t *wrk)
     582             : {
     583           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     584           0 :   uint32_t n_active_sessions, max_writes = 16, n_writes = 0;
     585           0 :   vcl_test_session_t *ts, *prev = 0;
     586           0 :   int i, rv, check_rx = 0, n_ev;
     587             : 
     588           0 :   rv = vtc_worker_connect_sessions_epoll (wrk);
     589           0 :   if (rv < 0)
     590             :     {
     591           0 :       vterr ("vtc_worker_connect_sessions()", rv);
     592           0 :       return rv;
     593             :     }
     594             : 
     595           0 :   n_active_sessions = rv;
     596           0 :   check_rx = wrk->cfg.test != VCL_TEST_TYPE_UNI;
     597             : 
     598           0 :   vtc_worker_start_transfer (wrk);
     599           0 :   ts = wrk->next_to_send;
     600             : 
     601           0 :   while (n_active_sessions && vcm->test_running)
     602             :     {
     603             :       /*
     604             :        * Try to write
     605             :        */
     606           0 :       if (!ts)
     607             :         {
     608           0 :           ts = wrk->next_to_send;
     609           0 :           if (!ts)
     610           0 :             goto get_epoll_evts;
     611             :         }
     612             : 
     613           0 :       rv = ts->write (ts, ts->txbuf, ts->cfg.txbuf_size);
     614           0 :       if (rv > 0)
     615             :         {
     616           0 :           if (vcm->incremental_stats)
     617           0 :             vtc_inc_stats_check (ts);
     618           0 :           if (vtc_session_check_is_done (ts, check_rx))
     619           0 :             n_active_sessions -= 1;
     620             :         }
     621           0 :       else if (rv == 0)
     622             :         {
     623           0 :           vtc_worker_epoll_send_del (wrk, ts, prev);
     624             :         }
     625             :       else
     626             :         {
     627           0 :           vtwrn ("vppcom_test_write (%d) failed -- aborting test", ts->fd);
     628           0 :           return -1;
     629             :         }
     630           0 :       prev = ts;
     631           0 :       ts = ts->next;
     632           0 :       n_writes += 1;
     633             : 
     634           0 :       if (rv > 0 && n_writes < max_writes)
     635           0 :         continue;
     636             : 
     637           0 :     get_epoll_evts:
     638             : 
     639             :       /*
     640             :        * Grab new events
     641             :        */
     642             : 
     643             :       n_ev =
     644           0 :         vppcom_epoll_wait (wrk->epoll_sh, wrk->ep_evts,
     645             :                            VCL_TEST_CFG_MAX_EPOLL_EVENTS, 0 /* timeout */);
     646           0 :       if (n_ev < 0)
     647             :         {
     648           0 :           vterr ("vppcom_epoll_wait()", n_ev);
     649           0 :           break;
     650             :         }
     651           0 :       else if (n_ev == 0)
     652             :         {
     653           0 :           continue;
     654             :         }
     655             : 
     656           0 :       for (i = 0; i < n_ev; i++)
     657             :         {
     658           0 :           ts = &wrk->sessions[wrk->ep_evts[i].data.u32];
     659             : 
     660           0 :           if (ts->is_done)
     661           0 :             continue;
     662             : 
     663           0 :           if (wrk->ep_evts[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))
     664             :             {
     665           0 :               vtinf ("%u finished before reading all data?", ts->fd);
     666           0 :               break;
     667             :             }
     668           0 :           if ((wrk->ep_evts[i].events & EPOLLIN) &&
     669           0 :               ts->stats.rx_bytes < ts->cfg.total_bytes)
     670             :             {
     671           0 :               rv = ts->read (ts, ts->rxbuf, ts->rxbuf_size);
     672           0 :               if (rv < 0)
     673           0 :                 break;
     674           0 :               if (vtc_session_check_is_done (ts, check_rx))
     675           0 :                 n_active_sessions -= 1;
     676             :             }
     677           0 :           if ((wrk->ep_evts[i].events & EPOLLOUT) &&
     678           0 :               ts->stats.tx_bytes < ts->cfg.total_bytes)
     679             :             {
     680           0 :               vtc_worker_epoll_send_add (wrk, ts);
     681             :             }
     682             :         }
     683             : 
     684           0 :       n_writes = 0;
     685             :     }
     686             : 
     687           0 :   return 0;
     688             : }
     689             : 
     690             : static inline int
     691           7 : vtc_worker_run (vcl_test_client_worker_t *wrk)
     692             : {
     693             :   int rv;
     694             : 
     695           7 :   vtinf ("Worker %u starting test ...", wrk->wrk_index);
     696             : 
     697           7 :   rv = wrk->wrk_run_fn (wrk);
     698             : 
     699           7 :   vtinf ("Worker %d done ...", wrk->wrk_index);
     700             : 
     701           7 :   return rv;
     702             : }
     703             : 
     704             : static void *
     705           7 : vtc_worker_loop (void *arg)
     706             : {
     707           7 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     708           7 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     709           7 :   vcl_test_client_worker_t *wrk = arg;
     710             : 
     711           7 :   if (vtc_worker_init (wrk))
     712           0 :     goto done;
     713             : 
     714           7 :   if (vtc_worker_run (wrk))
     715           0 :     goto done;
     716             : 
     717           7 :   vtc_accumulate_stats (wrk, ctrl);
     718           7 :   sleep (VCL_TEST_DELAY_DISCONNECT);
     719           7 :   vtc_worker_sessions_exit (wrk);
     720             : 
     721           7 : done:
     722             : 
     723           7 :   if (wrk->wrk_index)
     724           0 :     vt_atomic_add (&vcm->active_workers, -1);
     725             : 
     726           7 :   return 0;
     727             : }
     728             : 
     729             : static void
     730           7 : vtc_print_stats (vcl_test_session_t * ctrl)
     731             : {
     732           7 :   int is_echo = ctrl->cfg.test == VCL_TEST_TYPE_ECHO;
     733           7 :   int show_rx = 0;
     734             :   char buf[64];
     735             : 
     736           7 :   if (ctrl->cfg.test == VCL_TEST_TYPE_BI
     737           5 :       || ctrl->cfg.test == VCL_TEST_TYPE_ECHO)
     738           2 :     show_rx = 1;
     739             : 
     740           7 :   vcl_test_stats_dump ("CLIENT RESULTS", &ctrl->stats,
     741             :                        show_rx, 1 /* show tx */ ,
     742           7 :                        ctrl->cfg.verbose);
     743           7 :   vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
     744             : 
     745           7 :   if (ctrl->cfg.verbose)
     746             :     {
     747           0 :       vtinf ("  ctrl session info\n"
     748             :              VCL_TEST_SEPARATOR_STRING
     749             :              "          fd:  %d (0x%08x)\n"
     750             :              "       rxbuf:  %p\n"
     751             :              "  rxbuf size:  %u (0x%08x)\n"
     752             :              "       txbuf:  %p\n"
     753             :              "  txbuf size:  %u (0x%08x)\n"
     754             :              VCL_TEST_SEPARATOR_STRING,
     755             :              ctrl->fd, (uint32_t) ctrl->fd,
     756             :              ctrl->rxbuf, ctrl->rxbuf_size, ctrl->rxbuf_size,
     757             :              ctrl->txbuf, ctrl->txbuf_size, ctrl->txbuf_size);
     758             :     }
     759             : 
     760           7 :   if (is_echo)
     761           0 :     snprintf (buf, sizeof (buf), "Echo");
     762             :   else
     763           7 :     snprintf (buf, sizeof (buf), "%s-directional Stream",
     764           7 :               ctrl->cfg.test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
     765           7 : }
     766             : 
     767             : static void
     768           5 : vtc_echo_client (vcl_test_client_main_t * vcm)
     769             : {
     770           5 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     771           5 :   vcl_test_cfg_t *cfg = &ctrl->cfg;
     772             :   int rv;
     773             : 
     774           5 :   cfg->total_bytes = strlen (ctrl->txbuf) + 1;
     775           5 :   memset (&ctrl->stats, 0, sizeof (ctrl->stats));
     776             : 
     777           5 :   rv = ctrl->write (ctrl, ctrl->txbuf, cfg->total_bytes);
     778           5 :   if (rv < 0)
     779             :     {
     780           0 :       vtwrn ("vppcom_test_write (%d) failed ", ctrl->fd);
     781           0 :       return;
     782             :     }
     783             : 
     784           5 :   (void) ctrl->read (ctrl, ctrl->rxbuf, ctrl->rxbuf_size);
     785             : }
     786             : 
     787             : static void
     788           7 : vtc_stream_client (vcl_test_client_main_t * vcm)
     789             : {
     790           7 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     791           7 :   vcl_test_cfg_t *cfg = &ctrl->cfg;
     792             :   vcl_test_client_worker_t *wrk;
     793             :   uint32_t i, n_conn, n_conn_per_wrk;
     794             : 
     795           7 :   vtinf ("%s-directional Stream Test Starting!",
     796             :          ctrl->cfg.test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
     797             : 
     798           7 :   memset (&ctrl->stats, 0, sizeof (vcl_test_stats_t));
     799           7 :   cfg->total_bytes = cfg->num_writes * cfg->txbuf_size;
     800           7 :   cfg->ctrl_handle = ctrl->fd;
     801             : 
     802           7 :   n_conn = cfg->num_test_sessions;
     803           7 :   n_conn_per_wrk = n_conn / vcm->n_workers;
     804          14 :   for (i = 0; i < vcm->n_workers; i++)
     805             :     {
     806           7 :       wrk = &vcm->workers[i];
     807           7 :       wrk->wrk_index = i;
     808           7 :       wrk->cfg = ctrl->cfg;
     809           7 :       wrk->cfg.num_test_sessions = vtc_min (n_conn_per_wrk, n_conn);
     810           7 :       n_conn -= wrk->cfg.num_test_sessions;
     811             :     }
     812             : 
     813           7 :   vcm->test_running = 1;
     814           7 :   ctrl->cfg.cmd = VCL_TEST_CMD_START;
     815           7 :   if (vtc_cfg_sync (ctrl))
     816             :     {
     817           0 :       vtwrn ("test cfg sync failed -- aborting!");
     818           0 :       return;
     819             :     }
     820             : 
     821           7 :   for (i = 1; i < vcm->n_workers; i++)
     822             :     {
     823           0 :       wrk = &vcm->workers[i];
     824           0 :       pthread_create (&wrk->thread_handle, NULL, vtc_worker_loop,
     825             :                       (void *) wrk);
     826             :     }
     827           7 :   vtc_worker_loop (&vcm->workers[0]);
     828             : 
     829           7 :   while (vcm->active_workers > 0)
     830             :     ;
     831             : 
     832           7 :   vtinf ("Sending config on ctrl session (fd %d) for stats...", ctrl->fd);
     833           7 :   ctrl->cfg.cmd = VCL_TEST_CMD_STOP;
     834           7 :   if (vtc_cfg_sync (ctrl))
     835             :     {
     836           0 :       vtwrn ("test cfg sync failed -- aborting!");
     837           0 :       return;
     838             :     }
     839             : 
     840           7 :   vtc_print_stats (ctrl);
     841             : 
     842           7 :   ctrl->cfg.cmd = VCL_TEST_CMD_SYNC;
     843           7 :   ctrl->cfg.test = VCL_TEST_TYPE_ECHO;
     844           7 :   ctrl->cfg.total_bytes = 0;
     845           7 :   if (vtc_cfg_sync (ctrl))
     846           0 :     vtwrn ("post-test cfg sync failed!");
     847             : }
     848             : 
     849             : static void
     850           0 : cfg_txbuf_size_set (void)
     851             : {
     852           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     853           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     854           0 :   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_TXBUF_SIZE);
     855           0 :   uint64_t txbuf_size = strtoull ((const char *) p, NULL, 10);
     856             : 
     857           0 :   if (txbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
     858             :     {
     859           0 :       ctrl->cfg.txbuf_size = txbuf_size;
     860           0 :       ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
     861           0 :       vcl_test_buf_alloc (&ctrl->cfg, 0 /* is_rxbuf */ ,
     862           0 :                           (uint8_t **) & ctrl->txbuf, &ctrl->txbuf_size);
     863           0 :       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
     864             :     }
     865             :   else
     866           0 :     vtwrn ("Invalid txbuf size (%lu) < minimum buf size (%u)!",
     867             :            txbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
     868           0 : }
     869             : 
     870             : static void
     871           0 : cfg_num_writes_set (void)
     872             : {
     873           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     874           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     875           0 :   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_NUM_WRITES);
     876           0 :   uint32_t num_writes = strtoul ((const char *) p, NULL, 10);
     877             : 
     878           0 :   if (num_writes > 0)
     879             :     {
     880           0 :       ctrl->cfg.num_writes = num_writes;
     881           0 :       ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
     882           0 :       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
     883             :     }
     884             :   else
     885             :     {
     886           0 :       vtwrn ("invalid num writes: %u", num_writes);
     887             :     }
     888           0 : }
     889             : 
     890             : static void
     891           0 : cfg_num_test_sessions_set (void)
     892             : {
     893           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     894           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     895           0 :   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_NUM_TEST_SESS);
     896           0 :   uint32_t num_test_sessions = strtoul ((const char *) p, NULL, 10);
     897             : 
     898           0 :   if ((num_test_sessions > 0) &&
     899             :       (num_test_sessions <= VCL_TEST_CFG_MAX_TEST_SESS))
     900             :     {
     901           0 :       ctrl->cfg.num_test_sessions = num_test_sessions;
     902           0 :       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
     903             :     }
     904             :   else
     905             :     {
     906           0 :       vtwrn ("invalid num test sessions: %u, (%d max)",
     907             :              num_test_sessions, VCL_TEST_CFG_MAX_TEST_SESS);
     908             :     }
     909           0 : }
     910             : 
     911             : static void
     912           0 : cfg_rxbuf_size_set (void)
     913             : {
     914           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     915           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     916           0 :   char *p = ctrl->txbuf + strlen (VCL_TEST_TOKEN_RXBUF_SIZE);
     917           0 :   uint64_t rxbuf_size = strtoull ((const char *) p, NULL, 10);
     918             : 
     919           0 :   if (rxbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
     920             :     {
     921           0 :       ctrl->cfg.rxbuf_size = rxbuf_size;
     922           0 :       vcl_test_buf_alloc (&ctrl->cfg, 1 /* is_rxbuf */ ,
     923           0 :                           (uint8_t **) & ctrl->rxbuf, &ctrl->rxbuf_size);
     924           0 :       vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
     925             :     }
     926             :   else
     927           0 :     vtwrn ("Invalid rxbuf size (%lu) < minimum buf size (%u)!",
     928             :            rxbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
     929           0 : }
     930             : 
     931             : static void
     932           0 : cfg_verbose_toggle (void)
     933             : {
     934           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     935           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     936             : 
     937           0 :   ctrl->cfg.verbose = ctrl->cfg.verbose ? 0 : 1;
     938           0 :   vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
     939             : 
     940           0 : }
     941             : 
     942             : static vcl_test_t
     943           0 : parse_input ()
     944             : {
     945           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
     946           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
     947           0 :   vcl_test_t rv = VCL_TEST_TYPE_NONE;
     948             : 
     949           0 :   if (!strncmp (VCL_TEST_TOKEN_EXIT, ctrl->txbuf,
     950             :                 strlen (VCL_TEST_TOKEN_EXIT)))
     951           0 :     rv = VCL_TEST_TYPE_EXIT;
     952             : 
     953           0 :   else if (!strncmp (VCL_TEST_TOKEN_HELP, ctrl->txbuf,
     954             :                      strlen (VCL_TEST_TOKEN_HELP)))
     955           0 :     dump_help ();
     956             : 
     957           0 :   else if (!strncmp (VCL_TEST_TOKEN_SHOW_CFG, ctrl->txbuf,
     958             :                      strlen (VCL_TEST_TOKEN_SHOW_CFG)))
     959           0 :     vcm->dump_cfg = 1;
     960             : 
     961           0 :   else if (!strncmp (VCL_TEST_TOKEN_VERBOSE, ctrl->txbuf,
     962             :                      strlen (VCL_TEST_TOKEN_VERBOSE)))
     963           0 :     cfg_verbose_toggle ();
     964             : 
     965           0 :   else if (!strncmp (VCL_TEST_TOKEN_TXBUF_SIZE, ctrl->txbuf,
     966             :                      strlen (VCL_TEST_TOKEN_TXBUF_SIZE)))
     967           0 :     cfg_txbuf_size_set ();
     968             : 
     969           0 :   else if (!strncmp (VCL_TEST_TOKEN_NUM_TEST_SESS, ctrl->txbuf,
     970             :                      strlen (VCL_TEST_TOKEN_NUM_TEST_SESS)))
     971           0 :     cfg_num_test_sessions_set ();
     972             : 
     973           0 :   else if (!strncmp (VCL_TEST_TOKEN_NUM_WRITES, ctrl->txbuf,
     974             :                      strlen (VCL_TEST_TOKEN_NUM_WRITES)))
     975           0 :     cfg_num_writes_set ();
     976             : 
     977           0 :   else if (!strncmp (VCL_TEST_TOKEN_RXBUF_SIZE, ctrl->txbuf,
     978             :                      strlen (VCL_TEST_TOKEN_RXBUF_SIZE)))
     979           0 :     cfg_rxbuf_size_set ();
     980             : 
     981           0 :   else if (!strncmp (VCL_TEST_TOKEN_RUN_UNI, ctrl->txbuf,
     982             :                      strlen (VCL_TEST_TOKEN_RUN_UNI)))
     983           0 :     rv = ctrl->cfg.test = VCL_TEST_TYPE_UNI;
     984             : 
     985           0 :   else if (!strncmp (VCL_TEST_TOKEN_RUN_BI, ctrl->txbuf,
     986             :                      strlen (VCL_TEST_TOKEN_RUN_BI)))
     987           0 :     rv = ctrl->cfg.test = VCL_TEST_TYPE_BI;
     988             : 
     989             :   else
     990           0 :     rv = VCL_TEST_TYPE_ECHO;
     991             : 
     992           0 :   return rv;
     993             : }
     994             : 
     995             : void
     996           0 : print_usage_and_exit (void)
     997             : {
     998           0 :   fprintf (
     999             :     stderr,
    1000             :     "vcl_test_client [OPTIONS] <ipaddr> <port>\n"
    1001             :     "  OPTIONS\n"
    1002             :     "  -h               Print this message and exit.\n"
    1003             :     "  -6               Use IPv6\n"
    1004             :     "  -c               Print test config before test.\n"
    1005             :     "  -w <dir>         Write test results to <dir>.\n"
    1006             :     "  -X               Exit after running test.\n"
    1007             :     "  -p <proto>       Use <proto> transport layer\n"
    1008             :     "  -D               Use UDP transport layer\n"
    1009             :     "  -L               Use TLS transport layer\n"
    1010             :     "  -E               Run Echo test.\n"
    1011             :     "  -N <num-writes>  Test Cfg: number of writes.\n"
    1012             :     "  -R <rxbuf-size>  Test Cfg: rx buffer size.\n"
    1013             :     "  -T <txbuf-size>  Test Cfg: tx buffer size.\n"
    1014             :     "  -U               Run Uni-directional test.\n"
    1015             :     "  -B               Run Bi-directional test.\n"
    1016             :     "  -V               Verbose mode.\n"
    1017             :     "  -I <N>           Use N sessions.\n"
    1018             :     "  -s <N>           Use N sessions.\n"
    1019             :     "  -S          Print incremental stats per session.\n"
    1020             :     "  -q <n>           QUIC : use N Ssessions on top of n Qsessions\n");
    1021           0 :   exit (1);
    1022             : }
    1023             : 
    1024             : static void
    1025          12 : vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
    1026             : {
    1027          12 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
    1028             :   int c, v;
    1029             : 
    1030          12 :   opterr = 0;
    1031          53 :   while ((c = getopt (argc, argv, "chnp:w:xXE:I:N:R:T:UBV6DLs:q:S")) != -1)
    1032          41 :     switch (c)
    1033             :       {
    1034           0 :       case 'c':
    1035           0 :         vcm->dump_cfg = 1;
    1036           0 :         break;
    1037             : 
    1038           4 :       case 'I':         /* deprecated */
    1039             :       case 's':
    1040           4 :         if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions) != 1)
    1041           4 :           if (sscanf (optarg, "%u", &ctrl->cfg.num_test_sessions) != 1)
    1042             :             {
    1043           0 :               vtwrn ("Invalid value for option -%c!", c);
    1044           0 :               print_usage_and_exit ();
    1045             :             }
    1046           4 :         if (!ctrl->cfg.num_test_sessions ||
    1047           4 :             (ctrl->cfg.num_test_sessions > VCL_TEST_CFG_MAX_TEST_SESS))
    1048             :           {
    1049           0 :             vtwrn ("Invalid number of sessions (%d) specified for option -%c!"
    1050             :                    "\n       Valid range is 1 - %d",
    1051             :                    ctrl->cfg.num_test_sessions, c,
    1052             :                    VCL_TEST_CFG_MAX_TEST_SESS);
    1053           0 :             print_usage_and_exit ();
    1054             :           }
    1055           4 :         break;
    1056             : 
    1057           0 :       case 'q':
    1058           0 :         if (sscanf (optarg, "0x%x", &ctrl->cfg.num_test_sessions_perq) != 1)
    1059           0 :           if (sscanf (optarg, "%u", &ctrl->cfg.num_test_sessions_perq) != 1)
    1060             :             {
    1061           0 :               vtwrn ("Invalid value for option -%c!", c);
    1062           0 :               print_usage_and_exit ();
    1063             :             }
    1064           0 :         if (!ctrl->cfg.num_test_sessions_perq ||
    1065           0 :             (ctrl->cfg.num_test_sessions_perq > VCL_TEST_CFG_MAX_TEST_SESS))
    1066             :           {
    1067           0 :             vtwrn ("Invalid number of Stream sessions (%d) per Qsession"
    1068             :                    "for option -%c!\nValid range is 1 - %d",
    1069             :                    ctrl->cfg.num_test_sessions_perq, c,
    1070             :                    VCL_TEST_CFG_MAX_TEST_SESS);
    1071           0 :             print_usage_and_exit ();
    1072             :           }
    1073           0 :         break;
    1074             : 
    1075           0 :       case 'w':
    1076           0 :         if (sscanf (optarg, "%d", &v) != 1)
    1077             :           {
    1078           0 :             vtwrn ("Invalid value for option -%c!", c);
    1079           0 :             print_usage_and_exit ();
    1080             :           }
    1081           0 :         if (v > 1)
    1082           0 :           vcm->n_workers = v;
    1083           0 :         break;
    1084             : 
    1085          12 :       case 'X':
    1086          12 :         vcm->post_test = VCL_TEST_TYPE_EXIT;
    1087          12 :         break;
    1088             : 
    1089           0 :       case 'x':
    1090           0 :         vcm->post_test = VCL_TEST_TYPE_NONE;
    1091           0 :         break;
    1092             : 
    1093           5 :       case 'E':
    1094           5 :         if (strlen (optarg) > ctrl->txbuf_size)
    1095             :           {
    1096           0 :             vtwrn ("Option -%c value larger than txbuf size (%d)!",
    1097             :                    optopt, ctrl->txbuf_size);
    1098           0 :             print_usage_and_exit ();
    1099             :           }
    1100           5 :         strncpy (ctrl->txbuf, optarg, ctrl->txbuf_size);
    1101           5 :         ctrl->cfg.test = VCL_TEST_TYPE_ECHO;
    1102           5 :         break;
    1103             : 
    1104           7 :       case 'N':
    1105           7 :         if (sscanf (optarg, "0x%lx", &ctrl->cfg.num_writes) != 1)
    1106           7 :           if (sscanf (optarg, "%ld", &ctrl->cfg.num_writes) != 1)
    1107             :             {
    1108           0 :               vtwrn ("Invalid value for option -%c!", c);
    1109           0 :               print_usage_and_exit ();
    1110             :             }
    1111           7 :         ctrl->cfg.total_bytes = ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
    1112           7 :         break;
    1113             : 
    1114           0 :       case 'R':
    1115           0 :         if (sscanf (optarg, "0x%lx", &ctrl->cfg.rxbuf_size) != 1)
    1116           0 :           if (sscanf (optarg, "%ld", &ctrl->cfg.rxbuf_size) != 1)
    1117             :             {
    1118           0 :               vtwrn ("Invalid value for option -%c!", c);
    1119           0 :               print_usage_and_exit ();
    1120             :             }
    1121           0 :         if (ctrl->cfg.rxbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
    1122             :           {
    1123           0 :             ctrl->rxbuf_size = ctrl->cfg.rxbuf_size;
    1124           0 :             vcl_test_buf_alloc (&ctrl->cfg, 1 /* is_rxbuf */ ,
    1125           0 :                                 (uint8_t **) & ctrl->rxbuf,
    1126             :                                 &ctrl->rxbuf_size);
    1127             :           }
    1128             :         else
    1129             :           {
    1130           0 :             vtwrn ("rxbuf size (%lu) less than minumum (%u)",
    1131             :                    ctrl->cfg.rxbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
    1132           0 :             print_usage_and_exit ();
    1133             :           }
    1134             : 
    1135           0 :         break;
    1136             : 
    1137           1 :       case 'T':
    1138           1 :         if (sscanf (optarg, "0x%lx", &ctrl->cfg.txbuf_size) != 1)
    1139           1 :           if (sscanf (optarg, "%ld", &ctrl->cfg.txbuf_size) != 1)
    1140             :             {
    1141           0 :               vtwrn ("Invalid value for option -%c!", c);
    1142           0 :               print_usage_and_exit ();
    1143             :             }
    1144           1 :         if (ctrl->cfg.txbuf_size >= VCL_TEST_CFG_BUF_SIZE_MIN)
    1145             :           {
    1146           1 :             ctrl->txbuf_size = ctrl->cfg.txbuf_size;
    1147           1 :             vcl_test_buf_alloc (&ctrl->cfg, 0 /* is_rxbuf */ ,
    1148           1 :                                 (uint8_t **) & ctrl->txbuf,
    1149             :                                 &ctrl->txbuf_size);
    1150           1 :             ctrl->cfg.total_bytes =
    1151           1 :               ctrl->cfg.num_writes * ctrl->cfg.txbuf_size;
    1152             :           }
    1153             :         else
    1154             :           {
    1155           0 :             vtwrn ("txbuf size (%lu) less than minumum (%u)!",
    1156             :                    ctrl->cfg.txbuf_size, VCL_TEST_CFG_BUF_SIZE_MIN);
    1157           0 :             print_usage_and_exit ();
    1158             :           }
    1159           1 :         break;
    1160             : 
    1161           5 :       case 'U':
    1162           5 :         ctrl->cfg.test = VCL_TEST_TYPE_UNI;
    1163           5 :         break;
    1164             : 
    1165           2 :       case 'B':
    1166           2 :         ctrl->cfg.test = VCL_TEST_TYPE_BI;
    1167           2 :         break;
    1168             : 
    1169           0 :       case 'V':
    1170           0 :         ctrl->cfg.verbose = 1;
    1171           0 :         break;
    1172             : 
    1173           2 :       case '6':
    1174           2 :         ctrl->cfg.address_ip6 = 1;
    1175           2 :         break;
    1176             : 
    1177           1 :       case 'p':
    1178           1 :         if (vppcom_unformat_proto (&vcm->proto, optarg))
    1179           0 :           vtwrn ("Invalid vppcom protocol %s, defaulting to TCP", optarg);
    1180           1 :         break;
    1181             : 
    1182           0 :       case 'D':         /* deprecated */
    1183           0 :         vcm->proto = VPPCOM_PROTO_UDP;
    1184           0 :         break;
    1185             : 
    1186           2 :       case 'L':         /* deprecated */
    1187           2 :         vcm->proto = VPPCOM_PROTO_TLS;
    1188           2 :         break;
    1189             : 
    1190           0 :       case 'S':
    1191           0 :         vcm->incremental_stats = 1;
    1192           0 :         break;
    1193             : 
    1194           0 :       case '?':
    1195           0 :         switch (optopt)
    1196             :           {
    1197           0 :           case 'E':
    1198             :           case 'I':             /* deprecated */
    1199             :           case 'N':
    1200             :           case 'R':
    1201             :           case 'T':
    1202             :           case 'w':
    1203             :           case 'p':
    1204             :           case 'q':
    1205           0 :             vtwrn ("Option -%c requires an argument.", optopt);
    1206           0 :             break;
    1207             : 
    1208           0 :           default:
    1209           0 :             if (isprint (optopt))
    1210           0 :               vtwrn ("Unknown option `-%c'.", optopt);
    1211             :             else
    1212           0 :               vtwrn ("Unknown option character `\\x%x'.", optopt);
    1213             :           }
    1214             :         /* fall thru */
    1215             :       case 'h':
    1216             :       default:
    1217           0 :         print_usage_and_exit ();
    1218             :       }
    1219             : 
    1220          12 :   if (argc > (optind + 2))
    1221             :     {
    1222           0 :       vtwrn ("Invalid number of arguments!");
    1223           0 :       print_usage_and_exit ();
    1224             :     }
    1225             : 
    1226          12 :   ctrl->cfg.num_test_qsessions = vcm->proto != VPPCOM_PROTO_QUIC ? 0 :
    1227           0 :     (ctrl->cfg.num_test_sessions + ctrl->cfg.num_test_sessions_perq - 1) /
    1228           0 :     ctrl->cfg.num_test_sessions_perq;
    1229             : 
    1230          12 :   memset (&vcm->server_addr, 0, sizeof (vcm->server_addr));
    1231          12 :   if (ctrl->cfg.address_ip6)
    1232             :     {
    1233           2 :       struct in6_addr *in6 = &vcm->server_addr.v6;
    1234           2 :       inet_pton (AF_INET6, argv[optind++], in6);
    1235             : 
    1236           2 :       vcm->server_endpt.is_ip4 = 0;
    1237           2 :       vcm->server_endpt.ip = (uint8_t *) in6;
    1238             :     }
    1239             :   else
    1240             :     {
    1241          10 :       struct in_addr *in4 = &vcm->server_addr.v4;
    1242          10 :       inet_pton (AF_INET, argv[optind++], in4);
    1243             : 
    1244          10 :       vcm->server_endpt.is_ip4 = 1;
    1245          10 :       vcm->server_endpt.ip = (uint8_t *) in4;
    1246             :     }
    1247             : 
    1248          12 :   if (argc == optind + 1)
    1249          12 :     vcm->server_endpt.port = htons (atoi (argv[optind]));
    1250             :   else
    1251           0 :     vcm->server_endpt.port = htons (VCL_TEST_SERVER_PORT);
    1252          12 : }
    1253             : 
    1254             : static void
    1255           0 : vtc_read_user_input (vcl_test_session_t * ctrl)
    1256             : {
    1257           0 :   printf ("\nType some characters and hit <return>\n"
    1258             :           "('" VCL_TEST_TOKEN_HELP "' for help): ");
    1259             : 
    1260           0 :   if (fgets (ctrl->txbuf, ctrl->txbuf_size, stdin) != NULL)
    1261             :     {
    1262           0 :       if (strlen (ctrl->txbuf) == 1)
    1263             :         {
    1264           0 :           printf ("\nNothing to send!  Please try again...\n");
    1265           0 :           return;
    1266             :         }
    1267           0 :       ctrl->txbuf[strlen (ctrl->txbuf) - 1] = 0;  // chomp the newline.
    1268             : 
    1269             :       /* Parse input for keywords */
    1270           0 :       ctrl->cfg.test = parse_input ();
    1271             :     }
    1272             : }
    1273             : 
    1274             : static void
    1275          12 : vtc_ctrl_session_exit (void)
    1276             : {
    1277          12 :   vcl_test_client_main_t *vcm = &vcl_client_main;
    1278          12 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
    1279          12 :   int verbose = ctrl->cfg.verbose;
    1280             : 
    1281             :   /* Only clients exits, server can accept new connections */
    1282          12 :   if (vcm->post_test == VCL_TEST_TYPE_EXIT_CLIENT)
    1283           0 :     return;
    1284             : 
    1285          12 :   ctrl->cfg.test = VCL_TEST_TYPE_EXIT;
    1286          12 :   vtinf ("(fd %d): Sending exit cfg to server...", ctrl->fd);
    1287          12 :   if (verbose)
    1288           0 :     vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */);
    1289          12 :   (void) vcl_test_write (ctrl, (uint8_t *) &ctrl->cfg, sizeof (ctrl->cfg));
    1290          12 :   sleep (1);
    1291             : }
    1292             : 
    1293             : static int
    1294          12 : vtc_ctrl_session_init (vcl_test_client_main_t *vcm, vcl_test_session_t *ctrl)
    1295             : {
    1296             :   int rv;
    1297             : 
    1298          12 :   ctrl->fd = vppcom_session_create (VPPCOM_PROTO_TCP, 0 /* is_nonblocking */);
    1299          12 :   if (ctrl->fd < 0)
    1300             :     {
    1301           0 :       vterr ("vppcom_session_create()", ctrl->fd);
    1302           0 :       return ctrl->fd;
    1303             :     }
    1304             : 
    1305          12 :   vtinf ("Connecting to server...");
    1306          12 :   rv = vppcom_session_connect (ctrl->fd, &vcm->server_endpt);
    1307          12 :   if (rv)
    1308             :     {
    1309           0 :       vterr ("vppcom_session_connect()", rv);
    1310           0 :       return rv;
    1311             :     }
    1312          12 :   vtinf ("Control session (fd %d) connected.", ctrl->fd);
    1313             : 
    1314          12 :   ctrl->read = vcl_test_read;
    1315          12 :   ctrl->write = vcl_test_write;
    1316             : 
    1317          12 :   ctrl->cfg.cmd = VCL_TEST_CMD_SYNC;
    1318          12 :   rv = vtc_cfg_sync (ctrl);
    1319          12 :   if (rv)
    1320             :     {
    1321           0 :       vterr ("vtc_cfg_sync()", rv);
    1322           0 :       return rv;
    1323             :     }
    1324             : 
    1325          12 :   ctrl->cfg.ctrl_handle = ((vcl_test_cfg_t *) ctrl->rxbuf)->ctrl_handle;
    1326          12 :   memset (&ctrl->stats, 0, sizeof (ctrl->stats));
    1327             : 
    1328          12 :   return 0;
    1329             : }
    1330             : 
    1331             : static void
    1332           0 : vt_sigs_handler (int signum, siginfo_t *si, void *uc)
    1333             : {
    1334           0 :   vcl_test_client_main_t *vcm = &vcl_client_main;
    1335           0 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
    1336             : 
    1337           0 :   vcm->test_running = 0;
    1338           0 :   clock_gettime (CLOCK_REALTIME, &ctrl->stats.stop);
    1339           0 : }
    1340             : 
    1341             : static void
    1342          12 : vt_incercept_sigs (void)
    1343             : {
    1344             :   struct sigaction sa;
    1345             : 
    1346          12 :   memset (&sa, 0, sizeof (sa));
    1347          12 :   sa.sa_sigaction = vt_sigs_handler;
    1348          12 :   sa.sa_flags = SA_SIGINFO;
    1349          12 :   if (sigaction (SIGINT, &sa, 0))
    1350             :     {
    1351           0 :       vtwrn ("couldn't intercept sigint");
    1352           0 :       exit (-1);
    1353             :     }
    1354          12 : }
    1355             : 
    1356             : static void
    1357          12 : vtc_alloc_workers (vcl_test_client_main_t *vcm)
    1358             : {
    1359          12 :   vcl_test_main_t *vt = &vcl_test_main;
    1360             :   vtc_worker_run_fn *run_fn;
    1361             : 
    1362          12 :   vcm->workers = calloc (vcm->n_workers, sizeof (vcl_test_client_worker_t));
    1363          12 :   vt->wrk = calloc (vcm->n_workers, sizeof (vcl_test_wrk_t));
    1364             : 
    1365          12 :   if (vcm->ctrl_session.cfg.num_test_sessions > VCL_TEST_CFG_MAX_SELECT_SESS)
    1366           0 :     run_fn = vtc_worker_run_epoll;
    1367             :   else
    1368          12 :     run_fn = vtc_worker_run_select;
    1369             : 
    1370          24 :   for (int i = 0; i < vcm->n_workers; i++)
    1371          12 :     vcm->workers[i].wrk_run_fn = run_fn;
    1372          12 : }
    1373             : 
    1374             : int
    1375          12 : main (int argc, char **argv)
    1376             : {
    1377          12 :   vcl_test_client_main_t *vcm = &vcl_client_main;
    1378          12 :   vcl_test_session_t *ctrl = &vcm->ctrl_session;
    1379          12 :   vcl_test_main_t *vt = &vcl_test_main;
    1380             :   int rv;
    1381             : 
    1382          12 :   vcm->n_workers = 1;
    1383          12 :   vcm->post_test = VCL_TEST_TYPE_EXIT_CLIENT;
    1384             : 
    1385          12 :   vcl_test_cfg_init (&ctrl->cfg);
    1386          12 :   vt_incercept_sigs ();
    1387          12 :   vcl_test_session_buf_alloc (ctrl);
    1388          12 :   vtc_process_opts (vcm, argc, argv);
    1389             : 
    1390          12 :   vtc_alloc_workers (vcm);
    1391             : 
    1392          12 :   rv = vppcom_app_create ("vcl_test_client");
    1393          12 :   if (rv < 0)
    1394           0 :     vtfail ("vppcom_app_create()", rv);
    1395             : 
    1396             :   /* Protos like tls/dtls/quic need init */
    1397          12 :   if (vt->protos[vcm->proto]->init)
    1398             :     {
    1399           3 :       rv = vt->protos[vcm->proto]->init (&ctrl->cfg);
    1400           3 :       if (rv)
    1401           0 :         vtfail ("client init failed", rv);
    1402             :     }
    1403             : 
    1404          12 :   if ((rv = vtc_ctrl_session_init (vcm, ctrl)))
    1405           0 :     vtfail ("vppcom_session_create() ctrl session", rv);
    1406             : 
    1407             :   /* Update ctrl port to data port */
    1408          12 :   vcm->server_endpt.port += 1;
    1409             : 
    1410          24 :   while (ctrl->cfg.test != VCL_TEST_TYPE_EXIT)
    1411             :     {
    1412          12 :       if (vcm->dump_cfg)
    1413             :         {
    1414           0 :           vcl_test_cfg_dump (&ctrl->cfg, 1 /* is_client */ );
    1415           0 :           vcm->dump_cfg = 0;
    1416             :         }
    1417             : 
    1418          12 :       switch (ctrl->cfg.test)
    1419             :         {
    1420           5 :         case VCL_TEST_TYPE_ECHO:
    1421           5 :           vtc_echo_client (vcm);
    1422           5 :           break;
    1423             : 
    1424           7 :         case VCL_TEST_TYPE_UNI:
    1425             :         case VCL_TEST_TYPE_BI:
    1426           7 :           vtc_stream_client (vcm);
    1427           7 :           break;
    1428             : 
    1429           0 :         case VCL_TEST_TYPE_EXIT:
    1430           0 :           continue;
    1431             : 
    1432           0 :         case VCL_TEST_TYPE_NONE:
    1433             :         default:
    1434           0 :           break;
    1435             :         }
    1436          12 :       switch (vcm->post_test)
    1437             :         {
    1438          12 :         case VCL_TEST_TYPE_EXIT:
    1439             :         case VCL_TEST_TYPE_EXIT_CLIENT:
    1440          12 :           switch (ctrl->cfg.test)
    1441             :             {
    1442          12 :             case VCL_TEST_TYPE_EXIT:
    1443             :             case VCL_TEST_TYPE_UNI:
    1444             :             case VCL_TEST_TYPE_BI:
    1445             :             case VCL_TEST_TYPE_ECHO:
    1446          12 :               ctrl->cfg.test = VCL_TEST_TYPE_EXIT;
    1447          12 :               continue;
    1448             : 
    1449           0 :             case VCL_TEST_TYPE_NONE:
    1450             :             default:
    1451           0 :               break;
    1452             :             }
    1453           0 :           break;
    1454             : 
    1455           0 :         case VCL_TEST_TYPE_NONE:
    1456             :         case VCL_TEST_TYPE_ECHO:
    1457             :         case VCL_TEST_TYPE_UNI:
    1458             :         case VCL_TEST_TYPE_BI:
    1459             :         default:
    1460           0 :           break;
    1461             :         }
    1462             : 
    1463           0 :       memset (ctrl->txbuf, 0, ctrl->txbuf_size);
    1464           0 :       memset (ctrl->rxbuf, 0, ctrl->rxbuf_size);
    1465             : 
    1466           0 :       vtc_read_user_input (ctrl);
    1467             :     }
    1468             : 
    1469          12 :   vtc_ctrl_session_exit ();
    1470          12 :   vppcom_app_destroy ();
    1471          12 :   free (vcm->workers);
    1472          12 :   return 0;
    1473             : }
    1474             : 
    1475             : /*
    1476             :  * fd.io coding-style-patch-verification: ON
    1477             :  *
    1478             :  * Local Variables:
    1479             :  * eval: (c-set-style "gnu")
    1480             :  * End:
    1481             :  */

Generated by: LCOV version 1.14