diff --git a/src/utils/qemu.c b/src/utils/qemu.c index 19ae5b395..0b05ea281 100644 --- a/src/utils/qemu.c +++ b/src/utils/qemu.c @@ -70,6 +70,8 @@ int pg_util_spawn_qemu(const char *socket_path_0, const char *socket_path_1, const char *mac_0, const char *mac_1, + const int is_client_0, + const int is_client_1, const char *vm_image_path, const char *vm_key_path, const char *hugepages_path, @@ -81,13 +83,15 @@ int pg_util_spawn_qemu(const char *socket_path_0, pg_autofree char *argv_qemu = NULL; const char *argv_sock_0 = ""; const char *argv_sock_1 = ""; + const char *server_params = ",server,nowait"; pg_autofree char *argv_sock_0_t = NULL; pg_autofree char *argv_sock_1_t = NULL; pg_autofree char *ssh_cmd = NULL; GError *error = NULL; - g_assert(g_file_test(socket_path_0, G_FILE_TEST_EXISTS)); - if (socket_path_1) + if(!is_client_1) + g_assert(g_file_test(socket_path_0, G_FILE_TEST_EXISTS)); + if (socket_path_1 && !is_client_1) g_assert(g_file_test(socket_path_1, G_FILE_TEST_EXISTS)); g_assert(g_file_test(vm_image_path, G_FILE_TEST_EXISTS)); g_assert(g_file_test(vm_key_path, G_FILE_TEST_EXISTS)); @@ -95,21 +99,25 @@ int pg_util_spawn_qemu(const char *socket_path_0, if (socket_path_0) { argv_sock_0_t = g_strdup_printf( - PG_STRCAT(" -chardev socket,id=char0,path=%s", + PG_STRCAT(" -chardev socket,id=char0,path=%s%s", " -netdev type=vhost-user,id=mynet0,", "chardev=char0,vhostforce", " -device virtio-net-pci,mac=%s", - ",netdev=mynet0"), socket_path_0, mac_0); + ",netdev=mynet0"), socket_path_0, + is_client_0 ? server_params : "", + mac_0); argv_sock_0 = argv_sock_0_t; } if (socket_path_1) { argv_sock_1_t = g_strdup_printf( - PG_STRCAT(" -chardev socket,id=char1,path=%s", + PG_STRCAT(" -chardev socket,id=char1,path=%s%s", " -netdev type=vhost-user,id=mynet1,", "chardev=char1,vhostforce", " -device virtio-net-pci,mac=%s", - ",netdev=mynet1"), socket_path_1, mac_1); + ",netdev=mynet1"), socket_path_1, + is_client_0 ? server_params : "", + mac_1); argv_sock_1 = argv_sock_1_t; } diff --git a/src/utils/qemu.h b/src/utils/qemu.h index 5411111e6..695b2dcdc 100644 --- a/src/utils/qemu.h +++ b/src/utils/qemu.h @@ -32,6 +32,8 @@ int pg_util_spawn_qemu(const char *socket_path_0, const char *socket_path_1, const char *mac_0, const char *mac_1, + const int is_server_0, + const int is_server_1, const char *vm_image_path, const char *vm_ssh_key_path, const char *hugepages_path, diff --git a/src/vhost.c b/src/vhost.c index 94cae7526..e3777a120 100644 --- a/src/vhost.c +++ b/src/vhost.c @@ -84,6 +84,7 @@ struct pg_vhost_state { struct pg_vhost_socket *socket; rte_atomic32_t allow_queuing; int vid; + uint64_t flags; struct rte_mbuf *in[PG_MAX_PKTS_BURST]; struct rte_mbuf *out[PG_MAX_PKTS_BURST]; PG_PKTS_COUNT_TYPE tx_bytes; /* TX: [vhost] --> VM */ @@ -94,20 +95,18 @@ struct pg_vhost_state { #endif }; -static int new_vm(int dev); -static void destroy_vm(int dev); +static int on_new_device(int dev); +static void on_destroy_device(int dev); static const struct vhost_device_ops virtio_net_device_ops = { - .new_device = new_vm, - .destroy_device = destroy_vm, + .new_device = on_new_device, + .destroy_device = on_destroy_device, }; /* head of the socket list */ static LIST_HEAD(socket_list, pg_vhost_socket) sockets; static char *sockets_path; static int vhost_start_ok; -static int disable_freacture_mask; -static int enable_freacture_mask; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; @@ -125,6 +124,19 @@ static struct pg_brick_config *vhost_config_new(const char *name, return pg_brick_config_init(config, name, 1, 1, PG_MONOPOLE); } +/* To know if a vhost brick is server or not. */ +/* If the brick is server, return 0. */ +static int pg_vhost_is_server(struct pg_brick *brick) +{ + struct pg_vhost_state *state = + pg_brick_get_state(brick, + struct pg_vhost_state); + + if(state->flags & 1) + return -1; + return 0; +} + static int vhost_burst(struct pg_brick *brick, enum pg_side from, uint16_t edge_index, struct rte_mbuf **pkts, uint64_t pkts_mask, struct pg_error **errp) @@ -253,120 +265,23 @@ static int vhost_poll(struct pg_brick *brick, uint16_t *pkts_cnt, pkts_mask = pg_mask_firsts(count); ret = pg_brick_burst(s->edge.link, state->output, s->edge.pair_index, - in, pkts_mask, errp); + in, pkts_mask, errp); pg_packets_free(in, pkts_mask); return ret; } #ifndef RTE_VHOST_USER_CLIENT -#define RTE_VHOST_USER_CLIENT 0 +#define RTE_VHOST_USER_CLIENT (1ULL << 0) #endif #ifndef RTE_VHOST_USER_NO_RECONNECT -#define RTE_VHOST_USER_NO_RECONNECT 0 +#define RTE_VHOST_USER_NO_RECONNECT (1ULL << 1) #endif #ifndef RTE_VHOST_USER_DEQUEUE_ZERO_COPY -#define RTE_VHOST_USER_DEQUEUE_ZERO_COPY 0 +#define RTE_VHOST_USER_DEQUEUE_ZERO_COPY (1ULL << 2) #endif -static void vhost_create_socket(struct pg_vhost_state *state, uint64_t flags, - struct pg_error **errp) -{ - struct pg_vhost_socket *s; - char *path; - int ret; - - path = g_strdup_printf("%s/qemu-%s", sockets_path, state->brick.name); - g_remove(path); - - printf("New vhost-user socket: %s, zero-copy %s\n", path, - (flags & RTE_VHOST_USER_DEQUEUE_ZERO_COPY) ? - "enable" : "disable"); - - flags = flags & (RTE_VHOST_USER_CLIENT | RTE_VHOST_USER_NO_RECONNECT | - RTE_VHOST_USER_DEQUEUE_ZERO_COPY); - - ret = rte_vhost_driver_register(path, flags); - if (ret) { - *errp = pg_error_new_errno(-ret, - "Failed to start vhost user driver"); - goto free_exit; - } - - - ret = rte_vhost_driver_callback_register(path, &virtio_net_device_ops); - if (ret) { - *errp = pg_error_new_errno(-ret, - "Failed to register vhost-user callbacks"); - goto free_exit; - } - - rte_vhost_driver_start(path); - if (ret) { - *errp = pg_error_new_errno(-ret, - "Error registering driver for socket %s", - path); - goto free_exit; - } - - pthread_mutex_lock(&mutex); - - s = g_new0(struct pg_vhost_socket, 1); - s->path = path; - - state->socket = s; - state->socket->state = state; - - LIST_INSERT_HEAD(&sockets, s, socket_list); - - pthread_mutex_unlock(&mutex); - return; -free_exit: - g_free(path); -} - -#define VHOST_NOT_READY \ - "vhost not ready, did you called vhost_start after packetgraph_start ?" - -static int vhost_init(struct pg_brick *brick, struct pg_brick_config *config, - struct pg_error **errp) -{ - struct pg_vhost_state *state; - struct pg_vhost_config *vhost_config; - - state = pg_brick_get_state(brick, struct pg_vhost_state); - if (!vhost_start_ok) { - *errp = pg_error_new(VHOST_NOT_READY); - return -1; - } - - vhost_config = (struct pg_vhost_config *) config->brick_config; - state->output = vhost_config->output; - state->vid = -1; - PG_PKTS_COUNT_SET(state->rx_bytes, 0); - PG_PKTS_COUNT_SET(state->tx_bytes, 0); - pg_vhost_enable(brick, enable_freacture_mask); - pg_vhost_disable(brick, disable_freacture_mask); - - vhost_create_socket(state, vhost_config->flags, errp); - if (pg_error_is_set(errp)) - return -1; - - brick->burst = vhost_burst; - brick->poll = vhost_poll; - - rte_atomic32_init(&state->allow_queuing); - rte_atomic32_set(&state->allow_queuing, 1); -#ifdef PG_VHOST_FASTER_YET_BROKEN_POLL - state->check_atomic = 1024; - state->check_counter = 0; -#endif /* PG_VHOST_FASTER_YET_BROKEN_POLL */ - - return 0; -} - -#undef VHOST_NOT_READY static enum pg_side vhost_get_side(struct pg_brick *brick) { @@ -389,17 +304,16 @@ struct pg_brick *pg_vhost_new(const char *name, uint64_t flags, static void vhost_destroy(struct pg_brick *brick, struct pg_error **errp) { - struct pg_vhost_state *state; - - state = pg_brick_get_state(brick, struct pg_vhost_state); + struct pg_vhost_state *state = + pg_brick_get_state(brick, struct pg_vhost_state); rte_vhost_driver_unregister(state->socket->path); - pthread_mutex_lock(&mutex); - g_remove(state->socket->path); - LIST_REMOVE(state->socket, socket_list); g_free(state->socket->path); g_free(state->socket); - + LIST_REMOVE(state->socket, socket_list); + /* If the socket is client, do NOT destroy the existing socket. */ + if(pg_vhost_is_server(brick) == 0) + g_remove(state->socket->path); pthread_mutex_unlock(&mutex); } @@ -425,7 +339,7 @@ static uint64_t tx_bytes(struct pg_brick *brick) pg_brick_get_state(brick, struct pg_vhost_state)->tx_bytes); } -static int new_vm(int dev) +static int on_new_device(int dev) { struct pg_vhost_socket *s = NULL; char buf[256]; @@ -448,7 +362,7 @@ static int new_vm(int dev) return 0; } -static void destroy_vm(int dev) +static void on_destroy_device(int dev) { struct pg_vhost_socket *s = NULL; char buf[256]; @@ -472,11 +386,106 @@ static void destroy_vm(int dev) break; } } + pthread_mutex_unlock(&mutex); +} +static void vhost_create_socket(struct pg_vhost_state *state, uint64_t flags, + struct pg_error **errp) +{ + struct pg_vhost_socket *s; + char *path; + int ret; + + path = g_strdup_printf("%s/qemu-%s", sockets_path, state->brick.name); + /* If the socket is CLIENT do NOT destroy the socket. */ + if((flags & 1) == 0) + g_remove(path); + + printf("New vhost-user socket: %s, zero-copy %s\n", path, + (flags & RTE_VHOST_USER_DEQUEUE_ZERO_COPY) ? + "enable" : "disable"); + + flags = flags & (RTE_VHOST_USER_CLIENT | RTE_VHOST_USER_NO_RECONNECT | + RTE_VHOST_USER_DEQUEUE_ZERO_COPY); + + ret = rte_vhost_driver_register(path, flags); + if (ret) { + *errp = pg_error_new_errno(-ret, + "Failed to start vhost user driver"); + goto free_exit; + } + + ret = rte_vhost_driver_callback_register(path, + &virtio_net_device_ops); + if (ret) { + *errp = pg_error_new_errno(-ret, + "Failed to register vhost-user callbacks"); + goto free_exit; + } + + rte_vhost_driver_start(path); + if (ret) { + *errp = pg_error_new_errno(-ret, + "Error registering driver for socket %s", + path); + goto free_exit; + } + pthread_mutex_lock(&mutex); + + s = g_new0(struct pg_vhost_socket, 1); + s->path = path; + + state->socket = s; + state->socket->state = state; + + LIST_INSERT_HEAD(&sockets, s, socket_list); pthread_mutex_unlock(&mutex); + return; +free_exit: + g_free(path); +} + +#define VHOST_NOT_READY \ + "vhost not ready, did you called vhost_start after packetgraph_start ?" + +static int vhost_init(struct pg_brick *brick, struct pg_brick_config *config, + struct pg_error **errp) +{ + struct pg_vhost_state *state; + struct pg_vhost_config *vhost_config; + + state = pg_brick_get_state(brick, struct pg_vhost_state); + if (!vhost_start_ok) { + *errp = pg_error_new(VHOST_NOT_READY); + return -1; + } + vhost_config = (struct pg_vhost_config *) config->brick_config; + state->output = vhost_config->output; + state->vid = -1; + state->flags = vhost_config->flags; + PG_PKTS_COUNT_SET(state->rx_bytes, 0); + PG_PKTS_COUNT_SET(state->tx_bytes, 0); + vhost_create_socket(state, vhost_config->flags, errp); + if (pg_error_is_set(errp)) + return -1; + + brick->burst = vhost_burst; + brick->poll = vhost_poll; + + rte_atomic32_init(&state->allow_queuing); + rte_atomic32_set(&state->allow_queuing, 1); +#ifdef PG_VHOST_FASTER_YET_BROKEN_POLL + state->check_atomic = 1024; + state->check_counter = 0; +#endif /* PG_VHOST_FASTER_YET_BROKEN_POLL */ + + return 0; } +#undef VHOST_NOT_READY + + /** * Check that the socket path exists and has the right perm and store it * @@ -542,38 +551,6 @@ int pg_vhost_start(const char *base_dir, struct pg_error **errp) return 0; } -int pg_vhost_global_enable(uint64_t feature_mask) -{ - enable_freacture_mask |= feature_mask; - return 0; -} - -int pg_vhost_global_disable(uint64_t feature_mask) -{ - disable_freacture_mask |= feature_mask; - return 0; -} - -int pg_vhost_enable(struct pg_brick *brick, uint64_t feature_mask) -{ - const char *path = pg_vhost_socket_path(brick); - - if (unlikely(!path)) - return -1; - return rte_vhost_driver_enable_features(path, - feature_mask) ? -1 : 0; -} - -int pg_vhost_disable(struct pg_brick *brick, uint64_t feature_mask) -{ - const char *path = pg_vhost_socket_path(brick); - - if (unlikely(!path)) - return -1; - return rte_vhost_driver_disable_features(path, - feature_mask) ? -1 : 0; -} - static void vhost_link(struct pg_brick *brick, enum pg_side side, int edge) { struct pg_vhost_state *state = diff --git a/tests/integration/tests.c b/tests/integration/tests.c index b026ce619..713353fc2 100644 --- a/tests/integration/tests.c +++ b/tests/integration/tests.c @@ -1,3 +1,4 @@ + /* Copyright 2015 Outscale SAS * * This file is part of Packetgraph. @@ -171,6 +172,7 @@ static inline int start_qemu_graph(struct branch *branch, int qemu_pid = pg_util_spawn_qemu(sock_path_graph(branch), sock_read_path_graph(branch), tmp_mac, mac_reader, + 0,0, glob_vm_path, glob_vm_key_path, glob_hugepages_path, errp); diff --git a/tests/vhost/bench-vhost.c b/tests/vhost/bench-vhost.c index eb6296473..715a3b1de 100644 --- a/tests/vhost/bench-vhost.c +++ b/tests/vhost/bench-vhost.c @@ -80,6 +80,7 @@ void test_benchmark_vhost(char *vm_image_path, qemu_pid = pg_util_spawn_qemu(socket_path_enter, socket_path_exit, mac1_str, mac2_str, + 0,0, vm_image_path, vm_ssh_key_path, hugepages_path, &error); g_assert(!error); diff --git a/tests/vhost/test-vhost.c b/tests/vhost/test-vhost.c index e9756c50b..32be5c890 100644 --- a/tests/vhost/test-vhost.c +++ b/tests/vhost/test-vhost.c @@ -103,6 +103,7 @@ static void test_vhost_flow_(int qemu_exit_signal) qemu_pid = pg_util_spawn_qemu(socket_path_0, socket_path_1, mac_addr_0, mac_addr_1, + 0, 0, glob_vm_path, glob_vm_key_path, glob_hugepages_path, &error); @@ -268,6 +269,7 @@ static void test_vhost_multivm_(int qemu_exit_signal) g_assert(socket_path_11); qemu_pid0 = pg_util_spawn_qemu(socket_path_00, socket_path_01, mac_addr_00, mac_addr_01, + 0, 0, glob_vm_path, glob_vm_key_path, glob_hugepages_path, &error); g_assert(qemu_pid0); @@ -286,6 +288,7 @@ static void test_vhost_multivm_(int qemu_exit_signal) qemu_pid1 = pg_util_spawn_qemu(socket_path_10, socket_path_11, mac_addr_10, mac_addr_11, + 0, 0, glob_vm_path, glob_vm_key_path, glob_hugepages_path, &error); g_assert(qemu_pid1); @@ -445,6 +448,7 @@ static void qemu_test(struct qemu_test_params *p) qemu_pid = pg_util_spawn_qemu(p->socket_path[0], p->socket_path[1], mac_0, mac_1, + 0, 0, glob_vm_path, glob_vm_key_path, glob_hugepages_path, &error); @@ -584,61 +588,6 @@ static void test_vhost_reco(void) pg_vhost_stop(); } -static void test_vhost_destroy(void) -{ - const char mac_addr_0[18] = "52:54:00:12:34:11"; - const char mac_addr_1[18] = "52:54:00:12:34:12"; - struct pg_brick *vhost_0, *vhost_1; - const char *socket_path_0, *socket_path_1; - struct pg_error *error = NULL; - int ret, qemu_pid; - - /* start vhost */ - ret = pg_vhost_start("/tmp", &error); - g_assert(ret == 0); - g_assert(!error); - - /* instanciate brick */ - vhost_0 = pg_vhost_new("vhost-0", PG_VHOST_USER_DEQUEUE_ZERO_COPY, - &error); - g_assert(!error); - g_assert(vhost_0); - - vhost_1 = pg_vhost_new("vhost-1", PG_VHOST_USER_DEQUEUE_ZERO_COPY, - &error); - g_assert(!error); - g_assert(vhost_1); - - /* spawn QEMU */ - socket_path_0 = pg_vhost_socket_path(vhost_0); - g_assert(!error); - g_assert(socket_path_0); - socket_path_1 = pg_vhost_socket_path(vhost_1); - g_assert(!error); - g_assert(socket_path_1); - - qemu_pid = pg_util_spawn_qemu(socket_path_0, socket_path_1, - mac_addr_0, mac_addr_1, - glob_vm_path, - glob_vm_key_path, - glob_hugepages_path, &error); - - g_assert(!error); - g_assert(qemu_pid); - - /* try to destroy vhost brick before qemu is killed */ - pg_brick_destroy(vhost_0); - g_assert(!error); - pg_brick_destroy(vhost_1); - g_assert(!error); - - /* kill QEMU */ - pg_util_stop_qemu(qemu_pid, SIGQUIT); - - /* stop vhost */ - pg_vhost_stop(); -} - /* qemu_duo_* create a graph to test ping/iperf/tcp/udp: * --[vhost]--[vhost]-- * @@ -692,6 +641,7 @@ static void qemu_duo_new(struct qemu_duo_test_params *p) mac = g_strdup_printf("52:54:00:12:34:%02i", i); p->qemu_pid[i] = pg_util_spawn_qemu(socket_path, NULL, mac, NULL, + 0, 0, glob_vm_path, glob_vm_key_path, glob_hugepages_path, @@ -798,8 +748,269 @@ static void test_vhost_seccomp(void) pg_vhost_stop(); } +static void test_vhost_as_cli(void) +{ + int qemu_exit_signal = SIGKILL; + const char mac_addr_0[18] = "52:54:00:12:34:11"; + const char mac_addr_1[18] = "52:54:00:12:34:12"; + struct rte_mempool *mbuf_pool = pg_get_mempool(); + struct pg_brick *vhost_0, *vhost_1, *collect; + struct rte_mbuf *pkts[PG_MAX_PKTS_BURST]; + const char *socket_path_0, *socket_path_1; + struct pg_error *error = NULL; + struct rte_mbuf **result_pkts; + int ret, qemu_pid, i; + uint64_t pkts_mask; + + /* start vhost */ + ret = pg_vhost_start("/tmp", &error); + g_assert(ret == 0); + g_assert(!error); + + /* instanciate brick */ + vhost_0 = pg_vhost_new("vhost-0", PG_VHOST_USER_CLIENT, + &error); + g_assert(!error); + g_assert(vhost_0); + + vhost_1 = pg_vhost_new("vhost-1", PG_VHOST_USER_CLIENT, + &error); + g_assert(!error); + g_assert(vhost_1); + + collect = pg_collect_new("collect", &error); + g_assert(!error); + g_assert(collect); + + /* build the graph */ + pg_brick_link(collect, vhost_1, &error); + g_assert(!error); + + /* spawn first QEMU */ + socket_path_0 = pg_vhost_socket_path(vhost_0); + g_assert(!error); + g_assert(socket_path_0); + socket_path_1 = pg_vhost_socket_path(vhost_1); + g_assert(!error); + g_assert(socket_path_1); + + qemu_pid = pg_util_spawn_qemu(socket_path_0, socket_path_1, + mac_addr_0, mac_addr_1, + 1, 1, + glob_vm_path, + glob_vm_key_path, + glob_hugepages_path, &error); + + g_assert(!error); + g_assert(qemu_pid); + + /* Prepare VM's bridge. */ + SSH("brctl addbr br0", qemu_pid, qemu_exit_signal); + SSH("ifconfig br0 up", qemu_pid, qemu_exit_signal); + SSH("ifconfig ens4 up", qemu_pid, qemu_exit_signal); + SSH("ifconfig ens5 up", qemu_pid, qemu_exit_signal); + SSH("brctl addif br0 ens4", qemu_pid, qemu_exit_signal); + SSH("brctl addif br0 ens5", qemu_pid, qemu_exit_signal); + SSH("brctl setfd br0 0", qemu_pid, qemu_exit_signal); + SSH("brctl stp br0 off", qemu_pid, qemu_exit_signal); + ssh_port_id++; + + /* prepare packet to send */ + for (i = 0; i < NB_PKTS; i++) { + pkts[i] = rte_pktmbuf_alloc(mbuf_pool); + g_assert(pkts[i]); + rte_pktmbuf_append(pkts[i], ETHER_MIN_LEN); + /* set random dst/src mac address so the linux guest bridge + * will not filter them + */ + pg_set_mac_addrs(pkts[i], + "52:54:00:12:34:15", "52:54:00:12:34:16"); + /* set size */ + pg_set_ether_type(pkts[i], ETHER_MIN_LEN - ETHER_HDR_LEN - 4); + } + + /* send packet to the guest via one interface */ + pg_brick_burst_to_east(vhost_0, 0, pkts, + pg_mask_firsts(NB_PKTS), &error); + g_assert(!error); + + /* let the packet propagate and flow */ + for (i = 0; i < 10; i++) { + uint16_t count = 0; + + usleep(100000); + pg_brick_poll(vhost_1, &count, &error); + g_assert(!error); + if (count) + break; + } + + result_pkts = pg_brick_east_burst_get(collect, &pkts_mask, &error); + g_assert(!error); + g_assert(result_pkts); + g_assert(pg_brick_rx_bytes(vhost_0) == 0); + g_assert(pg_brick_tx_bytes(vhost_0) != 0); + g_assert(pg_brick_rx_bytes(vhost_1) != 0); + g_assert(pg_brick_tx_bytes(vhost_1) == 0); + + /* kill QEMU */ + pg_util_stop_qemu(qemu_pid, qemu_exit_signal); + + /* free sent packet */ + for (i = 0; i < NB_PKTS; i++) + rte_pktmbuf_free(pkts[i]); + + /* break the graph */ + pg_brick_unlink(collect, &error); + g_assert(!error); + + /* clean up */ + /* pg_brick_decref(vhost_0, &error); */ + pg_brick_destroy(vhost_0); + g_assert(!error); + pg_brick_destroy(vhost_1); + /* pg_brick_decref(vhost_1, &error); */ + g_assert(!error); + pg_brick_decref(collect, &error); + g_assert(!error); + + /* stop vhost */ + pg_vhost_stop(); +} + +static void test_vhost_destroy(void) +{ + int qemu_exit_signal = SIGKILL; + const char mac_addr_0[18] = "52:54:00:12:34:11"; + const char mac_addr_1[18] = "52:54:00:12:34:12"; + struct rte_mempool *mbuf_pool = pg_get_mempool(); + struct pg_brick *vhost_0, *vhost_1, *collect; + struct rte_mbuf *pkts[PG_MAX_PKTS_BURST]; + const char *socket_path_0, *socket_path_1; + struct pg_error *error = NULL; + struct rte_mbuf **result_pkts; + int ret, qemu_pid, i; + uint64_t pkts_mask; + + /* start vhost */ + ret = pg_vhost_start("/tmp", &error); + g_assert(ret == 0); + g_assert(!error); + + /* instanciate brick */ + vhost_0 = pg_vhost_new("vhost-0", PG_VHOST_USER_CLIENT, + &error); + g_assert(!error); + g_assert(vhost_0); + + vhost_1 = pg_vhost_new("vhost-1", PG_VHOST_USER_CLIENT, + &error); + g_assert(!error); + g_assert(vhost_1); + + collect = pg_collect_new("collect", &error); + g_assert(!error); + g_assert(collect); + + /* build the graph */ + pg_brick_link(collect, vhost_1, &error); + g_assert(!error); + + /* spawn first QEMU */ + socket_path_0 = pg_vhost_socket_path(vhost_0); + g_assert(!error); + g_assert(socket_path_0); + socket_path_1 = pg_vhost_socket_path(vhost_1); + g_assert(!error); + g_assert(socket_path_1); + + qemu_pid = pg_util_spawn_qemu(socket_path_0, socket_path_1, + mac_addr_0, mac_addr_1, + 1, 1, + glob_vm_path, + glob_vm_key_path, + glob_hugepages_path, &error); + + g_assert(!error); + g_assert(qemu_pid); + + /* Prepare VM's bridge. */ + SSH("brctl addbr br0", qemu_pid, qemu_exit_signal); + SSH("ifconfig br0 up", qemu_pid, qemu_exit_signal); + SSH("ifconfig ens4 up", qemu_pid, qemu_exit_signal); + SSH("ifconfig ens5 up", qemu_pid, qemu_exit_signal); + SSH("brctl addif br0 ens4", qemu_pid, qemu_exit_signal); + SSH("brctl addif br0 ens5", qemu_pid, qemu_exit_signal); + SSH("brctl setfd br0 0", qemu_pid, qemu_exit_signal); + SSH("brctl stp br0 off", qemu_pid, qemu_exit_signal); + ssh_port_id++; + + /* prepare packet to send */ + for (i = 0; i < NB_PKTS; i++) { + pkts[i] = rte_pktmbuf_alloc(mbuf_pool); + g_assert(pkts[i]); + rte_pktmbuf_append(pkts[i], ETHER_MIN_LEN); + /* set random dst/src mac address so the linux guest bridge + * will not filter them + */ + pg_set_mac_addrs(pkts[i], + "52:54:00:12:34:15", "52:54:00:12:34:16"); + /* set size */ + pg_set_ether_type(pkts[i], ETHER_MIN_LEN - ETHER_HDR_LEN - 4); + } + + /* send packet to the guest via one interface */ + pg_brick_burst_to_east(vhost_0, 0, pkts, + pg_mask_firsts(NB_PKTS), &error); + g_assert(!error); + + /* let the packet propagate and flow */ + for (i = 0; i < 10; i++) { + uint16_t count = 0; + + usleep(100000); + pg_brick_poll(vhost_1, &count, &error); + g_assert(!error); + if (count) + break; + } + + result_pkts = pg_brick_east_burst_get(collect, &pkts_mask, &error); + g_assert(!error); + g_assert(result_pkts); + g_assert(pg_brick_rx_bytes(vhost_0) == 0); + g_assert(pg_brick_tx_bytes(vhost_0) != 0); + g_assert(pg_brick_rx_bytes(vhost_1) != 0); + g_assert(pg_brick_tx_bytes(vhost_1) == 0); + + /* kill QEMU */ + pg_util_stop_qemu(qemu_pid, qemu_exit_signal); + + /* free sent packet */ + for (i = 0; i < NB_PKTS; i++) + rte_pktmbuf_free(pkts[i]); + + /* break the graph */ + pg_brick_unlink(collect, &error); + g_assert(!error); + + /* clean up */ + /* pg_brick_decref(vhost_0, &error); */ + pg_brick_destroy(vhost_0); + g_assert(!error); + pg_brick_destroy(vhost_1); + /* pg_brick_decref(vhost_1, &error); */ + g_assert(!error); + pg_brick_decref(collect, &error); + g_assert(!error); + + /* stop vhost */ + pg_vhost_stop(); +} + void test_vhost(void) { + pg_test_add_func("/vhost/as_cli", test_vhost_as_cli); pg_test_add_func("/vhost/net_classics", test_vhost_net_classics); pg_test_add_func("/vhost/flow", test_vhost_flow); pg_test_add_func("/vhost/multivm", test_vhost_multivm);