#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
{
uint32_t n_workers;
volatile int active_workers;
volatile int test_running;
- struct sockaddr_storage server_addr;
+ union
+ {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } server_addr;
} vcl_test_client_main_t;
vcl_test_client_main_t vcl_client_main;
}
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)
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:
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;
}
}
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;
}
clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
if (vcl_test_time_diff (&ts->old_stats.stop, &ts->stats.stop) > 1)
{
- vcl_test_stats_dump_inc (&ts->old_stats, &ts->stats);
+ vcl_test_stats_dump_inc (ts, 0 /* is_rx */);
ts->old_stats = ts->stats;
}
}
-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;
if (rv < 0)
{
vterr ("vppcom_select()", rv);
- goto exit;
+ break;
}
else if (rv == 0)
continue;
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;
}
}
}
-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;
}
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]);
" -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"
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':
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)
{
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;
print_usage_and_exit ();
}
- if (argc < (optind + 2))
+ if (argc > (optind + 2))
{
- vtwrn ("Insufficient number of arguments!");
+ vtwrn ("Invalid number of arguments!");
print_usage_and_exit ();
}
memset (&vcm->server_addr, 0, sizeof (vcm->server_addr));
if (ctrl->cfg.address_ip6)
{
- struct sockaddr_in6 *sddr6 = (struct sockaddr_in6 *) &vcm->server_addr;
- sddr6->sin6_family = AF_INET6;
- inet_pton (AF_INET6, argv[optind++], &(sddr6->sin6_addr));
- sddr6->sin6_port = htons (atoi (argv[optind]));
+ struct in6_addr *in6 = &vcm->server_addr.v6;
+ inet_pton (AF_INET6, argv[optind++], in6);
vcm->server_endpt.is_ip4 = 0;
- vcm->server_endpt.ip = (uint8_t *) & sddr6->sin6_addr;
- vcm->server_endpt.port = (uint16_t) sddr6->sin6_port;
+ vcm->server_endpt.ip = (uint8_t *) in6;
}
else
{
- struct sockaddr_in *saddr4 = (struct sockaddr_in *) &vcm->server_addr;
- saddr4->sin_family = AF_INET;
- inet_pton (AF_INET, argv[optind++], &(saddr4->sin_addr));
- saddr4->sin_port = htons (atoi (argv[optind]));
+ struct in_addr *in4 = &vcm->server_addr.v4;
+ inet_pton (AF_INET, argv[optind++], in4);
vcm->server_endpt.is_ip4 = 1;
- vcm->server_endpt.ip = (uint8_t *) & saddr4->sin_addr;
- vcm->server_endpt.port = (uint16_t) saddr4->sin_port;
+ vcm->server_endpt.ip = (uint8_t *) in4;
}
+
+ if (argc == optind + 1)
+ vcm->server_endpt.port = htons (atoi (argv[optind]));
+ else
+ vcm->server_endpt.port = htons (VCL_TEST_SERVER_PORT);
}
static 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)
}
}
+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)
{
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)
/* 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);
switch (vcm->post_test)
{
case VCL_TEST_TYPE_EXIT:
+ case VCL_TEST_TYPE_EXIT_CLIENT:
switch (ctrl->cfg.test)
{
case VCL_TEST_TYPE_EXIT: