/* * Copyright (c) 2017-2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { uint8_t is_alloc; int fd; uint8_t *buf; uint32_t buf_size; vcl_test_cfg_t cfg; vcl_test_stats_t stats; vppcom_endpt_t endpt; uint8_t ip[16]; vppcom_data_segments_t ds; } vcl_test_server_conn_t; typedef struct { uint16_t port; uint32_t address_ip6; u8 proto; u8 workers; vppcom_endpt_t endpt; } vcl_test_server_cfg_t; typedef struct { uint32_t wrk_index; int listen_fd; int epfd; struct epoll_event wait_events[VCL_TEST_CFG_MAX_EPOLL_EVENTS]; size_t conn_pool_size; vcl_test_server_conn_t *conn_pool; int nfds; pthread_t thread_handle; } vcl_test_server_worker_t; typedef struct { vcl_test_server_cfg_t cfg; vcl_test_server_worker_t *workers; struct sockaddr_storage servaddr; volatile int worker_fails; volatile int active_workers; u8 use_ds; } vcl_test_server_main_t; static __thread int __wrk_index = 0; static vcl_test_server_main_t vcl_server_main; static inline void conn_pool_expand (vcl_test_server_worker_t * wrk, size_t expand_size) { vcl_test_server_conn_t *conn_pool; size_t new_size = wrk->conn_pool_size + expand_size; int i; conn_pool = realloc (wrk->conn_pool, new_size * sizeof (*wrk->conn_pool)); if (conn_pool) { for (i = wrk->conn_pool_size; i < new_size; i++) { vcl_test_server_conn_t *conn = &conn_pool[i]; memset (conn, 0, sizeof (*conn)); vcl_test_cfg_init (&conn->cfg); vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ , &conn->buf, &conn->buf_size); conn->cfg.txbuf_size = conn->cfg.rxbuf_size; } wrk->conn_pool = conn_pool; wrk->conn_pool_size = new_size; } else { vterr ("conn_pool_expand()", -errno); } } static inline vcl_test_server_conn_t * conn_pool_alloc (vcl_test_server_worker_t * wrk) { int i, expand = 0; again: for (i = 0; i < wrk->conn_pool_size; i++) { if (!wrk->conn_pool[i].is_alloc) { wrk->conn_pool[i].endpt.ip = wrk->conn_pool[i].ip; wrk->conn_pool[i].is_alloc = 1; return (&wrk->conn_pool[i]); } } if (expand == 0) { conn_pool_expand (wrk, 2 * wrk->conn_pool_size); expand = 1; goto again; } vtwrn ("Failed to allocate connection even after expand"); return 0; } static inline void conn_pool_free (vcl_test_server_conn_t * conn) { conn->fd = 0; conn->is_alloc = 0; } static inline void sync_config_and_reply (vcl_test_server_conn_t * conn, vcl_test_cfg_t * rx_cfg) { conn->cfg = *rx_cfg; vcl_test_buf_alloc (&conn->cfg, 1 /* is_rxbuf */ , &conn->buf, &conn->buf_size); conn->cfg.txbuf_size = conn->cfg.rxbuf_size; if (conn->cfg.verbose) { vtinf ("(fd %d): Replying to cfg message!\n", conn->fd); vcl_test_cfg_dump (&conn->cfg, 0 /* is_client */ ); } (void) vcl_test_write (conn->fd, (uint8_t *) & conn->cfg, sizeof (conn->cfg), NULL, conn->cfg.verbose); } static void vts_server_start_stop (vcl_test_server_worker_t * wrk, vcl_test_server_conn_t * conn, vcl_test_cfg_t * rx_cfg) { u8 is_bi = rx_cfg->test == VCL_TEST_TYPE_BI; vcl_test_server_conn_t *tc; char buf[64]; int i; if (rx_cfg->ctrl_handle == conn->fd) { for (i = 0; i < wrk->conn_pool_size; i++) { tc = &wrk->conn_pool[i]; if (tc->cfg.ctrl_handle != conn->fd) continue; vcl_test_stats_accumulate (&conn->stats, &tc->stats); if (vcl_comp_tspec (&conn->stats.stop, &tc->stats.stop) < 0) conn->stats.stop = tc->stats.stop; /* Client delays sending of disconnect */ conn->stats.stop.tv_sec -= VCL_TEST_DELAY_DISCONNECT; if (conn->cfg.verbose) { sprintf (buf, "SERVER (fd %d) RESULTS", tc->fd); vcl_test_stats_dump (buf, &tc->stats, 1 /* show_rx */ , is_bi /* show tx */ , conn->cfg.verbose); } } v
SOURCE_PATH = $(CURDIR)/..
n vcl_test_read_ds (conn->fd, conn->ds, &conn->stats); else return vcl_test_read (conn->fd, conn->buf, conn->buf_size, &conn->stats); } static inline int vts_conn_has_ascii (vcl_test_server_conn_t * conn) { vcl_test_server_main_t *vsm = &vcl_server_main; if (vsm->use_ds) return isascii (conn->ds[0].data[0]); else return isascii (conn->buf[0]); } static void * vts_worker_loop (void *arg) { vcl_test_server_main_t *vsm = &vcl_server_main; vcl_test_server_worker_t *wrk = arg; vcl_test_server_conn_t *conn; int i, rx_bytes, num_ev; vcl_test_cfg_t *rx_cfg; if (wrk->wrk_index) vts_worker_init (wrk); while (1) { num_ev = vppcom_epoll_wait (wrk->epfd, wrk->wait_events, VCL_TEST_CFG_MAX_EPOLL_EVENTS, 60000.0); if (num_ev < 0) { vterr ("vppcom_epoll_wait()", num_ev); goto fail; } else if (num_ev == 0) { vtinf ("vppcom_epoll_wait() timeout!"); continue; } for (i = 0; i < num_ev; i++) { conn = &wrk->conn_pool[wrk->wait_events[i].data.u32]; if (wrk->wait_events[i].events & (EPOLLHUP | EPOLLRDHUP)) { vppcom_session_close (conn->fd); wrk->nfds -= 1; if (!wrk->nfds) { vtinf ("All client connections closed\n"); goto done; } continue; } if (wrk->wait_events[i].data.u32 == ~0) { vts_new_client (wrk); continue; } if (EPOLLIN & wrk->wait_events[i].events) { read_again: rx_bytes = vts_conn_read (conn); if (rx_bytes <= 0) { if (errno == ECONNRESET) { vtinf ("Connection reset by remote peer.\n"); goto fail; } else continue; } if (vts_conn_expect_config (conn)) { rx_cfg = vts_conn_read_config (conn); if (rx_cfg->magic == VCL_TEST_CFG_CTRL_MAGIC) { if (vsm->use_ds) vppcom_session_free_segments (conn->fd, conn->ds); vts_handle_cfg (wrk, rx_cfg, conn, rx_bytes); if (!wrk->nfds) { vtinf ("All client connections closed\n"); goto done; } continue; } } if ((conn->cfg.test == VCL_TEST_TYPE_UNI) || (conn->cfg.test == VCL_TEST_TYPE_BI)) { vts_server_rx (conn, rx_bytes); if (vppcom_session_attr (conn->fd, VPPCOM_ATTR_GET_NREAD, 0, 0) > 0) goto read_again; continue; } if (vts_conn_has_ascii (conn)) { vts_server_echo (conn, rx_bytes); } else { vtwrn ("FIFO not drained! extra bytes %d", rx_bytes); } } else { vtwrn ("Unhandled event"); goto fail; } } } fail: vsm->worker_fails -= 1; done: vppcom_session_close (wrk->listen_fd); if (wrk->conn_pool) free (wrk->conn_pool); vsm->active_workers -= 1; return 0; } int main (int argc, char **argv) { vcl_test_server_main_t *vsm = &vcl_server_main; int rv, i; clib_mem_init_thread_safe (0, 64 << 20); vsm->cfg.port = VCL_TEST_SERVER_PORT; vsm->cfg.workers = 1; vsm->active_workers = 0; vcl_test_server_process_opts (vsm, argc, argv); rv = vppcom_app_create ("vcl_test_server"); if (rv) vtfail ("vppcom_app_create()", rv); vsm->workers = calloc (vsm->cfg.workers, sizeof (*vsm->workers)); vts_worker_init (&vsm->workers[0]); for (i = 1; i < vsm->cfg.workers; i++) { vsm->workers[i].wrk_index = i; rv = pthread_create (&vsm->workers[i].thread_handle, NULL, vts_worker_loop, (void *) &vsm->workers[i]); } vts_worker_loop (&vsm->workers[0]); while (vsm->active_workers > 0) ; vppcom_app_destroy (); free (vsm->workers); return vsm->worker_fails; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */