LCOV - code coverage report
Current view: top level - plugins/hs_apps/vcl - vcl_test_server.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 315 435 72.4 %
Date: 2023-07-05 22:20:52 Functions: 20 22 90.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 <sys/types.h>
      19             : #include <sys/socket.h>
      20             : #include <stdio.h>
      21             : #include <string.h>
      22             : #include <time.h>
      23             : #include <ctype.h>
      24             : #include <sys/stat.h>
      25             : #include <fcntl.h>
      26             : #include <hs_apps/vcl/vcl_test.h>
      27             : #include <sys/epoll.h>
      28             : #include <vppinfra/mem.h>
      29             : #include <pthread.h>
      30             : 
      31             : typedef struct
      32             : {
      33             :   uint16_t port;
      34             :   uint32_t address_ip6;
      35             :   u8 proto;
      36             :   u8 workers;
      37             :   vppcom_endpt_t endpt;
      38             : } vcl_test_server_cfg_t;
      39             : 
      40             : typedef struct
      41             : {
      42             :   vcl_test_session_t *conn_pool;
      43             :   uint32_t wrk_index;
      44             :   int epfd;
      45             :   int conn_pool_size;
      46             :   int nfds;
      47             :   vcl_test_session_t listener;
      48             :   pthread_t thread_handle;
      49             : } vcl_test_server_worker_t;
      50             : 
      51             : typedef struct
      52             : {
      53             :   vcl_test_server_worker_t *workers;
      54             :   vcl_test_session_t *ctrl;
      55             :   vcl_test_server_cfg_t server_cfg;
      56             :   int ctrl_listen_fd;
      57             :   struct sockaddr_storage servaddr;
      58             :   volatile int worker_fails;
      59             :   volatile int active_workers;
      60             :   u8 use_ds;
      61             :   u8 incremental_stats;
      62             : } vcl_test_server_main_t;
      63             : 
      64             : vcl_test_main_t vcl_test_main;
      65             : 
      66             : static vcl_test_server_main_t vcl_server_main;
      67             : 
      68             : static inline void
      69          12 : conn_pool_expand (vcl_test_server_worker_t * wrk, size_t expand_size)
      70             : {
      71             :   vcl_test_session_t *conn_pool;
      72          12 :   size_t new_size = wrk->conn_pool_size + expand_size;
      73             :   int i;
      74             : 
      75          12 :   conn_pool = realloc (wrk->conn_pool, new_size * sizeof (*wrk->conn_pool));
      76          12 :   if (conn_pool)
      77             :     {
      78        6168 :       for (i = wrk->conn_pool_size; i < new_size; i++)
      79             :         {
      80        6156 :           vcl_test_session_t *conn = &conn_pool[i];
      81        6156 :           memset (conn, 0, sizeof (*conn));
      82             :         }
      83             : 
      84          12 :       wrk->conn_pool = conn_pool;
      85          12 :       wrk->conn_pool_size = new_size;
      86             :     }
      87             :   else
      88             :     {
      89           0 :       vterr ("conn_pool_expand()", -errno);
      90             :     }
      91          12 : }
      92             : 
      93             : static inline vcl_test_session_t *
      94          26 : conn_pool_alloc (vcl_test_server_worker_t *wrk)
      95             : {
      96             :   vcl_test_session_t *conn;
      97          26 :   int i, expand = 0;
      98             : 
      99          26 : again:
     100          53 :   for (i = 0; i < wrk->conn_pool_size; i++)
     101             :     {
     102          53 :       if (!wrk->conn_pool[i].is_alloc)
     103             :         {
     104          26 :           conn = &wrk->conn_pool[i];
     105          26 :           memset (conn, 0, sizeof (*conn));
     106          26 :           conn->endpt.ip = wrk->conn_pool[i].ip;
     107          26 :           conn->is_alloc = 1;
     108          26 :           conn->session_index = i;
     109          26 :           vcl_test_cfg_init (&conn->cfg);
     110          26 :           return (&wrk->conn_pool[i]);
     111             :         }
     112             :     }
     113             : 
     114           0 :   if (expand == 0)
     115             :     {
     116           0 :       conn_pool_expand (wrk, 2 * wrk->conn_pool_size);
     117           0 :       expand = 1;
     118           0 :       goto again;
     119             :     }
     120           0 :   vtwrn ("Failed to allocate connection even after expand");
     121           0 :   return 0;
     122             : }
     123             : 
     124             : static inline void
     125        2591 : conn_pool_free (vcl_test_session_t *ts)
     126             : {
     127        2591 :   ts->fd = 0;
     128        2591 :   ts->is_alloc = 0;
     129        2591 :   vcl_test_session_buf_free (ts);
     130        2591 : }
     131             : 
     132             : static inline void
     133          33 : sync_config_and_reply (vcl_test_session_t *conn, vcl_test_cfg_t *rx_cfg)
     134             : {
     135          33 :   conn->cfg = *rx_cfg;
     136          33 :   vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */, (uint8_t **) &conn->rxbuf,
     137             :                       &conn->rxbuf_size);
     138          33 :   conn->cfg.txbuf_size = conn->cfg.rxbuf_size;
     139             : 
     140          33 :   if (conn->cfg.verbose)
     141             :     {
     142           0 :       vtinf ("(fd %d): Replying to cfg message!\n", conn->fd);
     143           0 :       vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
     144             :     }
     145          33 :   (void) vcl_test_write (conn, &conn->cfg, sizeof (conn->cfg));
     146          33 : }
     147             : 
     148             : static void
     149        2591 : vts_session_close (vcl_test_session_t *conn)
     150             : {
     151        2591 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     152        2591 :   vcl_test_main_t *vt = &vcl_test_main;
     153             : 
     154        2591 :   if (!conn->is_open)
     155        2565 :     return;
     156             : 
     157          26 :   if (vt->protos[vsm->server_cfg.proto]->close)
     158           0 :     vt->protos[vsm->server_cfg.proto]->close (conn);
     159             : 
     160          26 :   vppcom_session_close (conn->fd);
     161          26 :   conn->is_open = 0;
     162             : }
     163             : 
     164             : static void
     165        2591 : vts_session_cleanup (vcl_test_session_t *ts)
     166             : {
     167        2591 :   vts_session_close (ts);
     168        2591 :   conn_pool_free (ts);
     169        2591 : }
     170             : 
     171             : static void
     172           5 : vts_wrk_cleanup_all (vcl_test_server_worker_t *wrk)
     173             : {
     174             :   vcl_test_session_t *conn;
     175             :   int i;
     176             : 
     177        2570 :   for (i = 0; i < wrk->conn_pool_size; i++)
     178             :     {
     179        2565 :       conn = &wrk->conn_pool[i];
     180        2565 :       vts_session_cleanup (conn);
     181             :     }
     182             : 
     183           5 :   wrk->nfds = 0;
     184           5 : }
     185             : 
     186             : static void
     187          21 : vts_test_cmd (vcl_test_server_worker_t *wrk, vcl_test_session_t *conn,
     188             :               vcl_test_cfg_t *rx_cfg)
     189             : {
     190          21 :   u8 is_bi = rx_cfg->test == VCL_TEST_TYPE_BI;
     191             :   vcl_test_session_t *tc;
     192             :   char buf[64];
     193             :   int i;
     194             : 
     195          21 :   if (rx_cfg->cmd == VCL_TEST_CMD_STOP)
     196             :     {
     197             :       struct timespec stop;
     198           7 :       clock_gettime (CLOCK_REALTIME, &stop);
     199             : 
     200             :       /* Test session are not closed, e.g., connection-less or errors */
     201           7 :       if (wrk->nfds > 1)
     202             :         {
     203           5 :           vtinf ("%u sessions are still open", wrk->nfds - 1);
     204           5 :           stop.tv_sec -= VCL_TEST_DELAY_DISCONNECT;
     205           5 :           conn->stats.stop = stop;
     206             :         }
     207             : 
     208             :       /* Accumulate stats over all of the worker's sessions */
     209        3598 :       for (i = 0; i < wrk->conn_pool_size; i++)
     210             :         {
     211        3591 :           tc = &wrk->conn_pool[i];
     212        3591 :           if (tc == conn)
     213           7 :             continue;
     214             : 
     215        3584 :           vcl_test_stats_accumulate (&conn->stats, &tc->stats);
     216        3584 :           if (tc->is_open)
     217             :             {
     218           8 :               vts_session_cleanup (tc);
     219           8 :               continue;
     220             :             }
     221             :           /* Only relevant if all connections previously closed */
     222        3576 :           if (vcl_comp_tspec (&conn->stats.stop, &tc->stats.stop) < 0)
     223           5 :             conn->stats.stop = tc->stats.stop;
     224             :         }
     225             : 
     226           7 :       if (conn->cfg.verbose)
     227             :         {
     228           0 :           snprintf (buf, sizeof (buf), "SERVER (fd %d) RESULTS", conn->fd);
     229           0 :           vcl_test_stats_dump (buf, &conn->stats, 1 /* show_rx */,
     230           0 :                                is_bi /* show tx */, conn->cfg.verbose);
     231             :         }
     232             : 
     233           7 :       vcl_test_stats_dump ("SERVER RESULTS", &conn->stats, 1 /* show_rx */ ,
     234           7 :                            is_bi /* show_tx */ , conn->cfg.verbose);
     235           7 :       vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ );
     236           7 :       if (conn->cfg.verbose)
     237             :         {
     238           0 :           vtinf ("  vcl server main\n" VCL_TEST_SEPARATOR_STRING
     239             :                  "       buf:  %p\n"
     240             :                  "  buf size:  %u (0x%08x)\n" VCL_TEST_SEPARATOR_STRING,
     241             :                  conn->rxbuf, conn->rxbuf_size, conn->rxbuf_size);
     242             :         }
     243             : 
     244           7 :       sync_config_and_reply (conn, rx_cfg);
     245           7 :       memset (&conn->stats, 0, sizeof (conn->stats));
     246             :     }
     247          14 :   else if (rx_cfg->cmd == VCL_TEST_CMD_SYNC)
     248             :     {
     249           7 :       rx_cfg->ctrl_handle = conn->fd;
     250           7 :       vtinf ("Set control fd %d for test!", conn->fd);
     251           7 :       sync_config_and_reply (conn, rx_cfg);
     252             :     }
     253           7 :   else if (rx_cfg->cmd == VCL_TEST_CMD_START)
     254             :     {
     255           7 :       vtinf ("Starting %s-directional Stream Test (fd %d)!",
     256             :              is_bi ? "Bi" : "Uni", conn->fd);
     257           7 :       rx_cfg->ctrl_handle = conn->fd;
     258           7 :       sync_config_and_reply (conn, rx_cfg);
     259             : 
     260             :       /* read the 1st chunk, record start time */
     261           7 :       memset (&conn->stats, 0, sizeof (conn->stats));
     262           7 :       clock_gettime (CLOCK_REALTIME, &conn->stats.start);
     263             :     }
     264          21 : }
     265             : 
     266             : static inline void
     267        5886 : vts_server_process_rx (vcl_test_session_t *conn, int rx_bytes)
     268             : {
     269        5886 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     270             : 
     271        5886 :   if (conn->cfg.test == VCL_TEST_TYPE_BI)
     272             :     {
     273        1297 :       if (vsm->use_ds)
     274             :         {
     275           0 :           (void) vcl_test_write (conn, conn->ds[0].data, conn->ds[0].len);
     276           0 :           if (conn->ds[1].len)
     277           0 :             (void) vcl_test_write (conn, conn->ds[1].data, conn->ds[1].len);
     278             :         }
     279             :       else
     280        1297 :         (void) vcl_test_write (conn, conn->rxbuf, rx_bytes);
     281             :     }
     282             : 
     283        5886 :   if (vsm->use_ds)
     284           0 :     vppcom_session_free_segments (conn->fd, rx_bytes);
     285             : 
     286        5886 :   if (conn->stats.rx_bytes >= conn->cfg.total_bytes)
     287          22 :     clock_gettime (CLOCK_REALTIME, &conn->stats.stop);
     288        5886 : }
     289             : 
     290             : static void
     291           5 : vts_server_echo (vcl_test_session_t *conn, int rx_bytes)
     292             : {
     293             :   int tx_bytes, nbytes, pos;
     294             : 
     295             :   /* If it looks vaguely like a string, make sure it's terminated */
     296           5 :   pos = rx_bytes < conn->rxbuf_size ? rx_bytes : conn->rxbuf_size - 1;
     297           5 :   ((char *) conn->rxbuf)[pos] = 0;
     298           5 :   vtinf ("(fd %d): RX (%d bytes) - '%s'", conn->fd, rx_bytes, conn->rxbuf);
     299             : 
     300           5 :   if (conn->cfg.verbose)
     301           0 :     vtinf ("(fd %d): Echoing back", conn->fd);
     302             : 
     303           5 :   nbytes = strlen ((const char *) conn->rxbuf) + 1;
     304           5 :   tx_bytes = conn->write (conn, conn->rxbuf, nbytes);
     305           5 :   if (tx_bytes >= 0)
     306           5 :     vtinf ("(fd %d): TX (%d bytes) - '%s'", conn->fd, tx_bytes, conn->rxbuf);
     307           5 : }
     308             : 
     309             : static vcl_test_session_t *
     310          12 : vts_accept_ctrl (vcl_test_server_worker_t *wrk, int listen_fd)
     311             : {
     312          12 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     313             :   const vcl_test_proto_vft_t *tp;
     314             :   vcl_test_session_t *conn;
     315             :   struct epoll_event ev;
     316             :   int rv;
     317             : 
     318          12 :   conn = conn_pool_alloc (wrk);
     319          12 :   if (!conn)
     320             :     {
     321           0 :       vtwrn ("No free connections!");
     322           0 :       return 0;
     323             :     }
     324             : 
     325          12 :   if (vsm->ctrl)
     326           0 :     conn->cfg = vsm->ctrl->cfg;
     327          12 :   vcl_test_session_buf_alloc (conn);
     328          12 :   clock_gettime (CLOCK_REALTIME, &conn->old_stats.stop);
     329             : 
     330          12 :   tp = vcl_test_main.protos[VPPCOM_PROTO_TCP];
     331          12 :   if (tp->accept (listen_fd, conn))
     332           0 :     return 0;
     333             : 
     334          12 :   vtinf ("CTRL accepted fd = %d (0x%08x) on listener fd = %d (0x%08x)",
     335             :          conn->fd, conn->fd, listen_fd, listen_fd);
     336             : 
     337          12 :   ev.events = EPOLLET | EPOLLIN;
     338          12 :   ev.data.u64 = conn - wrk->conn_pool;
     339          12 :   rv = vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, conn->fd, &ev);
     340          12 :   if (rv < 0)
     341             :     {
     342           0 :       vterr ("vppcom_epoll_ctl()", rv);
     343           0 :       return 0;
     344             :     }
     345             : 
     346          12 :   wrk->nfds++;
     347             : 
     348          12 :   return conn;
     349             : }
     350             : 
     351             : static vcl_test_session_t *
     352          14 : vts_accept_client (vcl_test_server_worker_t *wrk, int listen_fd)
     353             : {
     354          14 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     355             :   const vcl_test_proto_vft_t *tp;
     356             :   vcl_test_session_t *conn;
     357             :   struct epoll_event ev;
     358             :   int rv;
     359             : 
     360          14 :   conn = conn_pool_alloc (wrk);
     361          14 :   if (!conn)
     362             :     {
     363           0 :       vtwrn ("No free connections!");
     364           0 :       return 0;
     365             :     }
     366             : 
     367          14 :   if (vsm->ctrl)
     368          14 :     conn->cfg = vsm->ctrl->cfg;
     369          14 :   vcl_test_session_buf_alloc (conn);
     370          14 :   clock_gettime (CLOCK_REALTIME, &conn->old_stats.stop);
     371             : 
     372          14 :   tp = vcl_test_main.protos[vsm->server_cfg.proto];
     373          14 :   if (tp->accept (listen_fd, conn))
     374           0 :     return 0;
     375             : 
     376          14 :   if (conn->cfg.num_test_sessions < VCL_TEST_CFG_MAX_SELECT_SESS)
     377          14 :     vtinf ("Got a connection -- fd = %d (0x%08x) on listener fd = %d (0x%08x)",
     378             :            conn->fd, conn->fd, listen_fd, listen_fd);
     379             : 
     380          14 :   ev.events = EPOLLET | EPOLLIN;
     381          14 :   ev.data.u64 = conn - wrk->conn_pool;
     382          14 :   rv = vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, conn->fd, &ev);
     383          14 :   if (rv < 0)
     384             :     {
     385           0 :       vterr ("vppcom_epoll_ctl()", rv);
     386           0 :       return 0;
     387             :     }
     388          14 :   wrk->nfds++;
     389             : 
     390          14 :   return conn;
     391             : }
     392             : 
     393             : static void
     394           0 : print_usage_and_exit (void)
     395             : {
     396           0 :   fprintf (stderr, "vcl_test_server [OPTIONS] <port>\n"
     397             :                    "  OPTIONS\n"
     398             :                    "  -h               Print this message and exit.\n"
     399             :                    "  -6               Use IPv6\n"
     400             :                    "  -w <num>         Number of workers\n"
     401             :                    "  -p <PROTO>       Use <PROTO> transport layer\n"
     402             :                    "  -D               Use UDP transport layer\n"
     403             :                    "  -L               Use TLS transport layer\n"
     404             :                    "  -S          Incremental stats\n");
     405           0 :   exit (1);
     406             : }
     407             : 
     408             : static void
     409          12 : vcl_test_init_endpoint_addr (vcl_test_server_main_t * vsm)
     410             : {
     411          12 :   struct sockaddr_storage *servaddr = &vsm->servaddr;
     412          12 :   memset (servaddr, 0, sizeof (*servaddr));
     413             : 
     414          12 :   if (vsm->server_cfg.address_ip6)
     415             :     {
     416           2 :       struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) servaddr;
     417           2 :       server_addr->sin6_family = AF_INET6;
     418           2 :       server_addr->sin6_addr = in6addr_any;
     419           2 :       server_addr->sin6_port = htons (vsm->server_cfg.port);
     420             :     }
     421             :   else
     422             :     {
     423          10 :       struct sockaddr_in *server_addr = (struct sockaddr_in *) servaddr;
     424          10 :       server_addr->sin_family = AF_INET;
     425          10 :       server_addr->sin_addr.s_addr = htonl (INADDR_ANY);
     426          10 :       server_addr->sin_port = htons (vsm->server_cfg.port);
     427             :     }
     428             : 
     429          12 :   if (vsm->server_cfg.address_ip6)
     430             :     {
     431           2 :       struct sockaddr_in6 *server_addr = (struct sockaddr_in6 *) servaddr;
     432           2 :       vsm->server_cfg.endpt.is_ip4 = 0;
     433           2 :       vsm->server_cfg.endpt.ip = (uint8_t *) &server_addr->sin6_addr;
     434           2 :       vsm->server_cfg.endpt.port = (uint16_t) server_addr->sin6_port;
     435             :     }
     436             :   else
     437             :     {
     438          10 :       struct sockaddr_in *server_addr = (struct sockaddr_in *) servaddr;
     439          10 :       vsm->server_cfg.endpt.is_ip4 = 1;
     440          10 :       vsm->server_cfg.endpt.ip = (uint8_t *) &server_addr->sin_addr;
     441          10 :       vsm->server_cfg.endpt.port = (uint16_t) server_addr->sin_port;
     442             :     }
     443          12 : }
     444             : 
     445             : static void
     446          12 : vcl_test_server_process_opts (vcl_test_server_main_t * vsm, int argc,
     447             :                               char **argv)
     448             : {
     449             :   int v, c;
     450             : 
     451          12 :   vsm->server_cfg.proto = VPPCOM_PROTO_TCP;
     452             : 
     453          12 :   opterr = 0;
     454          17 :   while ((c = getopt (argc, argv, "6DLsw:hp:S")) != -1)
     455           5 :     switch (c)
     456             :       {
     457           2 :       case '6':
     458           2 :         vsm->server_cfg.address_ip6 = 1;
     459           2 :         break;
     460             : 
     461           1 :       case 'p':
     462           1 :         if (vppcom_unformat_proto (&vsm->server_cfg.proto, optarg))
     463           0 :           vtwrn ("Invalid vppcom protocol %s, defaulting to TCP", optarg);
     464           1 :         break;
     465             : 
     466           0 :       case 'D':
     467           0 :         vsm->server_cfg.proto = VPPCOM_PROTO_UDP;
     468           0 :         break;
     469             : 
     470           2 :       case 'L':
     471           2 :         vsm->server_cfg.proto = VPPCOM_PROTO_TLS;
     472           2 :         break;
     473             : 
     474           0 :       case 'w':
     475           0 :         v = atoi (optarg);
     476           0 :         if (v > 1)
     477           0 :           vsm->server_cfg.workers = v;
     478             :         else
     479           0 :           vtwrn ("Invalid number of workers %d", v);
     480           0 :         break;
     481           0 :       case 's':
     482           0 :         vsm->use_ds = 1;
     483           0 :         break;
     484           0 :       case 'S':
     485           0 :         vsm->incremental_stats = 1;
     486           0 :         break;
     487           0 :       case '?':
     488           0 :         switch (optopt)
     489             :           {
     490           0 :           case 'w':
     491             :           case 'p':
     492           0 :             vtwrn ("Option `-%c' requires an argument.", optopt);
     493           0 :             break;
     494           0 :           default:
     495           0 :             if (isprint (optopt))
     496           0 :               vtwrn ("Unknown option `-%c'.", optopt);
     497             :             else
     498           0 :               vtwrn ("Unknown option character `\\x%x'.", optopt);
     499             :           }
     500             :         /* fall thru */
     501             :       case 'h':
     502             :       default:
     503           0 :         print_usage_and_exit ();
     504             :       }
     505             : 
     506          12 :   if (argc > (optind + 1))
     507             :     {
     508           0 :       fprintf (stderr, "Incorrect number of arguments!\n");
     509           0 :       print_usage_and_exit ();
     510             :     }
     511          12 :   else if (argc > 1 && argc == (optind + 1))
     512             :     {
     513          12 :       if (sscanf (argv[optind], "%d", &v) == 1)
     514          12 :         vsm->server_cfg.port = (uint16_t) v;
     515             :       else
     516             :         {
     517           0 :           fprintf (stderr, "Invalid port (%s)!\n", argv[optind]);
     518           0 :           print_usage_and_exit ();
     519             :         }
     520             :     }
     521             : 
     522          12 :   vcl_test_init_endpoint_addr (vsm);
     523          12 : }
     524             : 
     525             : int
     526          45 : vts_handle_ctrl_cfg (vcl_test_server_worker_t *wrk, vcl_test_cfg_t *rx_cfg,
     527             :                      vcl_test_session_t *conn, int rx_bytes)
     528             : {
     529          45 :   if (rx_cfg->verbose)
     530             :     {
     531           0 :       vtinf ("(fd %d): Received a cfg msg!", conn->fd);
     532           0 :       vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
     533             :     }
     534             : 
     535          45 :   if (rx_bytes != sizeof (*rx_cfg))
     536             :     {
     537           0 :       vtinf ("(fd %d): Invalid cfg msg size %d expected %lu!", conn->fd,
     538             :              rx_bytes, sizeof (*rx_cfg));
     539           0 :       conn->cfg.rxbuf_size = 0;
     540           0 :       conn->cfg.num_writes = 0;
     541           0 :       if (conn->cfg.verbose)
     542             :         {
     543           0 :           vtinf ("(fd %d): Replying to cfg msg", conn->fd);
     544           0 :           vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
     545             :         }
     546           0 :       conn->write (conn, &conn->cfg, sizeof (conn->cfg));
     547           0 :       return -1;
     548             :     }
     549             : 
     550          45 :   switch (rx_cfg->test)
     551             :     {
     552          12 :     case VCL_TEST_TYPE_NONE:
     553             :     case VCL_TEST_TYPE_ECHO:
     554          12 :       sync_config_and_reply (conn, rx_cfg);
     555          12 :       break;
     556             : 
     557          21 :     case VCL_TEST_TYPE_BI:
     558             :     case VCL_TEST_TYPE_UNI:
     559          21 :       vts_test_cmd (wrk, conn, rx_cfg);
     560          21 :       break;
     561             : 
     562          12 :     case VCL_TEST_TYPE_EXIT:
     563          12 :       vtinf ("Ctrl session fd %d closing!", conn->fd);
     564          12 :       vts_session_cleanup (conn);
     565          12 :       wrk->nfds--;
     566          12 :       if (wrk->nfds)
     567           5 :         vts_wrk_cleanup_all (wrk);
     568          12 :       vcl_server_main.ctrl = 0;
     569          12 :       break;
     570             : 
     571           0 :     default:
     572           0 :       vtwrn ("Unknown test type %d", rx_cfg->test);
     573           0 :       vcl_test_cfg_dump (rx_cfg, 0 /* is_client */ );
     574           0 :       break;
     575             :     }
     576             : 
     577          45 :   return 0;
     578             : }
     579             : 
     580             : static void
     581          12 : vts_worker_init (vcl_test_server_worker_t * wrk)
     582             : {
     583          12 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     584          12 :   vcl_test_main_t *vt = &vcl_test_main;
     585             :   const vcl_test_proto_vft_t *tp;
     586             :   struct epoll_event listen_ev;
     587             :   int rv;
     588             : 
     589          12 :   __wrk_index = wrk->wrk_index;
     590             : 
     591          12 :   vtinf ("Initializing worker ...");
     592             : 
     593          12 :   conn_pool_expand (wrk, VCL_TEST_CFG_INIT_TEST_SESS + 1);
     594          12 :   if (wrk->wrk_index)
     595           0 :     if (vppcom_worker_register ())
     596           0 :       vtfail ("vppcom_worker_register()", 1);
     597             : 
     598          12 :   tp = vt->protos[vsm->server_cfg.proto];
     599          12 :   if ((rv = tp->listen (&wrk->listener, &vsm->server_cfg.endpt)))
     600           0 :     vtfail ("proto listen", rv);
     601             : 
     602             :   /* First worker already has epoll fd */
     603          12 :   if (wrk->wrk_index)
     604             :     {
     605           0 :       wrk->epfd = vppcom_epoll_create ();
     606           0 :       if (wrk->epfd < 0)
     607           0 :         vtfail ("vppcom_epoll_create()", wrk->epfd);
     608             :     }
     609             : 
     610          12 :   listen_ev.events = EPOLLET | EPOLLIN;
     611          12 :   listen_ev.data.u32 = VCL_TEST_DATA_LISTENER;
     612             :   rv =
     613          12 :     vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, wrk->listener.fd, &listen_ev);
     614          12 :   if (rv < 0)
     615           0 :     vtfail ("vppcom_epoll_ctl", rv);
     616             : 
     617          12 :   vsm->active_workers += 1;
     618          12 :   vtinf ("Waiting for client data connections on port %d ...",
     619             :          ntohs (vsm->server_cfg.endpt.port));
     620          12 : }
     621             : 
     622             : static inline int
     623        5886 : vts_conn_read (vcl_test_session_t *conn)
     624             : {
     625        5886 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     626        5886 :   if (vsm->use_ds)
     627           0 :     return vcl_test_read_ds (conn);
     628             :   else
     629        5886 :     return conn->read (conn, conn->rxbuf, conn->rxbuf_size);
     630             : }
     631             : 
     632             : static void
     633           0 : vts_inc_stats_check (vcl_test_session_t *ts)
     634             : {
     635             :   /* Avoid checking time too often because of syscall cost */
     636           0 :   if (ts->stats.rx_bytes - ts->old_stats.rx_bytes < 1 << 20)
     637           0 :     return;
     638             : 
     639           0 :   clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
     640           0 :   if (vcl_test_time_diff (&ts->old_stats.stop, &ts->stats.stop) > 1)
     641             :     {
     642           0 :       vcl_test_stats_dump_inc (ts, 1 /* is_rx */);
     643           0 :       ts->old_stats = ts->stats;
     644             :     }
     645             : }
     646             : 
     647             : static void *
     648          12 : vts_worker_loop (void *arg)
     649             : {
     650             :   struct epoll_event ep_evts[VCL_TEST_CFG_MAX_EPOLL_EVENTS];
     651          12 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     652          12 :   vcl_test_server_worker_t *wrk = arg;
     653             :   vcl_test_session_t *conn;
     654             :   int i, rx_bytes, num_ev;
     655             :   vcl_test_cfg_t *rx_cfg;
     656             : 
     657          12 :   if (wrk->wrk_index)
     658           0 :     vts_worker_init (wrk);
     659             : 
     660             :   while (1)
     661             :     {
     662   120988000 :       num_ev =
     663   120988000 :         vppcom_epoll_wait (wrk->epfd, ep_evts, VCL_TEST_CFG_MAX_EPOLL_EVENTS,
     664             :                            0 /* poll session events */);
     665   120988000 :       if (num_ev < 0)
     666             :         {
     667           0 :           vterr ("vppcom_epoll_wait()", num_ev);
     668           0 :           goto fail;
     669             :         }
     670   120988000 :       else if (num_ev == 0)
     671             :         {
     672   120984000 :           continue;
     673             :         }
     674        8880 :       for (i = 0; i < num_ev; i++)
     675             :         {
     676        4757 :           conn = &wrk->conn_pool[ep_evts[i].data.u32];
     677             :           /*
     678             :            * Check for close events
     679             :            */
     680        4757 :           if (ep_evts[i].events & (EPOLLHUP | EPOLLRDHUP))
     681             :             {
     682           6 :               if (conn == vsm->ctrl)
     683             :                 {
     684           0 :                   vtinf ("ctrl session went away");
     685           0 :                   vsm->ctrl = 0;
     686             :                 }
     687           6 :               vts_session_cleanup (conn);
     688           6 :               wrk->nfds--;
     689           6 :               continue;
     690             :             }
     691             : 
     692             :           /*
     693             :            * Check if new session needs to be accepted
     694             :            */
     695             : 
     696        4751 :           if (!wrk->wrk_index && ep_evts[i].data.u32 == VCL_TEST_CTRL_LISTENER)
     697             :             {
     698          12 :               if (vsm->ctrl)
     699             :                 {
     700           0 :                   vtwrn ("ctrl already exists");
     701           0 :                   continue;
     702             :                 }
     703          12 :               vsm->ctrl = vts_accept_ctrl (wrk, vsm->ctrl_listen_fd);
     704          12 :               continue;
     705             :             }
     706             : 
     707             :           /* at this point ctrl session must be valid */
     708        4739 :           ASSERT (vsm->ctrl);
     709             : 
     710        4739 :           if (ep_evts[i].data.u32 == VCL_TEST_DATA_LISTENER)
     711             :             {
     712          14 :               conn = vts_accept_client (wrk, wrk->listener.fd);
     713          14 :               conn->cfg = vsm->ctrl->cfg;
     714          14 :               continue;
     715             :             }
     716        4725 :           else if (vppcom_session_is_connectable_listener (conn->fd))
     717             :             {
     718           0 :               vts_accept_client (wrk, conn->fd);
     719           0 :               continue;
     720             :             }
     721             : 
     722             :           /*
     723             :            * Message on control session
     724             :            */
     725             : 
     726        4725 :           if (!wrk->wrk_index && conn->fd == vsm->ctrl->fd)
     727             :             {
     728          50 :               rx_bytes = conn->read (conn, conn->rxbuf, conn->rxbuf_size);
     729          50 :               rx_cfg = (vcl_test_cfg_t *) conn->rxbuf;
     730          50 :               if (rx_cfg->magic == VCL_TEST_CFG_CTRL_MAGIC)
     731             :                 {
     732          45 :                   vts_handle_ctrl_cfg (wrk, rx_cfg, conn, rx_bytes);
     733          45 :                   if (!wrk->nfds)
     734             :                     {
     735          12 :                       vtinf ("All client connections closed\n");
     736          12 :                       goto done;
     737             :                     }
     738             :                 }
     739           5 :               else if (isascii (conn->rxbuf[0]))
     740             :                 {
     741           5 :                   vts_server_echo (conn, rx_bytes);
     742             :                 }
     743             :               else
     744             :                 {
     745           0 :                   vtwrn ("FIFO not drained! extra bytes %d", rx_bytes);
     746             :                 }
     747          38 :               continue;
     748             :             }
     749             : 
     750             :           /*
     751             :            * Read perf test data
     752             :            */
     753             : 
     754        4675 :           if (EPOLLIN & ep_evts[i].events)
     755             :             {
     756        4675 :             read_again:
     757        5886 :               rx_bytes = vts_conn_read (conn);
     758             : 
     759        5886 :               if (rx_bytes <= 0)
     760             :                 {
     761           0 :                   if (errno == ECONNRESET)
     762             :                     {
     763           0 :                       vtinf ("Connection reset by remote peer.\n");
     764           0 :                       goto fail;
     765             :                     }
     766             :                   else
     767           0 :                     continue;
     768             :                 }
     769        5886 :               vts_server_process_rx (conn, rx_bytes);
     770        5886 :               if (vppcom_session_attr (conn->fd, VPPCOM_ATTR_GET_NREAD, 0, 0) >
     771             :                   0)
     772        1211 :                 goto read_again;
     773        4675 :               if (vsm->incremental_stats)
     774           0 :                 vts_inc_stats_check (conn);
     775        4675 :               continue;
     776             :             }
     777             :           else
     778             :             {
     779           0 :               vtwrn ("Unhandled event");
     780           0 :               goto fail;
     781             :             }
     782             :         }
     783             :     }
     784             : 
     785           0 : fail:
     786           0 :   vsm->worker_fails -= 1;
     787             : 
     788          12 : done:
     789          12 :   vppcom_session_close (wrk->listener.fd);
     790          12 :   if (wrk->conn_pool)
     791          12 :     free (wrk->conn_pool);
     792          12 :   vsm->active_workers -= 1;
     793          12 :   return 0;
     794             : }
     795             : 
     796             : static void
     797          12 : vts_ctrl_session_init (vcl_test_server_worker_t *wrk)
     798             : {
     799          12 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     800             :   struct epoll_event listen_ev;
     801             :   int rv;
     802             : 
     803          12 :   vtinf ("Initializing main ctrl session ...");
     804             : 
     805          12 :   vsm->ctrl_listen_fd =
     806          12 :     vppcom_session_create (VPPCOM_PROTO_TCP, 0 /* is_nonblocking */);
     807          12 :   if (vsm->ctrl_listen_fd < 0)
     808           0 :     vtfail ("vppcom_session_create()", vsm->ctrl_listen_fd);
     809             : 
     810          12 :   rv = vppcom_session_bind (vsm->ctrl_listen_fd, &vsm->server_cfg.endpt);
     811          12 :   if (rv < 0)
     812           0 :     vtfail ("vppcom_session_bind()", rv);
     813             : 
     814          12 :   rv = vppcom_session_listen (vsm->ctrl_listen_fd, 10);
     815          12 :   if (rv < 0)
     816           0 :     vtfail ("vppcom_session_listen()", rv);
     817             : 
     818          12 :   wrk->epfd = vppcom_epoll_create ();
     819          12 :   if (wrk->epfd < 0)
     820           0 :     vtfail ("vppcom_epoll_create()", wrk->epfd);
     821             : 
     822          12 :   listen_ev.events = EPOLLET | EPOLLIN;
     823          12 :   listen_ev.data.u32 = VCL_TEST_CTRL_LISTENER;
     824          12 :   rv = vppcom_epoll_ctl (wrk->epfd, EPOLL_CTL_ADD, vsm->ctrl_listen_fd,
     825             :                          &listen_ev);
     826          12 :   if (rv < 0)
     827           0 :     vtfail ("vppcom_epoll_ctl", rv);
     828             : 
     829          12 :   vtinf ("Waiting for client ctrl connection on port %d ...",
     830             :          vsm->server_cfg.port);
     831          12 : }
     832             : 
     833             : int
     834          12 : main (int argc, char **argv)
     835             : {
     836          12 :   vcl_test_server_main_t *vsm = &vcl_server_main;
     837          12 :   vcl_test_main_t *vt = &vcl_test_main;
     838             :   int rv, i;
     839             : 
     840          12 :   clib_mem_init_thread_safe (0, 64 << 20);
     841          12 :   vsm->server_cfg.port = VCL_TEST_SERVER_PORT;
     842          12 :   vsm->server_cfg.workers = 1;
     843          12 :   vsm->active_workers = 0;
     844          12 :   vcl_test_server_process_opts (vsm, argc, argv);
     845             : 
     846          12 :   rv = vppcom_app_create ("vcl_test_server");
     847          12 :   if (rv)
     848           0 :     vtfail ("vppcom_app_create()", rv);
     849             : 
     850             :   /* Protos like tls/dtls/quic need init */
     851          12 :   if (vt->protos[vsm->server_cfg.proto]->init)
     852           3 :     vt->protos[vsm->server_cfg.proto]->init (0);
     853             : 
     854          12 :   vsm->workers = calloc (vsm->server_cfg.workers, sizeof (*vsm->workers));
     855          12 :   vts_ctrl_session_init (&vsm->workers[0]);
     856             : 
     857             :   /* Update ctrl port to data port */
     858          12 :   vsm->server_cfg.endpt.port += 1;
     859          12 :   vts_worker_init (&vsm->workers[0]);
     860          12 :   for (i = 1; i < vsm->server_cfg.workers; i++)
     861             :     {
     862           0 :       vsm->workers[i].wrk_index = i;
     863           0 :       rv = pthread_create (&vsm->workers[i].thread_handle, NULL,
     864           0 :                            vts_worker_loop, (void *) &vsm->workers[i]);
     865             :     }
     866             : 
     867          12 :   vts_worker_loop (&vsm->workers[0]);
     868             : 
     869          12 :   while (vsm->active_workers > 0)
     870             :     ;
     871             : 
     872          12 :   vppcom_app_destroy ();
     873          12 :   free (vsm->workers);
     874             : 
     875          12 :   return vsm->worker_fails;
     876             : }
     877             : 
     878             : /*
     879             :  * fd.io coding-style-patch-verification: ON
     880             :  *
     881             :  * Local Variables:
     882             :  * eval: (c-set-style "gnu")
     883             :  * End:
     884             :  */

Generated by: LCOV version 1.14