aboutsummaryrefslogtreecommitdiffstats
path: root/examples/vhost_xen/vhost_monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/vhost_xen/vhost_monitor.c')
-rw-r--r--examples/vhost_xen/vhost_monitor.c595
1 files changed, 595 insertions, 0 deletions
diff --git a/examples/vhost_xen/vhost_monitor.c b/examples/vhost_xen/vhost_monitor.c
new file mode 100644
index 00000000..fb9606bf
--- /dev/null
+++ b/examples/vhost_xen/vhost_monitor.c
@@ -0,0 +1,595 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <xen/xen-compat.h>
+#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00040200
+#include <xs.h>
+#else
+#include <xenstore.h>
+#endif
+#include <linux/virtio_ring.h>
+#include <linux/virtio_pci.h>
+#include <linux/virtio_net.h>
+
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+
+#include "virtio-net.h"
+#include "xen_vhost.h"
+
+struct virtio_watch {
+ struct xs_handle *xs;
+ int watch_fd;
+};
+
+
+/* device ops to add/remove device to/from data core. */
+static struct virtio_net_device_ops const *notify_ops;
+
+/* root address of the linked list in the configuration core. */
+static struct virtio_net_config_ll *ll_root = NULL;
+
+/* root address of VM. */
+static struct xen_guestlist guest_root;
+
+static struct virtio_watch watch;
+
+static void
+vq_vring_init(struct vhost_virtqueue *vq, unsigned int num, uint8_t *p,
+ unsigned long align)
+{
+ vq->size = num;
+ vq->desc = (struct vring_desc *) p;
+ vq->avail = (struct vring_avail *) (p +
+ num * sizeof(struct vring_desc));
+ vq->used = (void *)
+ RTE_ALIGN_CEIL( (uintptr_t)(&vq->avail->ring[num]), align);
+
+}
+
+static int
+init_watch(void)
+{
+ struct xs_handle *xs;
+ int ret;
+ int fd;
+
+ /* get a connection to the daemon */
+ xs = xs_daemon_open();
+ if (xs == NULL) {
+ RTE_LOG(ERR, XENHOST, "xs_daemon_open failed\n");
+ return -1;
+ }
+
+ ret = xs_watch(xs, "/local/domain", "mytoken");
+ if (ret == 0) {
+ RTE_LOG(ERR, XENHOST, "%s: xs_watch failed\n", __func__);
+ xs_daemon_close(xs);
+ return -1;
+ }
+
+ /* We are notified of read availability on the watch via the file descriptor. */
+ fd = xs_fileno(xs);
+ watch.xs = xs;
+ watch.watch_fd = fd;
+
+ TAILQ_INIT(&guest_root);
+ return 0;
+}
+
+static struct xen_guest *
+get_xen_guest(int dom_id)
+{
+ struct xen_guest *guest = NULL;
+
+ TAILQ_FOREACH(guest, &guest_root, next) {
+ if(guest->dom_id == dom_id)
+ return guest;
+ }
+
+ return NULL;
+}
+
+
+static struct xen_guest *
+add_xen_guest(int32_t dom_id)
+{
+ struct xen_guest *guest = NULL;
+
+ if ((guest = get_xen_guest(dom_id)) != NULL)
+ return guest;
+
+ guest = calloc(1, sizeof(struct xen_guest));
+ if (guest) {
+ RTE_LOG(ERR, XENHOST, " %s: return newly created guest with %d rings\n", __func__, guest->vring_num);
+ TAILQ_INSERT_TAIL(&guest_root, guest, next);
+ guest->dom_id = dom_id;
+ }
+
+ return guest;
+}
+
+static void
+cleanup_device(struct virtio_net_config_ll *ll_dev)
+{
+ if (ll_dev == NULL)
+ return;
+ if (ll_dev->dev.virtqueue_rx) {
+ rte_free(ll_dev->dev.virtqueue_rx);
+ ll_dev->dev.virtqueue_rx = NULL;
+ }
+ if (ll_dev->dev.virtqueue_tx) {
+ rte_free(ll_dev->dev.virtqueue_tx);
+ ll_dev->dev.virtqueue_tx = NULL;
+ }
+ free(ll_dev);
+}
+
+/*
+ * Add entry containing a device to the device configuration linked list.
+ */
+static void
+add_config_ll_entry(struct virtio_net_config_ll *new_ll_dev)
+{
+ struct virtio_net_config_ll *ll_dev = ll_root;
+
+ /* If ll_dev == NULL then this is the first device so go to else */
+ if (ll_dev) {
+ /* If the 1st device_id != 0 then we insert our device here. */
+ if (ll_dev->dev.device_fh != 0) {
+ new_ll_dev->dev.device_fh = 0;
+ new_ll_dev->next = ll_dev;
+ ll_root = new_ll_dev;
+ } else {
+ /* increment through the ll until we find un unused device_id,
+ * insert the device at that entry
+ */
+ while ((ll_dev->next != NULL) && (ll_dev->dev.device_fh == (ll_dev->next->dev.device_fh - 1)))
+ ll_dev = ll_dev->next;
+
+ new_ll_dev->dev.device_fh = ll_dev->dev.device_fh + 1;
+ new_ll_dev->next = ll_dev->next;
+ ll_dev->next = new_ll_dev;
+ }
+ } else {
+ ll_root = new_ll_dev;
+ ll_root->dev.device_fh = 0;
+ }
+}
+
+
+/*
+ * Remove an entry from the device configuration linked list.
+ */
+static struct virtio_net_config_ll *
+rm_config_ll_entry(struct virtio_net_config_ll *ll_dev, struct virtio_net_config_ll *ll_dev_last)
+{
+ /* First remove the device and then clean it up. */
+ if (ll_dev == ll_root) {
+ ll_root = ll_dev->next;
+ cleanup_device(ll_dev);
+ return ll_root;
+ } else {
+ ll_dev_last->next = ll_dev->next;
+ cleanup_device(ll_dev);
+ return ll_dev_last->next;
+ }
+}
+
+/*
+ * Retrieves an entry from the devices configuration linked list.
+ */
+static struct virtio_net_config_ll *
+get_config_ll_entry(unsigned int virtio_idx, unsigned int dom_id)
+{
+ struct virtio_net_config_ll *ll_dev = ll_root;
+
+ /* Loop through linked list until the dom_id is found. */
+ while (ll_dev != NULL) {
+ if (ll_dev->dev.dom_id == dom_id && ll_dev->dev.virtio_idx == virtio_idx)
+ return ll_dev;
+ ll_dev = ll_dev->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Initialise all variables in device structure.
+ */
+static void
+init_dev(struct virtio_net *dev)
+{
+ RTE_SET_USED(dev);
+}
+
+
+static struct
+virtio_net_config_ll *new_device(unsigned int virtio_idx, struct xen_guest *guest)
+{
+ struct virtio_net_config_ll *new_ll_dev;
+ struct vhost_virtqueue *virtqueue_rx, *virtqueue_tx;
+ size_t size, vq_ring_size, vq_size = VQ_DESC_NUM;
+ void *vq_ring_virt_mem;
+ uint64_t gpa;
+ uint32_t i;
+
+ /* Setup device and virtqueues. */
+ new_ll_dev = calloc(1, sizeof(struct virtio_net_config_ll));
+ virtqueue_rx = rte_zmalloc(NULL, sizeof(struct vhost_virtqueue), RTE_CACHE_LINE_SIZE);
+ virtqueue_tx = rte_zmalloc(NULL, sizeof(struct vhost_virtqueue), RTE_CACHE_LINE_SIZE);
+ if (new_ll_dev == NULL || virtqueue_rx == NULL || virtqueue_tx == NULL)
+ goto err;
+
+ new_ll_dev->dev.virtqueue_rx = virtqueue_rx;
+ new_ll_dev->dev.virtqueue_tx = virtqueue_tx;
+ new_ll_dev->dev.dom_id = guest->dom_id;
+ new_ll_dev->dev.virtio_idx = virtio_idx;
+ /* Initialise device and virtqueues. */
+ init_dev(&new_ll_dev->dev);
+
+ size = vring_size(vq_size, VIRTIO_PCI_VRING_ALIGN);
+ vq_ring_size = RTE_ALIGN_CEIL(size, VIRTIO_PCI_VRING_ALIGN);
+ (void)vq_ring_size;
+
+ vq_ring_virt_mem = guest->vring[virtio_idx].rxvring_addr;
+ vq_vring_init(virtqueue_rx, vq_size, vq_ring_virt_mem, VIRTIO_PCI_VRING_ALIGN);
+ virtqueue_rx->size = vq_size;
+ virtqueue_rx->vhost_hlen = sizeof(struct virtio_net_hdr);
+
+ vq_ring_virt_mem = guest->vring[virtio_idx].txvring_addr;
+ vq_vring_init(virtqueue_tx, vq_size, vq_ring_virt_mem, VIRTIO_PCI_VRING_ALIGN);
+ virtqueue_tx->size = vq_size;
+ memcpy(&new_ll_dev->dev.mac_address, &guest->vring[virtio_idx].addr, sizeof(struct ether_addr));
+
+ /* virtio_memory has to be one per domid */
+ new_ll_dev->dev.mem = malloc(sizeof(struct virtio_memory) + sizeof(struct virtio_memory_regions) * MAX_XENVIRT_MEMPOOL);
+ new_ll_dev->dev.mem->nregions = guest->pool_num;
+ for (i = 0; i < guest->pool_num; i++) {
+ gpa = new_ll_dev->dev.mem->regions[i].guest_phys_address =
+ (uint64_t)((uintptr_t)guest->mempool[i].gva);
+ new_ll_dev->dev.mem->regions[i].guest_phys_address_end =
+ gpa + guest->mempool[i].mempfn_num * getpagesize();
+ new_ll_dev->dev.mem->regions[i].address_offset =
+ (uint64_t)((uintptr_t)guest->mempool[i].hva -
+ (uintptr_t)gpa);
+ }
+
+ new_ll_dev->next = NULL;
+
+ /* Add entry to device configuration linked list. */
+ add_config_ll_entry(new_ll_dev);
+ return new_ll_dev;
+err:
+ free(new_ll_dev);
+ rte_free(virtqueue_rx);
+ rte_free(virtqueue_tx);
+
+ return NULL;
+}
+
+static void
+destroy_guest(struct xen_guest *guest)
+{
+ uint32_t i;
+
+ for (i = 0; i < guest->vring_num; i++)
+ cleanup_vring(&guest->vring[i]);
+ /* clean mempool */
+ for (i = 0; i < guest->pool_num; i++)
+ cleanup_mempool(&guest->mempool[i]);
+ free(guest);
+
+ return;
+}
+
+/*
+ * This function will cleanup the device and remove it from device configuration linked list.
+ */
+static void
+destroy_device(unsigned int virtio_idx, unsigned int dom_id)
+{
+ struct virtio_net_config_ll *ll_dev_cur_ctx, *ll_dev_last = NULL;
+ struct virtio_net_config_ll *ll_dev_cur = ll_root;
+
+ /* clean virtio device */
+ struct xen_guest *guest = NULL;
+ guest = get_xen_guest(dom_id);
+ if (guest == NULL)
+ return;
+
+ /* Find the linked list entry for the device to be removed. */
+ ll_dev_cur_ctx = get_config_ll_entry(virtio_idx, dom_id);
+ while (ll_dev_cur != NULL) {
+ /* If the device is found or a device that doesn't exist is found then it is removed. */
+ if (ll_dev_cur == ll_dev_cur_ctx) {
+ if ((ll_dev_cur->dev.flags & VIRTIO_DEV_RUNNING))
+ notify_ops->destroy_device(&(ll_dev_cur->dev));
+ ll_dev_cur = rm_config_ll_entry(ll_dev_cur, ll_dev_last);
+ } else {
+ ll_dev_last = ll_dev_cur;
+ ll_dev_cur = ll_dev_cur->next;
+ }
+ }
+ RTE_LOG(INFO, XENHOST, " %s guest:%p vring:%p rxvring:%p txvring:%p flag:%p\n",
+ __func__, guest, &guest->vring[virtio_idx], guest->vring[virtio_idx].rxvring_addr, guest->vring[virtio_idx].txvring_addr, guest->vring[virtio_idx].flag);
+ cleanup_vring(&guest->vring[virtio_idx]);
+ guest->vring[virtio_idx].removed = 1;
+ guest->vring_num -= 1;
+}
+
+
+
+
+static void
+watch_unmap_event(void)
+{
+ int i;
+ struct xen_guest *guest = NULL;
+ bool remove_request;
+
+ TAILQ_FOREACH(guest, &guest_root, next) {
+ for (i = 0; i < MAX_VIRTIO; i++) {
+ if (guest->vring[i].dom_id && guest->vring[i].removed == 0 && *guest->vring[i].flag == 0) {
+ RTE_LOG(INFO, XENHOST, "\n\n");
+ RTE_LOG(INFO, XENHOST, " #####%s: (%d, %d) to be removed\n",
+ __func__,
+ guest->vring[i].dom_id,
+ i);
+ destroy_device(i, guest->dom_id);
+ RTE_LOG(INFO, XENHOST, " %s: DOM %u, vring num: %d\n",
+ __func__,
+ guest->dom_id,
+ guest->vring_num);
+ }
+ }
+ }
+
+_find_next_remove:
+ guest = NULL;
+ remove_request = false;
+ TAILQ_FOREACH(guest, &guest_root, next) {
+ if (guest->vring_num == 0) {
+ remove_request = true;
+ break;
+ }
+ }
+ if (remove_request == true) {
+ TAILQ_REMOVE(&guest_root, guest, next);
+ RTE_LOG(INFO, XENHOST, " #####%s: destroy guest (%d)\n", __func__, guest->dom_id);
+ destroy_guest(guest);
+ goto _find_next_remove;
+ }
+ return;
+}
+
+/*
+ * OK, if the guest starts first, it is ok.
+ * if host starts first, it is ok.
+ * if guest starts, and has run for sometime, and host stops and restarts,
+ * then last_used_idx 0? how to solve this. */
+
+static void virtio_init(void)
+{
+ uint32_t len, e_num;
+ uint32_t i,j;
+ char **dom;
+ char *status;
+ int dom_id;
+ char path[PATH_MAX];
+ char node[PATH_MAX];
+ xs_transaction_t th;
+ struct xen_guest *guest;
+ struct virtio_net_config_ll *net_config;
+ char *end;
+ int val;
+
+ /* init env for watch the node */
+ if (init_watch() < 0)
+ return;
+
+ dom = xs_directory(watch.xs, XBT_NULL, "/local/domain", &e_num);
+
+ for (i = 0; i < e_num; i++) {
+ errno = 0;
+ dom_id = strtol(dom[i], &end, 0);
+ if (errno != 0 || end == NULL || dom_id == 0)
+ continue;
+
+ for (j = 0; j < RTE_MAX_ETHPORTS; j++) {
+ snprintf(node, PATH_MAX, "%s%d", VIRTIO_START, j);
+ snprintf(path, PATH_MAX, XEN_VM_NODE_FMT,
+ dom_id, node);
+
+ th = xs_transaction_start(watch.xs);
+ status = xs_read(watch.xs, th, path, &len);
+ xs_transaction_end(watch.xs, th, false);
+
+ if (status == NULL)
+ break;
+
+ /* if there's any valid virtio device */
+ errno = 0;
+ val = strtol(status, &end, 0);
+ if (errno != 0 || end == NULL || dom_id == 0)
+ val = 0;
+ if (val == 1) {
+ guest = add_xen_guest(dom_id);
+ if (guest == NULL)
+ continue;
+ RTE_LOG(INFO, XENHOST, " there's a new virtio existed, new a virtio device\n\n");
+
+ RTE_LOG(INFO, XENHOST, " parse_vringnode dom_id %d virtioidx %d\n",dom_id,j);
+ if (parse_vringnode(guest, j)) {
+ RTE_LOG(ERR, XENHOST, " there is invalid information in xenstore\n");
+ TAILQ_REMOVE(&guest_root, guest, next);
+ destroy_guest(guest);
+
+ continue;
+ }
+
+ /*if pool_num > 0, then mempool has already been parsed*/
+ if (guest->pool_num == 0 && parse_mempoolnode(guest)) {
+ RTE_LOG(ERR, XENHOST, " there is error information in xenstore\n");
+ TAILQ_REMOVE(&guest_root, guest, next);
+ destroy_guest(guest);
+ continue;
+ }
+
+ net_config = new_device(j, guest);
+ /* every thing is ready now, added into data core */
+ notify_ops->new_device(&net_config->dev);
+ }
+ }
+ }
+
+ free(dom);
+ return;
+}
+
+void
+virtio_monitor_loop(void)
+{
+ char **vec;
+ xs_transaction_t th;
+ char *buf;
+ unsigned int len;
+ unsigned int dom_id;
+ uint32_t virtio_idx;
+ struct xen_guest *guest;
+ struct virtio_net_config_ll *net_config;
+ enum fieldnames {
+ FLD_NULL = 0,
+ FLD_LOCAL,
+ FLD_DOMAIN,
+ FLD_ID,
+ FLD_CONTROL,
+ FLD_DPDK,
+ FLD_NODE,
+ _NUM_FLD
+ };
+ char *str_fld[_NUM_FLD];
+ char *str;
+ char *end;
+
+ virtio_init();
+ while (1) {
+ watch_unmap_event();
+
+ usleep(50);
+ vec = xs_check_watch(watch.xs);
+
+ if (vec == NULL)
+ continue;
+
+ th = xs_transaction_start(watch.xs);
+
+ buf = xs_read(watch.xs, th, vec[XS_WATCH_PATH],&len);
+ xs_transaction_end(watch.xs, th, false);
+
+ if (buf) {
+ /* theres' some node for vhost existed */
+ if (rte_strsplit(vec[XS_WATCH_PATH], strnlen(vec[XS_WATCH_PATH], PATH_MAX),
+ str_fld, _NUM_FLD, '/') == _NUM_FLD) {
+ if (strstr(str_fld[FLD_NODE], VIRTIO_START)) {
+ errno = 0;
+ str = str_fld[FLD_ID];
+ dom_id = strtoul(str, &end, 0);
+ if (errno != 0 || end == NULL || end == str ) {
+ RTE_LOG(INFO, XENHOST, "invalid domain id\n");
+ continue;
+ }
+
+ errno = 0;
+ str = str_fld[FLD_NODE] + sizeof(VIRTIO_START) - 1;
+ virtio_idx = strtoul(str, &end, 0);
+ if (errno != 0 || end == NULL || end == str
+ || virtio_idx > MAX_VIRTIO) {
+ RTE_LOG(INFO, XENHOST, "invalid virtio idx\n");
+ continue;
+ }
+ RTE_LOG(INFO, XENHOST, " #####virtio dev (%d, %d) is started\n", dom_id, virtio_idx);
+
+ guest = add_xen_guest(dom_id);
+ if (guest == NULL)
+ continue;
+ guest->dom_id = dom_id;
+ if (parse_vringnode(guest, virtio_idx)) {
+ RTE_LOG(ERR, XENHOST, " there is invalid information in xenstore\n");
+ /*guest newly created? guest existed ?*/
+ TAILQ_REMOVE(&guest_root, guest, next);
+ destroy_guest(guest);
+ continue;
+ }
+ /*if pool_num > 0, then mempool has already been parsed*/
+ if (guest->pool_num == 0 && parse_mempoolnode(guest)) {
+ RTE_LOG(ERR, XENHOST, " there is error information in xenstore\n");
+ TAILQ_REMOVE(&guest_root, guest, next);
+ destroy_guest(guest);
+ continue;
+ }
+
+
+ net_config = new_device(virtio_idx, guest);
+ RTE_LOG(INFO, XENHOST, " Add to dataplane core\n");
+ notify_ops->new_device(&net_config->dev);
+
+ }
+ }
+ }
+
+ free(vec);
+ }
+ return;
+}
+
+/*
+ * Register ops so that we can add/remove device to data core.
+ */
+int
+init_virtio_xen(struct virtio_net_device_ops const *const ops)
+{
+ notify_ops = ops;
+ if (xenhost_init())
+ return -1;
+ return 0;
+}