hsa: fix coverity issue CID-313635
[vpp.git] / src / plugins / hs_apps / vcl / vcl_test_client.c
index f0c626e..1962e81 100644 (file)
 #include <pthread.h>
 #include <signal.h>
 
-typedef struct
+typedef struct vtc_worker_ vcl_test_client_worker_t;
+typedef int (vtc_worker_run_fn) (vcl_test_client_worker_t *wrk);
+
+struct vtc_worker_
 {
   vcl_test_session_t *sessions;
   vcl_test_session_t *qsessions;
   uint32_t n_sessions;
   uint32_t wrk_index;
-  fd_set wr_fdset;
-  fd_set rd_fdset;
-  int max_fd_index;
+  union
+  {
+    struct
+    {
+      fd_set wr_fdset;
+      fd_set rd_fdset;
+      int max_fd_index;
+    };
+    struct
+    {
+      uint32_t epoll_sh;
+      struct epoll_event ep_evts[VCL_TEST_CFG_MAX_EPOLL_EVENTS];
+      vcl_test_session_t *next_to_send;
+    };
+  };
   pthread_t thread_handle;
+  vtc_worker_run_fn *wrk_run_fn;
   vcl_test_cfg_t cfg;
-} vcl_test_client_worker_t;
+};
 
 typedef struct
 {
@@ -121,14 +137,12 @@ vtc_cfg_sync (vcl_test_session_t * ts)
 }
 
 static int
-vtc_connect_test_sessions (vcl_test_client_worker_t * wrk)
+vtc_worker_alloc_sessions (vcl_test_client_worker_t *wrk)
 {
-  vcl_test_client_main_t *vcm = &vcl_client_main;
-  vcl_test_main_t *vt = &vcl_test_main;
-  const vcl_test_proto_vft_t *tp;
   vcl_test_session_t *ts;
   uint32_t n_test_sessions;
-  int i, rv;
+  struct timespec now;
+  int i, j;
 
   n_test_sessions = wrk->cfg.num_test_sessions;
   if (n_test_sessions < 1)
@@ -152,46 +166,18 @@ vtc_connect_test_sessions (vcl_test_client_worker_t * wrk)
       return errno;
     }
 
-  tp = vt->protos[vcm->proto];
+  clock_gettime (CLOCK_REALTIME, &now);
 
   for (i = 0; i < n_test_sessions; i++)
     {
       ts = &wrk->sessions[i];
       memset (ts, 0, sizeof (*ts));
       ts->session_index = i;
+      ts->old_stats.stop = now;
       ts->cfg = wrk->cfg;
       vcl_test_session_buf_alloc (ts);
-      rv = tp->open (&wrk->sessions[i], &vcm->server_endpt);
-      if (rv < 0)
-       return rv;
-    }
-  wrk->n_sessions = n_test_sessions;
 
-done:
-  vtinf ("All test sessions (%d) connected!", n_test_sessions);
-  return 0;
-}
-
-static int
-vtc_worker_test_setup (vcl_test_client_worker_t * wrk)
-{
-  vcl_test_cfg_t *cfg = &wrk->cfg;
-  vcl_test_session_t *ts;
-  struct timespec now;
-  uint32_t sidx;
-  int i, j;
-
-  FD_ZERO (&wrk->wr_fdset);
-  FD_ZERO (&wrk->rd_fdset);
-
-  clock_gettime (CLOCK_REALTIME, &now);
-
-  for (i = 0; i < cfg->num_test_sessions; i++)
-    {
-      ts = &wrk->sessions[i];
-      ts->old_stats.stop = now;
-
-      switch (cfg->test)
+      switch (ts->cfg.test)
        {
        case VCL_TEST_TYPE_UNI:
        case VCL_TEST_TYPE_BI:
@@ -201,13 +187,12 @@ vtc_worker_test_setup (vcl_test_client_worker_t * wrk)
        default:
          break;
        }
-
-      FD_SET (vppcom_session_index (ts->fd), &wrk->wr_fdset);
-      FD_SET (vppcom_session_index (ts->fd), &wrk->rd_fdset);
-      sidx = vppcom_session_index (ts->fd);
-      wrk->max_fd_index = vtc_max (sidx, wrk->max_fd_index);
     }
-  wrk->max_fd_index += 1;
+  wrk->n_sessions = n_test_sessions;
+
+done:
+
+  vtinf ("All test sessions (%d) initialized!", n_test_sessions);
 
   return 0;
 }
@@ -231,16 +216,13 @@ vtc_worker_init (vcl_test_client_worker_t * wrk)
        }
       vt_atomic_add (&vcm->active_workers, 1);
     }
-  rv = vtc_connect_test_sessions (wrk);
+  rv = vtc_worker_alloc_sessions (wrk);
   if (rv)
     {
-      vterr ("vtc_connect_test_sessions ()", rv);
+      vterr ("vtc_worker_alloc_sessions ()", rv);
       return rv;
     }
 
-  if (vtc_worker_test_setup (wrk))
-    return -1;
-
   return 0;
 }
 
@@ -312,32 +294,90 @@ vtc_inc_stats_check (vcl_test_session_t *ts)
     }
 }
 
-static void *
-vtc_worker_loop (void *arg)
+static void
+vtc_worker_start_transfer (vcl_test_client_worker_t *wrk)
+{
+  vtinf ("Worker %u starting transfer ...", wrk->wrk_index);
+
+  if (wrk->wrk_index == 0)
+    {
+      vcl_test_client_main_t *vcm = &vcl_client_main;
+      vcl_test_session_t *ctrl = &vcm->ctrl_session;
+
+      clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
+    }
+}
+
+static int
+vtc_session_check_is_done (vcl_test_session_t *ts, uint8_t check_rx)
+{
+  if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes) ||
+      (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
+    {
+      clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
+      ts->is_done = 1;
+      return 1;
+    }
+  return 0;
+}
+
+static int
+vtc_worker_connect_sessions_select (vcl_test_client_worker_t *wrk)
+{
+  vcl_test_client_main_t *vcm = &vcl_client_main;
+  vcl_test_main_t *vt = &vcl_test_main;
+  const vcl_test_proto_vft_t *tp;
+  vcl_test_session_t *ts;
+  uint32_t sidx;
+  int i, rv;
+
+  tp = vt->protos[vcm->proto];
+
+  FD_ZERO (&wrk->wr_fdset);
+  FD_ZERO (&wrk->rd_fdset);
+
+  for (i = 0; i < wrk->cfg.num_test_sessions; i++)
+    {
+      ts = &wrk->sessions[i];
+
+      rv = tp->open (&wrk->sessions[i], &vcm->server_endpt);
+      if (rv < 0)
+       return rv;
+
+      FD_SET (vppcom_session_index (ts->fd), &wrk->wr_fdset);
+      FD_SET (vppcom_session_index (ts->fd), &wrk->rd_fdset);
+      sidx = vppcom_session_index (ts->fd);
+      wrk->max_fd_index = vtc_max (sidx, wrk->max_fd_index);
+    }
+  wrk->max_fd_index += 1;
+
+  vtinf ("All test sessions (%d) connected!", wrk->cfg.num_test_sessions);
+
+  return 0;
+}
+
+static int
+vtc_worker_run_select (vcl_test_client_worker_t *wrk)
 {
   vcl_test_client_main_t *vcm = &vcl_client_main;
-  vcl_test_session_t *ctrl = &vcm->ctrl_session;
-  vcl_test_client_worker_t *wrk = arg;
-  uint32_t n_active_sessions;
   fd_set _wfdset, *wfdset = &_wfdset;
   fd_set _rfdset, *rfdset = &_rfdset;
+  uint32_t n_active_sessions;
   vcl_test_session_t *ts;
   int i, rv, check_rx = 0;
 
-  rv = vtc_worker_init (wrk);
+  rv = vtc_worker_connect_sessions_select (wrk);
   if (rv)
     {
-      vterr ("vtc_worker_init()", rv);
-      return 0;
+      vterr ("vtc_worker_connect_sessions()", rv);
+      return rv;
     }
 
-  vtinf ("Starting test ...");
-
-  if (wrk->wrk_index == 0)
-    clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
-
   check_rx = wrk->cfg.test != VCL_TEST_TYPE_UNI;
   n_active_sessions = wrk->cfg.num_test_sessions;
+
+  vtc_worker_start_transfer (wrk);
+
   while (n_active_sessions && vcm->test_running)
     {
       _wfdset = wrk->wr_fdset;
@@ -348,7 +388,7 @@ vtc_worker_loop (void *arg)
       if (rv < 0)
        {
          vterr ("vppcom_select()", rv);
-         goto exit;
+         break;
        }
       else if (rv == 0)
        continue;
@@ -359,29 +399,29 @@ vtc_worker_loop (void *arg)
          if (ts->is_done)
            continue;
 
-         if (FD_ISSET (vppcom_session_index (ts->fd), rfdset)
-             && ts->stats.rx_bytes < ts->cfg.total_bytes)
+         if (FD_ISSET (vppcom_session_index (ts->fd), rfdset) &&
+             ts->stats.rx_bytes < ts->cfg.total_bytes)
            {
              rv = ts->read (ts, ts->rxbuf, ts->rxbuf_size);
              if (rv < 0)
-               goto exit;
+               break;
            }
 
-         if (FD_ISSET (vppcom_session_index (ts->fd), wfdset)
-             && ts->stats.tx_bytes < ts->cfg.total_bytes)
+         if (FD_ISSET (vppcom_session_index (ts->fd), wfdset) &&
+             ts->stats.tx_bytes < ts->cfg.total_bytes)
            {
              rv = ts->write (ts, ts->txbuf, ts->cfg.txbuf_size);
              if (rv < 0)
                {
                  vtwrn ("vppcom_test_write (%d) failed -- aborting test",
                         ts->fd);
-                 goto exit;
+                 break;
                }
              if (vcm->incremental_stats)
                vtc_inc_stats_check (ts);
            }
-         if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes)
-             || (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
+         if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes) ||
+             (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
            {
              clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
              ts->is_done = 1;
@@ -389,13 +429,300 @@ vtc_worker_loop (void *arg)
            }
        }
     }
-exit:
+
+  return 0;
+}
+
+static void
+vtc_worker_epoll_send_add (vcl_test_client_worker_t *wrk,
+                          vcl_test_session_t *ts)
+{
+  if (!wrk->next_to_send)
+    {
+      wrk->next_to_send = ts;
+    }
+  else
+    {
+      ts->next = wrk->next_to_send;
+      wrk->next_to_send = ts->next;
+    }
+}
+
+static void
+vtc_worker_epoll_send_del (vcl_test_client_worker_t *wrk,
+                          vcl_test_session_t *ts, vcl_test_session_t *prev)
+{
+  if (!prev)
+    {
+      wrk->next_to_send = ts->next;
+    }
+  else
+    {
+      prev->next = ts->next;
+    }
+}
+
+static int
+vtc_worker_connect_sessions_epoll (vcl_test_client_worker_t *wrk)
+{
+  vcl_test_client_main_t *vcm = &vcl_client_main;
+  vcl_test_main_t *vt = &vcl_test_main;
+  const vcl_test_proto_vft_t *tp;
+  struct timespec start, end;
+  uint32_t n_connected = 0;
+  vcl_test_session_t *ts;
+  struct epoll_event ev;
+  int i, ci = 0, rv, n_ev;
+  double diff;
+
+  tp = vt->protos[vcm->proto];
+  wrk->epoll_sh = vppcom_epoll_create ();
+
+  ev.events = EPOLLET | EPOLLOUT;
+
+  clock_gettime (CLOCK_REALTIME, &start);
+
+  while (n_connected < wrk->cfg.num_test_sessions)
+    {
+      /*
+       * Try to connect more sessions if under pending threshold
+       */
+      while ((ci - n_connected) < 16 && ci < wrk->cfg.num_test_sessions)
+       {
+         ts = &wrk->sessions[ci];
+         ts->noblk_connect = 1;
+         rv = tp->open (&wrk->sessions[ci], &vcm->server_endpt);
+         if (rv < 0)
+           {
+             vtwrn ("open: %d", rv);
+             return rv;
+           }
+
+         ev.data.u64 = ci;
+         rv = vppcom_epoll_ctl (wrk->epoll_sh, EPOLL_CTL_ADD, ts->fd, &ev);
+         if (rv < 0)
+           {
+             vtwrn ("vppcom_epoll_ctl: %d", rv);
+             return rv;
+           }
+         ci += 1;
+       }
+
+      /*
+       * Handle connected events
+       */
+      n_ev =
+       vppcom_epoll_wait (wrk->epoll_sh, wrk->ep_evts,
+                          VCL_TEST_CFG_MAX_EPOLL_EVENTS, 0 /* timeout */);
+      if (n_ev < 0)
+       {
+         vterr ("vppcom_epoll_wait() returned", n_ev);
+         return -1;
+       }
+      else if (n_ev == 0)
+       {
+         continue;
+       }
+
+      for (i = 0; i < n_ev; i++)
+       {
+         ts = &wrk->sessions[wrk->ep_evts[i].data.u32];
+         if (!(wrk->ep_evts[i].events & EPOLLOUT))
+           {
+             vtwrn ("connect failed");
+             return -1;
+           }
+         if (ts->is_open)
+           {
+             vtwrn ("connection already open?");
+             return -1;
+           }
+         ts->is_open = 1;
+         n_connected += 1;
+       }
+    }
+
+  clock_gettime (CLOCK_REALTIME, &end);
+
+  diff = vcl_test_time_diff (&start, &end);
+  vtinf ("Connected (%u) connected in %.2f seconds (%u CPS)!",
+        wrk->cfg.num_test_sessions, diff,
+        (uint32_t) ((double) wrk->cfg.num_test_sessions / diff));
+
+  ev.events = EPOLLET | EPOLLIN | EPOLLOUT;
+
+  for (i = 0; i < wrk->cfg.num_test_sessions; i++)
+    {
+      ts = &wrk->sessions[i];
+
+      /* No data to be sent */
+      if (ts->cfg.total_bytes == 0)
+       {
+         n_connected -= 1;
+         clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
+         ts->is_done = 1;
+         continue;
+       }
+
+      ev.data.u64 = i;
+      rv = vppcom_epoll_ctl (wrk->epoll_sh, EPOLL_CTL_MOD, ts->fd, &ev);
+      if (rv < 0)
+       {
+         vtwrn ("vppcom_epoll_ctl: %d", rv);
+         return rv;
+       }
+      vtc_worker_epoll_send_add (wrk, ts);
+    }
+
+  return n_connected;
+}
+
+static int
+vtc_worker_run_epoll (vcl_test_client_worker_t *wrk)
+{
+  vcl_test_client_main_t *vcm = &vcl_client_main;
+  uint32_t n_active_sessions, max_writes = 16, n_writes = 0;
+  vcl_test_session_t *ts, *prev = 0;
+  int i, rv, check_rx = 0, n_ev;
+
+  rv = vtc_worker_connect_sessions_epoll (wrk);
+  if (rv < 0)
+    {
+      vterr ("vtc_worker_connect_sessions()", rv);
+      return rv;
+    }
+
+  n_active_sessions = rv;
+  check_rx = wrk->cfg.test != VCL_TEST_TYPE_UNI;
+
+  vtc_worker_start_transfer (wrk);
+  ts = wrk->next_to_send;
+
+  while (n_active_sessions && vcm->test_running)
+    {
+      /*
+       * Try to write
+       */
+      if (!ts)
+       {
+         ts = wrk->next_to_send;
+         if (!ts)
+           goto get_epoll_evts;
+       }
+
+      rv = ts->write (ts, ts->txbuf, ts->cfg.txbuf_size);
+      if (rv > 0)
+       {
+         if (vcm->incremental_stats)
+           vtc_inc_stats_check (ts);
+         if (vtc_session_check_is_done (ts, check_rx))
+           n_active_sessions -= 1;
+       }
+      else if (rv == 0)
+       {
+         vtc_worker_epoll_send_del (wrk, ts, prev);
+       }
+      else
+       {
+         vtwrn ("vppcom_test_write (%d) failed -- aborting test", ts->fd);
+         return -1;
+       }
+      prev = ts;
+      ts = ts->next;
+      n_writes += 1;
+
+      if (rv > 0 && n_writes < max_writes)
+       continue;
+
+    get_epoll_evts:
+
+      /*
+       * Grab new events
+       */
+
+      n_ev =
+       vppcom_epoll_wait (wrk->epoll_sh, wrk->ep_evts,
+                          VCL_TEST_CFG_MAX_EPOLL_EVENTS, 0 /* timeout */);
+      if (n_ev < 0)
+       {
+         vterr ("vppcom_epoll_wait()", n_ev);
+         break;
+       }
+      else if (n_ev == 0)
+       {
+         continue;
+       }
+
+      for (i = 0; i < n_ev; i++)
+       {
+         ts = &wrk->sessions[wrk->ep_evts[i].data.u32];
+
+         if (ts->is_done)
+           continue;
+
+         if (wrk->ep_evts[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))
+           {
+             vtinf ("%u finished before reading all data?", ts->fd);
+             break;
+           }
+         if ((wrk->ep_evts[i].events & EPOLLIN) &&
+             ts->stats.rx_bytes < ts->cfg.total_bytes)
+           {
+             rv = ts->read (ts, ts->rxbuf, ts->rxbuf_size);
+             if (rv < 0)
+               break;
+             if (vtc_session_check_is_done (ts, check_rx))
+               n_active_sessions -= 1;
+           }
+         if ((wrk->ep_evts[i].events & EPOLLOUT) &&
+             ts->stats.tx_bytes < ts->cfg.total_bytes)
+           {
+             vtc_worker_epoll_send_add (wrk, ts);
+           }
+       }
+
+      n_writes = 0;
+    }
+
+  return 0;
+}
+
+static inline int
+vtc_worker_run (vcl_test_client_worker_t *wrk)
+{
+  int rv;
+
+  vtinf ("Worker %u starting test ...", wrk->wrk_index);
+
+  rv = wrk->wrk_run_fn (wrk);
+
   vtinf ("Worker %d done ...", wrk->wrk_index);
+
+  return rv;
+}
+
+static void *
+vtc_worker_loop (void *arg)
+{
+  vcl_test_client_main_t *vcm = &vcl_client_main;
+  vcl_test_session_t *ctrl = &vcm->ctrl_session;
+  vcl_test_client_worker_t *wrk = arg;
+
+  if (vtc_worker_init (wrk))
+    goto done;
+
+  if (vtc_worker_run (wrk))
+    goto done;
+
   vtc_accumulate_stats (wrk, ctrl);
   sleep (VCL_TEST_DELAY_DISCONNECT);
   vtc_worker_sessions_exit (wrk);
+
+done:
+
   if (wrk->wrk_index)
     vt_atomic_add (&vcm->active_workers, -1);
+
   return 0;
 }
 
@@ -494,8 +821,12 @@ vtc_stream_client (vcl_test_client_main_t * vcm)
   for (i = 1; i < vcm->n_workers; i++)
     {
       wrk = &vcm->workers[i];
-      pthread_create (&wrk->thread_handle, NULL, vtc_worker_loop,
-                     (void *) wrk);
+      if (pthread_create (&wrk->thread_handle, NULL, vtc_worker_loop,
+                         (void *) wrk))
+       {
+         vtwrn ("pthread_create failed -- aborting!");
+         return;
+       }
     }
   vtc_worker_loop (&vcm->workers[0]);
 
@@ -686,6 +1017,7 @@ print_usage_and_exit (void)
     "  -T <txbuf-size>  Test Cfg: tx buffer size.\n"
     "  -U               Run Uni-directional test.\n"
     "  -B               Run Bi-directional test.\n"
+    "  -b <bytes>       Total number of bytes transferred\n"
     "  -V               Verbose mode.\n"
     "  -I <N>           Use N sessions.\n"
     "  -s <N>           Use N sessions.\n"
@@ -701,7 +1033,7 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
   int c, v;
 
   opterr = 0;
-  while ((c = getopt (argc, argv, "chnp:w:XE:I:N:R:T:UBV6DLs:q:S")) != -1)
+  while ((c = getopt (argc, argv, "chnp:w:xXE:I:N:R:T:b:UBV6DLs:q:S")) != -1)
     switch (c)
       {
       case 'c':
@@ -759,6 +1091,10 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
        vcm->post_test = VCL_TEST_TYPE_EXIT;
        break;
 
+      case 'x':
+       vcm->post_test = VCL_TEST_TYPE_NONE;
+       break;
+
       case 'E':
        if (strlen (optarg) > ctrl->txbuf_size)
          {
@@ -826,6 +1162,21 @@ vtc_process_opts (vcl_test_client_main_t * vcm, int argc, char **argv)
            print_usage_and_exit ();
          }
        break;
+      case 'b':
+       if (sscanf (optarg, "0x%lu", &ctrl->cfg.total_bytes) != 1)
+         if (sscanf (optarg, "%ld", &ctrl->cfg.total_bytes) != 1)
+           {
+             vtwrn ("Invalid value for option -%c!", c);
+             print_usage_and_exit ();
+           }
+       if (ctrl->cfg.total_bytes % ctrl->cfg.txbuf_size)
+         {
+           vtwrn ("total bytes must be mutliple of txbuf size(0x%lu)!",
+                  ctrl->cfg.txbuf_size);
+           print_usage_and_exit ();
+         }
+       ctrl->cfg.num_writes = ctrl->cfg.total_bytes / ctrl->cfg.txbuf_size;
+       break;
 
       case 'U':
        ctrl->cfg.test = VCL_TEST_TYPE_UNI;
@@ -947,6 +1298,10 @@ vtc_ctrl_session_exit (void)
   vcl_test_session_t *ctrl = &vcm->ctrl_session;
   int verbose = ctrl->cfg.verbose;
 
+  /* Only clients exits, server can accept new connections */
+  if (vcm->post_test == VCL_TEST_TYPE_EXIT_CLIENT)
+    return;
+
   ctrl->cfg.test = VCL_TEST_TYPE_EXIT;
   vtinf ("(fd %d): Sending exit cfg to server...", ctrl->fd);
   if (verbose)
@@ -1018,6 +1373,24 @@ vt_incercept_sigs (void)
     }
 }
 
+static void
+vtc_alloc_workers (vcl_test_client_main_t *vcm)
+{
+  vcl_test_main_t *vt = &vcl_test_main;
+  vtc_worker_run_fn *run_fn;
+
+  vcm->workers = calloc (vcm->n_workers, sizeof (vcl_test_client_worker_t));
+  vt->wrk = calloc (vcm->n_workers, sizeof (vcl_test_wrk_t));
+
+  if (vcm->ctrl_session.cfg.num_test_sessions > VCL_TEST_CFG_MAX_SELECT_SESS)
+    run_fn = vtc_worker_run_epoll;
+  else
+    run_fn = vtc_worker_run_select;
+
+  for (int i = 0; i < vcm->n_workers; i++)
+    vcm->workers[i].wrk_run_fn = run_fn;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -1027,13 +1400,14 @@ main (int argc, char **argv)
   int rv;
 
   vcm->n_workers = 1;
+  vcm->post_test = VCL_TEST_TYPE_EXIT_CLIENT;
+
   vcl_test_cfg_init (&ctrl->cfg);
+  vt_incercept_sigs ();
   vcl_test_session_buf_alloc (ctrl);
   vtc_process_opts (vcm, argc, argv);
-  vt_incercept_sigs ();
 
-  vcm->workers = calloc (vcm->n_workers, sizeof (vcl_test_client_worker_t));
-  vt->wrk = calloc (vcm->n_workers, sizeof (vcl_test_wrk_t));
+  vtc_alloc_workers (vcm);
 
   rv = vppcom_app_create ("vcl_test_client");
   if (rv < 0)
@@ -1041,7 +1415,11 @@ main (int argc, char **argv)
 
   /* Protos like tls/dtls/quic need init */
   if (vt->protos[vcm->proto]->init)
-    vt->protos[vcm->proto]->init (&ctrl->cfg);
+    {
+      rv = vt->protos[vcm->proto]->init (&ctrl->cfg);
+      if (rv)
+       vtfail ("client init failed", rv);
+    }
 
   if ((rv = vtc_ctrl_session_init (vcm, ctrl)))
     vtfail ("vppcom_session_create() ctrl session", rv);
@@ -1078,6 +1456,7 @@ main (int argc, char **argv)
       switch (vcm->post_test)
        {
        case VCL_TEST_TYPE_EXIT:
+       case VCL_TEST_TYPE_EXIT_CLIENT:
          switch (ctrl->cfg.test)
            {
            case VCL_TEST_TYPE_EXIT: