diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 780714701..c37e9bce1 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -62,6 +62,7 @@ SRCS += hw/usb_core.c SRCS += hw/uart_core.c SRCS += hw/pci/virtio/virtio.c SRCS += hw/pci/virtio/virtio_kernel.c +SRCS += hw/pci/virtio/vhost.c SRCS += hw/platform/usb_mouse.c SRCS += hw/platform/usb_pmapper.c SRCS += hw/platform/atkbdc.c diff --git a/devicemodel/hw/pci/virtio/vhost.c b/devicemodel/hw/pci/virtio/vhost.c new file mode 100644 index 000000000..ef8ac230b --- /dev/null +++ b/devicemodel/hw/pci/virtio/vhost.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" +#include "pci_core.h" +#include "irq.h" +#include "vmmapi.h" +#include "vhost.h" + +static int vhost_debug; +#define LOG_TAG "vhost: " +#define DPRINTF(fmt, args...) \ + do { if (vhost_debug) printf(LOG_TAG fmt, ##args); } while (0) +#define WPRINTF(fmt, args...) printf(LOG_TAG fmt, ##args) + +static void +vhost_kernel_init(struct vhost_dev *vdev, struct virtio_base *base, + int fd, int vq_idx, uint32_t busyloop_timeout) +{ + /* to be implemented */ +} + +static void +vhost_kernel_deinit(struct vhost_dev *vdev) +{ + /* to be implemented */ +} + +static int +vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *vdev, + struct vhost_vring_state *s) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_kernel_set_features(struct vhost_dev *vdev, + uint64_t features) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_kernel_get_features(struct vhost_dev *vdev, + uint64_t *features) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_kernel_set_owner(struct vhost_dev *vdev) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_kernel_reset_device(struct vhost_dev *vdev) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_kernel_net_set_backend(struct vhost_dev *vdev, + struct vhost_vring_file *file) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_vq_init(struct vhost_dev *vdev, int idx) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_vq_deinit(struct vhost_vq *vq) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_vq_start(struct vhost_dev *vdev, int idx) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_vq_stop(struct vhost_dev *vdev, int idx) +{ + /* to be implemented */ + return -1; +} + +static int +vhost_set_mem_table(struct vhost_dev *vdev) +{ + /* to be implemented */ + return -1; +} + +int +vhost_dev_init(struct vhost_dev *vdev, + struct virtio_base *base, + int fd, + int vq_idx, + uint64_t vhost_features, + uint64_t vhost_ext_features, + uint32_t busyloop_timeout) +{ + uint64_t features; + int i, rc; + + /* sanity check */ + if (!base || !base->queues || !base->vops) { + WPRINTF("virtio_base is not initialized\n"); + goto fail; + } + + if (!vdev->vqs || vdev->nvqs == 0) { + WPRINTF("virtqueue is not initialized\n"); + goto fail; + } + + if (vq_idx + vdev->nvqs > base->vops->nvq) { + WPRINTF("invalid vq_idx: %d\n", vq_idx); + goto fail; + } + + vhost_kernel_init(vdev, base, fd, vq_idx, busyloop_timeout); + + rc = vhost_kernel_get_features(vdev, &features); + if (rc < 0) { + WPRINTF("vhost_get_features failed\n"); + goto fail; + } + + for (i = 0; i < vdev->nvqs; i++) { + rc = vhost_vq_init(vdev, i); + if (rc < 0) + goto fail; + } + + /* specific backend features to vhost */ + vdev->vhost_ext_features = vhost_ext_features & features; + + /* features supported by vhost */ + vdev->vhost_features = vhost_features & features; + + /* + * If the features bits are not supported by either vhost kernel + * mediator or configuration of device model(specified by + * vhost_features), they should be disabled in device_caps, + * which expose as virtio host_features for virtio FE driver. + */ + vdev->base->device_caps &= ~(vhost_features ^ features); + vdev->started = false; + + return 0; + +fail: + vhost_dev_deinit(vdev); + return -1; +} + +int +vhost_dev_deinit(struct vhost_dev *vdev) +{ + int i; + + if (!vdev->base || !vdev->base->queues || !vdev->base->vops) + return -1; + + for (i = 0; i < vdev->nvqs; i++) + vhost_vq_deinit(&vdev->vqs[i]); + + vhost_kernel_deinit(vdev); + + return 0; +} + +int +vhost_dev_start(struct vhost_dev *vdev) +{ + struct vhost_vring_state state; + uint64_t features; + int i, rc; + + if (vdev->started) + return 0; + + /* sanity check */ + if (!vdev->base || !vdev->base->queues || !vdev->base->vops) { + WPRINTF("virtio_base is not initialized\n"); + goto fail; + } + + if ((vdev->base->status & VIRTIO_CR_STATUS_DRIVER_OK) == 0) { + WPRINTF("status error 0x%x\n", vdev->base->status); + goto fail; + } + + rc = vhost_kernel_set_owner(vdev); + if (rc < 0) { + WPRINTF("vhost_set_owner failed\n"); + goto fail; + } + + /* set vhost internal features */ + features = (vdev->base->negotiated_caps & vdev->vhost_features) | + vdev->vhost_ext_features; + rc = vhost_kernel_set_features(vdev, features); + if (rc < 0) { + WPRINTF("set_features failed\n"); + goto fail; + } + DPRINTF("set_features: 0x%lx\n", features); + + /* set memory table */ + rc = vhost_set_mem_table(vdev); + if (rc < 0) { + WPRINTF("set_mem_table failed\n"); + goto fail; + } + + /* config busyloop timeout */ + if (vdev->busyloop_timeout) { + state.num = vdev->busyloop_timeout; + for (i = 0; i < vdev->nvqs; i++) { + state.index = i; + rc = vhost_kernel_set_vring_busyloop_timeout(vdev, + &state); + if (rc < 0) { + WPRINTF("set_busyloop_timeout failed\n"); + goto fail; + } + } + } + + /* start vhost virtqueue */ + for (i = 0; i < vdev->nvqs; i++) { + rc = vhost_vq_start(vdev, i); + if (rc < 0) + goto fail_vq; + } + + vdev->started = true; + return 0; + +fail_vq: + while (--i >= 0) + vhost_vq_stop(vdev, i); +fail: + return -1; +} + +int +vhost_dev_stop(struct vhost_dev *vdev) +{ + int i, rc = 0; + + for (i = 0; i < vdev->nvqs; i++) + vhost_vq_stop(vdev, i); + + /* the following are done by this ioctl: + * 1) resources of the vhost dev are freed + * 2) vhost virtqueues are reset + */ + rc = vhost_kernel_reset_device(vdev); + if (rc < 0) { + WPRINTF("vhost_reset_device failed\n"); + rc = -1; + } + + vdev->started = false; + return rc; +} + +int +vhost_net_set_backend(struct vhost_dev *vdev, int backend_fd) +{ + struct vhost_vring_file file; + int rc, i; + + file.fd = backend_fd; + for (i = 0; i < vdev->nvqs; i++) { + file.index = i; + rc = vhost_kernel_net_set_backend(vdev, &file); + if (rc < 0) + goto fail; + } + + return 0; +fail: + file.fd = -1; + while (--i >= 0) { + file.index = i; + vhost_kernel_net_set_backend(vdev, &file); + } + + return -1; +} diff --git a/devicemodel/include/vhost.h b/devicemodel/include/vhost.h new file mode 100644 index 000000000..24172e282 --- /dev/null +++ b/devicemodel/include/vhost.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __VHOST_H__ +#define __VHOST_H__ + +#include "virtio.h" +#include "mevent.h" + +struct vhost_vq { + int kick_fd; /**< fd of kick eventfd */ + int call_fd; /**< fd of call eventfd */ + int idx; /**< index of this vq in vhost dev */ + struct mevent *mevp; /**< mevent for call eventfd */ + struct vhost_dev *dev; /**< pointer to vhost_dev */ +}; + +struct vhost_dev { + /** + * backpointer to virtio_base + */ + struct virtio_base *base; + + /** + * pointer to vhost_vq array + */ + struct vhost_vq *vqs; + + /** + * number of virtqueues + */ + int nvqs; + + /** + * vhost chardev fd + */ + int fd; + + /** + * first vq's index in virtio_vq_info + */ + int vq_idx; + + /** + * supported virtio defined features + */ + uint64_t vhost_features; + + /** + * vhost self-defined internal features bits used for + * communicate between vhost user-space and kernel-space modules + */ + uint64_t vhost_ext_features; + + /** + * vq busyloop timeout in us + */ + uint32_t busyloop_timeout; + + /** + * whether vhost is started + */ + bool started; +}; + +/** + * @brief vhost_dev initialization. + * + * This interface is called to initialize the vhost_dev. It must be called + * before the actual feature negoitiation with the guest OS starts. + * + * @param vdev Pointer to struct vhost_dev. + * @param base Pointer to struct virtio_base. + * @param fd fd of the vhost chardev. + * @param vq_idx The first virtqueue which would be used by this vhost dev. + * @param vhost_features Subset of vhost features which would be enabled. + * @param vhost_ext_features Specific vhost internal features to be enabled. + * @param busyloop_timeout Busy loop timeout in us. + * + * @return 0 on success and -1 on failure. + */ +int vhost_dev_init(struct vhost_dev *vdev, struct virtio_base *base, int fd, + int vq_idx, uint64_t vhost_features, + uint64_t vhost_ext_features, uint32_t busyloop_timeout); + +/** + * @brief vhost_dev cleanup. + * + * This interface is called to cleanup the vhost_dev. + * + * @param vdev Pointer to struct vhost_dev. + * + * @return 0 on success and -1 on failure. + */ +int vhost_dev_deinit(struct vhost_dev *vdev); + +/** + * @brief start vhost data plane. + * + * This interface is called to start the data plane in vhost. + * + * @param vdev Pointer to struct vhost_dev. + * + * @return 0 on success and -1 on failure. + */ +int vhost_dev_start(struct vhost_dev *vdev); + +/** + * @brief stop vhost data plane. + * + * This interface is called to stop the data plane in vhost. + * + * @param vdev Pointer to struct vhost_dev. + * + * @return 0 on success and -1 on failure. + */ +int vhost_dev_stop(struct vhost_dev *vdev); + +/** + * @brief set backend fd of vhost net. + * + * This interface is called to set the backend fd (for example tap fd) + * to vhost. + * + * @param vdev Pointer to struct vhost_dev. + * @param backend_fd fd of backend (for example tap fd). + * + * @return 0 on success and -1 on failure. + */ +int vhost_net_set_backend(struct vhost_dev *vdev, int backend_fd); + +#endif