vcl: add support for reconnect
[vpp.git] / src / plugins / hs_apps / vcl / vcl_test_server.c
index a36801b..1b3b759 100644 (file)
@@ -58,6 +58,7 @@ typedef struct
   volatile int worker_fails;
   volatile int active_workers;
   u8 use_ds;
+  u8 incremental_stats;
 } vcl_test_server_main_t;
 
 vcl_test_main_t vcl_test_main;
@@ -101,10 +102,10 @@ again:
       if (!wrk->conn_pool[i].is_alloc)
        {
          conn = &wrk->conn_pool[i];
+         memset (conn, 0, sizeof (*conn));
          conn->endpt.ip = wrk->conn_pool[i].ip;
          conn->is_alloc = 1;
          conn->session_index = i;
-         memset (&conn->stats, 0, sizeof (vcl_test_stats_t));
          vcl_test_cfg_init (&conn->cfg);
          return (&wrk->conn_pool[i]);
        }
@@ -305,6 +306,48 @@ vts_server_echo (vcl_test_session_t *conn, int rx_bytes)
     vtinf ("(fd %d): TX (%d bytes) - '%s'", conn->fd, tx_bytes, conn->rxbuf);
 }
 
+static vcl_test_session_t *
+vts_accept_ctrl (vcl_test_server_worker_t *wrk, int listen_fd)
+{
+  vcl_test_server_main_t *vsm = &vcl_server_main;
+  const vcl_test_proto_vft_t *tp;
+  vcl_test_session_t *conn;
+  struct epoll_event ev;
+  int rv;
+
+  conn = conn_pool_alloc (wrk);
+  if (!conn)
+    {
+      vtwrn ("No free connections!");
+      return 0;
+    }
+
+  if (vsm->ctrl)
+    conn->cfg = vsm->ctrl->cfg;
+  vcl_test_session_buf_alloc (conn);
+  clock_gettime (CLOCK_REALTIME, &conn->old_stats.stop);
+
+  tp = vcl_test_main.protos[VPPCOM_PROTO_TCP];
+  if (tp->accept (listen_fd, conn))
+    return 0;
+
+  vtinf ("CTRL accepted fd = %d (0x%08x) on listener fd = %d (0x%08x)",
+        conn->fd, conn->fd, listen_fd, listen_fd);
+
+  ev.events = EPOLLET | EPOLLIN;
+  ev.data.u64 = conn - wrk->conn_pool;
+  rv = vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, conn->fd, &ev);
+  if (rv < 0)
+    {
+      vterr ("vppcom_epoll_ctl()", rv);
+      return 0;
+    }
+
+  wrk->nfds++;
+
+  return conn;
+}
+
 static vcl_test_session_t *
 vts_accept_client (vcl_test_server_worker_t *wrk, int listen_fd)
 {
@@ -324,6 +367,7 @@ vts_accept_client (vcl_test_server_worker_t *wrk, int listen_fd)
   if (vsm->ctrl)
     conn->cfg = vsm->ctrl->cfg;
   vcl_test_session_buf_alloc (conn);
+  clock_gettime (CLOCK_REALTIME, &conn->old_stats.stop);
 
   tp = vcl_test_main.protos[vsm->server_cfg.proto];
   if (tp->accept (listen_fd, conn))
@@ -332,7 +376,7 @@ vts_accept_client (vcl_test_server_worker_t *wrk, int listen_fd)
   vtinf ("Got a connection -- fd = %d (0x%08x) on listener fd = %d (0x%08x)",
         conn->fd, conn->fd, listen_fd, listen_fd);
 
-  ev.events = EPOLLIN;
+  ev.events = EPOLLET | EPOLLIN;
   ev.data.u64 = conn - wrk->conn_pool;
   rv = vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, conn->fd, &ev);
   if (rv < 0)
@@ -348,15 +392,15 @@ vts_accept_client (vcl_test_server_worker_t *wrk, int listen_fd)
 static void
 print_usage_and_exit (void)
 {
-  fprintf (stderr,
-          "vcl_test_server [OPTIONS] <port>\n"
-          "  OPTIONS\n"
-          "  -h               Print this message and exit.\n"
-          "  -6               Use IPv6\n"
-          "  -w <num>         Number of workers\n"
-          "  -p <PROTO>       Use <PROTO> transport layer\n"
-          "  -D               Use UDP transport layer\n"
-          "  -L               Use TLS transport layer\n");
+  fprintf (stderr, "vcl_test_server [OPTIONS] <port>\n"
+                  "  OPTIONS\n"
+                  "  -h               Print this message and exit.\n"
+                  "  -6               Use IPv6\n"
+                  "  -w <num>         Number of workers\n"
+                  "  -p <PROTO>       Use <PROTO> transport layer\n"
+                  "  -D               Use UDP transport layer\n"
+                  "  -L               Use TLS transport layer\n"
+                  "  -S               Incremental stats\n");
   exit (1);
 }
 
@@ -406,7 +450,7 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
   vsm->server_cfg.proto = VPPCOM_PROTO_TCP;
 
   opterr = 0;
-  while ((c = getopt (argc, argv, "6DLsw:hp:")) != -1)
+  while ((c = getopt (argc, argv, "6DLsw:hp:S")) != -1)
     switch (c)
       {
       case '6':
@@ -436,6 +480,9 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
       case 's':
        vsm->use_ds = 1;
        break;
+      case 'S':
+       vsm->incremental_stats = 1;
+       break;
       case '?':
        switch (optopt)
          {
@@ -455,18 +502,20 @@ vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
        print_usage_and_exit ();
       }
 
-  if (argc < (optind + 1))
+  if (argc > (optind + 1))
     {
-      fprintf (stderr, "SERVER: ERROR: Insufficient number of arguments!\n");
+      fprintf (stderr, "Incorrect number of arguments!\n");
       print_usage_and_exit ();
     }
-
-  if (sscanf (argv[optind], "%d", &v) == 1)
-    vsm->server_cfg.port = (uint16_t) v;
-  else
+  else if (argc > 1 && argc == (optind + 1))
     {
-      fprintf (stderr, "SERVER: ERROR: Invalid port (%s)!\n", argv[optind]);
-      print_usage_and_exit ();
+      if (sscanf (argv[optind], "%d", &v) == 1)
+       vsm->server_cfg.port = (uint16_t) v;
+      else
+       {
+         fprintf (stderr, "Invalid port (%s)!\n", argv[optind]);
+         print_usage_and_exit ();
+       }
     }
 
   vcl_test_init_endpoint_addr (vsm);
@@ -515,6 +564,7 @@ vts_handle_ctrl_cfg (vcl_test_server_worker_t *wrk, vcl_test_cfg_t *rx_cfg,
       wrk->nfds--;
       if (wrk->nfds)
        vts_wrk_cleanup_all (wrk);
+      vcl_server_main.ctrl = 0;
       break;
 
     default:
@@ -556,7 +606,7 @@ vts_worker_init (vcl_test_server_worker_t * wrk)
        vtfail ("vppcom_epoll_create()", wrk->epfd);
     }
 
-  listen_ev.events = EPOLLIN;
+  listen_ev.events = EPOLLET | EPOLLIN;
   listen_ev.data.u32 = VCL_TEST_DATA_LISTENER;
   rv =
     vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, wrk->listener.fd, &listen_ev);
@@ -578,6 +628,21 @@ vts_conn_read (vcl_test_session_t *conn)
     return conn->read (conn, conn->rxbuf, conn->rxbuf_size);
 }
 
+static void
+vts_inc_stats_check (vcl_test_session_t *ts)
+{
+  /* Avoid checking time too often because of syscall cost */
+  if (ts->stats.rx_bytes - ts->old_stats.rx_bytes < 1 << 20)
+    return;
+
+  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, 1 /* is_rx */);
+      ts->old_stats = ts->stats;
+    }
+}
+
 static void *
 vts_worker_loop (void *arg)
 {
@@ -613,13 +678,13 @@ vts_worker_loop (void *arg)
           */
          if (ep_evts[i].events & (EPOLLHUP | EPOLLRDHUP))
            {
-             vts_session_cleanup (conn);
-             wrk->nfds--;
-             if (!wrk->nfds)
+             if (conn == vsm->ctrl)
                {
-                 vtinf ("All client connections closed\n");
-                 goto done;
+                 vtinf ("ctrl session went away");
+                 vsm->ctrl = 0;
                }
+             vts_session_cleanup (conn);
+             wrk->nfds--;
              continue;
            }
 
@@ -634,7 +699,7 @@ vts_worker_loop (void *arg)
                  vtwrn ("ctrl already exists");
                  continue;
                }
-             vsm->ctrl = vts_accept_client (wrk, vsm->ctrl_listen_fd);
+             vsm->ctrl = vts_accept_ctrl (wrk, vsm->ctrl_listen_fd);
              continue;
            }
          if (ep_evts[i].data.u32 == VCL_TEST_DATA_LISTENER)
@@ -700,6 +765,8 @@ vts_worker_loop (void *arg)
              if (vppcom_session_attr (conn->fd, VPPCOM_ATTR_GET_NREAD, 0, 0) >
                  0)
                goto read_again;
+             if (vsm->incremental_stats)
+               vts_inc_stats_check (conn);
              continue;
            }
          else
@@ -747,7 +814,7 @@ vts_ctrl_session_init (vcl_test_server_worker_t *wrk)
   if (wrk->epfd < 0)
     vtfail ("vppcom_epoll_create()", wrk->epfd);
 
-  listen_ev.events = EPOLLIN;
+  listen_ev.events = EPOLLET | EPOLLIN;
   listen_ev.data.u32 = VCL_TEST_CTRL_LISTENER;
   rv = vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, vsm->ctrl_listen_fd,
                         &listen_ev);