#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 "virtio-net.h"
#include "xen_vhost.h"
struct virtio_watch {
struct xs_handle *xs;
int watch_fd;
};
static struct virtio_net_config_ll *ll_root = NULL;
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 *)
}
static int
init_watch(void)
{
struct xs_handle *xs;
int ret;
int fd;
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;
}
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) {
ll_dev->dev.virtqueue_rx = NULL;
}
if (ll_dev->dev.virtqueue_tx) {
ll_dev->dev.virtqueue_tx = NULL;
}
free(ll_dev);
}
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) {
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 {
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;
}
}
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)
{
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;
}
}
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;
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;
}
static void
init_dev(struct virtio_net *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;
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;
init_dev(&new_ll_dev->dev);
size = vring_size(vq_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));
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_config_ll_entry(new_ll_dev);
return new_ll_dev;
err:
free(new_ll_dev);
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]);
for (i = 0; i < guest->pool_num; i++)
cleanup_mempool(&guest->mempool[i]);
free(guest);
return;
}
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;
struct xen_guest *guest = NULL;
guest = get_xen_guest(dom_id);
if (guest == NULL)
return;
ll_dev_cur_ctx = get_config_ll_entry(virtio_idx, dom_id);
while (ll_dev_cur != NULL) {
if (ll_dev_cur == ll_dev_cur_ctx) {
if ((ll_dev_cur->dev.flags & VIRTIO_DEV_RUNNING))
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,
" #####%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;
}
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;
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;
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 (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);
}
}
}
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) {
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");
TAILQ_REMOVE(&guest_root, guest, next);
destroy_guest(guest);
continue;
}
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");
}
}
}
free(vec);
}
return;
}
int
{
notify_ops = ops;
if (xenhost_init())
return -1;
return 0;
}