vhost-vdpa: introduce vhost-vdpa backend
Currently we have 2 types of vhost backends in QEMU: vhost kernel and vhost-user. The above patch provides a generic device for vDPA purpose, this vDPA device exposes to user space a non-vendor-specific configuration interface for setting up a vhost HW accelerator, this patch set introduces a third vhost backend called vhost-vdpa based on the vDPA interface. Vhost-vdpa usage: qemu-system-x86_64 -cpu host -enable-kvm \ ...... -netdev type=vhost-vdpa,vhostdev=/dev/vhost-vdpa-id,id=vhost-vdpa0 \ -device virtio-net-pci,netdev=vhost-vdpa0,page-per-vq=on \ Signed-off-by: Lingshan zhu <lingshan.zhu@intel.com> Signed-off-by: Tiwei Bie <tiwei.bie@intel.com> Signed-off-by: Cindy Lu <lulu@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Message-Id: <20200701145538.22333-14-lulu@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Jason Wang <jasowang@redhat.com>
This commit is contained in:
parent
38140cc4d9
commit
108a64818e
21
configure
vendored
21
configure
vendored
@ -1575,6 +1575,10 @@ for opt do
|
||||
;;
|
||||
--enable-vhost-user) vhost_user="yes"
|
||||
;;
|
||||
--disable-vhost-vdpa) vhost_vdpa="no"
|
||||
;;
|
||||
--enable-vhost-vdpa) vhost_vdpa="yes"
|
||||
;;
|
||||
--disable-vhost-kernel) vhost_kernel="no"
|
||||
;;
|
||||
--enable-vhost-kernel) vhost_kernel="yes"
|
||||
@ -1883,6 +1887,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
vhost-crypto vhost-user-crypto backend support
|
||||
vhost-kernel vhost kernel backend support
|
||||
vhost-user vhost-user backend support
|
||||
vhost-vdpa vhost-vdpa kernel backend support
|
||||
spice spice
|
||||
rbd rados block device (rbd)
|
||||
libiscsi iscsi support
|
||||
@ -2394,6 +2399,10 @@ test "$vhost_user" = "" && vhost_user=yes
|
||||
if test "$vhost_user" = "yes" && test "$mingw32" = "yes"; then
|
||||
error_exit "vhost-user isn't available on win32"
|
||||
fi
|
||||
test "$vhost_vdpa" = "" && vhost_vdpa=$linux
|
||||
if test "$vhost_vdpa" = "yes" && test "$linux" != "yes"; then
|
||||
error_exit "vhost-vdpa is only available on Linux"
|
||||
fi
|
||||
test "$vhost_kernel" = "" && vhost_kernel=$linux
|
||||
if test "$vhost_kernel" = "yes" && test "$linux" != "yes"; then
|
||||
error_exit "vhost-kernel is only available on Linux"
|
||||
@ -2422,6 +2431,11 @@ test "$vhost_user_fs" = "" && vhost_user_fs=$vhost_user
|
||||
if test "$vhost_user_fs" = "yes" && test "$vhost_user" = "no"; then
|
||||
error_exit "--enable-vhost-user-fs requires --enable-vhost-user"
|
||||
fi
|
||||
#vhost-vdpa backends
|
||||
test "$vhost_net_vdpa" = "" && vhost_net_vdpa=$vhost_vdpa
|
||||
if test "$vhost_net_vdpa" = "yes" && test "$vhost_vdpa" = "no"; then
|
||||
error_exit "--enable-vhost-net-vdpa requires --enable-vhost-vdpa"
|
||||
fi
|
||||
|
||||
# OR the vhost-kernel and vhost-user values for simplicity
|
||||
if test "$vhost_net" = ""; then
|
||||
@ -6936,6 +6950,7 @@ echo "vhost-scsi support $vhost_scsi"
|
||||
echo "vhost-vsock support $vhost_vsock"
|
||||
echo "vhost-user support $vhost_user"
|
||||
echo "vhost-user-fs support $vhost_user_fs"
|
||||
echo "vhost-vdpa support $vhost_vdpa"
|
||||
echo "Trace backends $trace_backends"
|
||||
if have_backend "simple"; then
|
||||
echo "Trace output file $trace_file-<pid>"
|
||||
@ -7437,6 +7452,9 @@ fi
|
||||
if test "$vhost_net_user" = "yes" ; then
|
||||
echo "CONFIG_VHOST_NET_USER=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vhost_net_vdpa" = "yes" ; then
|
||||
echo "CONFIG_VHOST_NET_VDPA=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vhost_crypto" = "yes" ; then
|
||||
echo "CONFIG_VHOST_CRYPTO=y" >> $config_host_mak
|
||||
fi
|
||||
@ -7452,6 +7470,9 @@ fi
|
||||
if test "$vhost_user" = "yes" ; then
|
||||
echo "CONFIG_VHOST_USER=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vhost_vdpa" = "yes" ; then
|
||||
echo "CONFIG_VHOST_VDPA=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vhost_user_fs" = "yes" ; then
|
||||
echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -20,3 +20,4 @@ Contents:
|
||||
qemu-ga
|
||||
vhost-user
|
||||
vhost-user-gpu
|
||||
vhost-vdpa
|
||||
|
17
docs/interop/vhost-vdpa.rst
Normal file
17
docs/interop/vhost-vdpa.rst
Normal file
@ -0,0 +1,17 @@
|
||||
=====================
|
||||
Vhost-vdpa Protocol
|
||||
=====================
|
||||
|
||||
Introduction
|
||||
=============
|
||||
vDPA(Virtual data path acceleration) device is a device that uses
|
||||
a datapath which complies with the virtio specifications with vendor
|
||||
specific control path. vDPA devices can be both physically located on
|
||||
the hardware or emulated by software.
|
||||
|
||||
This document describes the vDPA support in qemu
|
||||
|
||||
Here is the kernel commit here
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c8cf31885f69e86be0b5b9e6677a26797365e1d
|
||||
|
||||
TODO : More information will add later
|
@ -17,6 +17,7 @@
|
||||
#include "net/net.h"
|
||||
#include "net/tap.h"
|
||||
#include "net/vhost-user.h"
|
||||
#include "net/vhost-vdpa.h"
|
||||
|
||||
#include "standard-headers/linux/vhost_types.h"
|
||||
#include "hw/virtio/virtio-net.h"
|
||||
@ -33,12 +34,6 @@
|
||||
#include "hw/virtio/vhost.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
|
||||
struct vhost_net {
|
||||
struct vhost_dev dev;
|
||||
struct vhost_virtqueue vqs[2];
|
||||
int backend;
|
||||
NetClientState *nc;
|
||||
};
|
||||
|
||||
/* Features supported by host kernel. */
|
||||
static const int kernel_feature_bits[] = {
|
||||
@ -96,6 +91,11 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
|
||||
case NET_CLIENT_DRIVER_VHOST_USER:
|
||||
feature_bits = user_feature_bits;
|
||||
break;
|
||||
#ifdef CONFIG_VHOST_NET_VDPA
|
||||
case NET_CLIENT_DRIVER_VHOST_VDPA:
|
||||
feature_bits = vdpa_feature_bits;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
error_report("Feature bits not defined for this type: %d",
|
||||
net->nc->info->type);
|
||||
@ -443,6 +443,12 @@ VHostNetState *get_vhost_net(NetClientState *nc)
|
||||
vhost_net = vhost_user_get_vhost_net(nc);
|
||||
assert(vhost_net);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_VHOST_NET_VDPA
|
||||
case NET_CLIENT_DRIVER_VHOST_VDPA:
|
||||
vhost_net = vhost_vdpa_get_vhost_net(nc);
|
||||
assert(vhost_net);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "monitor/qdev.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "net_rx_pkt.h"
|
||||
#include "hw/virtio/vhost.h"
|
||||
|
||||
#define VIRTIO_NET_VM_VERSION 11
|
||||
|
||||
@ -125,6 +126,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
VirtIONet *n = VIRTIO_NET(vdev);
|
||||
struct virtio_net_config netcfg;
|
||||
|
||||
int ret = 0;
|
||||
memset(&netcfg, 0 , sizeof(struct virtio_net_config));
|
||||
virtio_stw_p(vdev, &netcfg.status, n->status);
|
||||
virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
|
||||
virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
|
||||
@ -138,6 +141,15 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
virtio_stl_p(vdev, &netcfg.supported_hash_types,
|
||||
VIRTIO_NET_RSS_SUPPORTED_HASHES);
|
||||
memcpy(config, &netcfg, n->config_size);
|
||||
|
||||
NetClientState *nc = qemu_get_queue(n->nic);
|
||||
if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
|
||||
ret = vhost_net_get_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg,
|
||||
n->config_size);
|
||||
if (ret != -1) {
|
||||
memcpy(config, &netcfg, n->config_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
|
||||
@ -153,6 +165,13 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
|
||||
memcpy(n->mac, netcfg.mac, ETH_ALEN);
|
||||
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
|
||||
}
|
||||
|
||||
NetClientState *nc = qemu_get_queue(n->nic);
|
||||
if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
|
||||
vhost_net_set_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg,
|
||||
0, n->config_size,
|
||||
VHOST_SET_CONFIG_TYPE_MASTER);
|
||||
}
|
||||
}
|
||||
|
||||
static bool virtio_net_started(VirtIONet *n, uint8_t status)
|
||||
|
@ -5,6 +5,7 @@ obj-y += virtio.o
|
||||
obj-$(CONFIG_VHOST) += vhost.o vhost-backend.o
|
||||
common-obj-$(call lnot,$(CONFIG_VHOST)) += vhost-stub.o
|
||||
obj-$(CONFIG_VHOST_USER) += vhost-user.o
|
||||
obj-$(CONFIG_VHOST_VDPA) += vhost-vdpa.o
|
||||
|
||||
common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o
|
||||
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "standard-headers/linux/vhost_types.h"
|
||||
|
||||
#include "hw/virtio/vhost-vdpa.h"
|
||||
#ifdef CONFIG_VHOST_KERNEL
|
||||
#include <linux/vhost.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -285,6 +286,11 @@ int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type)
|
||||
case VHOST_BACKEND_TYPE_USER:
|
||||
dev->vhost_ops = &user_ops;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_VHOST_VDPA
|
||||
case VHOST_BACKEND_TYPE_VDPA:
|
||||
dev->vhost_ops = &vdpa_ops;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
error_report("Unknown vhost backend type");
|
||||
|
475
hw/virtio/vhost-vdpa.c
Normal file
475
hw/virtio/vhost-vdpa.c
Normal file
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* vhost-vdpa
|
||||
*
|
||||
* Copyright(c) 2017-2018 Intel Corporation.
|
||||
* Copyright(c) 2020 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <linux/vhost.h>
|
||||
#include <linux/vfio.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "hw/virtio/vhost.h"
|
||||
#include "hw/virtio/vhost-backend.h"
|
||||
#include "hw/virtio/virtio-net.h"
|
||||
#include "hw/virtio/vhost-vdpa.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include <linux/kvm.h>
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section)
|
||||
{
|
||||
return (!memory_region_is_ram(section->mr) &&
|
||||
!memory_region_is_iommu(section->mr)) ||
|
||||
/*
|
||||
* Sizing an enabled 64-bit BAR can cause spurious mappings to
|
||||
* addresses in the upper part of the 64-bit address space. These
|
||||
* are never accessed by the CPU and beyond the address width of
|
||||
* some IOMMU hardware. TODO: VDPA should tell us the IOMMU width.
|
||||
*/
|
||||
section->offset_within_address_space & (1ULL << 63);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
|
||||
void *vaddr, bool readonly)
|
||||
{
|
||||
struct vhost_msg_v2 msg;
|
||||
int fd = v->device_fd;
|
||||
int ret = 0;
|
||||
|
||||
msg.type = v->msg_type;
|
||||
msg.iotlb.iova = iova;
|
||||
msg.iotlb.size = size;
|
||||
msg.iotlb.uaddr = (uint64_t)(uintptr_t)vaddr;
|
||||
msg.iotlb.perm = readonly ? VHOST_ACCESS_RO : VHOST_ACCESS_RW;
|
||||
msg.iotlb.type = VHOST_IOTLB_UPDATE;
|
||||
|
||||
if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
|
||||
error_report("failed to write, fd=%d, errno=%d (%s)",
|
||||
fd, errno, strerror(errno));
|
||||
return -EIO ;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova,
|
||||
hwaddr size)
|
||||
{
|
||||
struct vhost_msg_v2 msg;
|
||||
int fd = v->device_fd;
|
||||
int ret = 0;
|
||||
|
||||
msg.type = v->msg_type;
|
||||
msg.iotlb.iova = iova;
|
||||
msg.iotlb.size = size;
|
||||
msg.iotlb.type = VHOST_IOTLB_INVALIDATE;
|
||||
|
||||
if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
|
||||
error_report("failed to write, fd=%d, errno=%d (%s)",
|
||||
fd, errno, strerror(errno));
|
||||
return -EIO ;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vhost_vdpa_listener_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener);
|
||||
hwaddr iova;
|
||||
Int128 llend, llsize;
|
||||
void *vaddr;
|
||||
int ret;
|
||||
|
||||
if (vhost_vdpa_listener_skipped_section(section)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) !=
|
||||
(section->offset_within_region & ~TARGET_PAGE_MASK))) {
|
||||
error_report("%s received unaligned region", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
|
||||
llend = int128_make64(section->offset_within_address_space);
|
||||
llend = int128_add(llend, section->size);
|
||||
llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
|
||||
|
||||
if (int128_ge(int128_make64(iova), llend)) {
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_ref(section->mr);
|
||||
|
||||
/* Here we assume that memory_region_is_ram(section->mr)==true */
|
||||
|
||||
vaddr = memory_region_get_ram_ptr(section->mr) +
|
||||
section->offset_within_region +
|
||||
(iova - section->offset_within_address_space);
|
||||
|
||||
llsize = int128_sub(llend, int128_make64(iova));
|
||||
|
||||
ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize),
|
||||
vaddr, section->readonly);
|
||||
if (ret) {
|
||||
error_report("vhost vdpa map fail!");
|
||||
if (memory_region_is_ram_device(section->mr)) {
|
||||
/* Allow unexpected mappings not to be fatal for RAM devices */
|
||||
error_report("map ram fail!");
|
||||
return ;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (memory_region_is_ram_device(section->mr)) {
|
||||
error_report("failed to vdpa_dma_map. pci p2p may not work");
|
||||
return;
|
||||
|
||||
}
|
||||
/*
|
||||
* On the initfn path, store the first error in the container so we
|
||||
* can gracefully fail. Runtime, there's not much we can do other
|
||||
* than throw a hardware error.
|
||||
*/
|
||||
error_report("vhost-vdpa: DMA mapping failed, unable to continue");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static void vhost_vdpa_listener_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener);
|
||||
hwaddr iova;
|
||||
Int128 llend, llsize;
|
||||
int ret;
|
||||
bool try_unmap = true;
|
||||
|
||||
if (vhost_vdpa_listener_skipped_section(section)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) !=
|
||||
(section->offset_within_region & ~TARGET_PAGE_MASK))) {
|
||||
error_report("%s received unaligned region", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
|
||||
llend = int128_make64(section->offset_within_address_space);
|
||||
llend = int128_add(llend, section->size);
|
||||
llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
|
||||
|
||||
if (int128_ge(int128_make64(iova), llend)) {
|
||||
return;
|
||||
}
|
||||
|
||||
llsize = int128_sub(llend, int128_make64(iova));
|
||||
|
||||
if (try_unmap) {
|
||||
ret = vhost_vdpa_dma_unmap(v, iova, int128_get64(llsize));
|
||||
if (ret) {
|
||||
error_report("vhost_vdpa dma unmap error!");
|
||||
}
|
||||
}
|
||||
|
||||
memory_region_unref(section->mr);
|
||||
}
|
||||
/*
|
||||
* IOTLB API is used by vhost-vpda which requires incremental updating
|
||||
* of the mapping. So we can not use generic vhost memory listener which
|
||||
* depends on the addnop().
|
||||
*/
|
||||
static const MemoryListener vhost_vdpa_memory_listener = {
|
||||
.region_add = vhost_vdpa_listener_region_add,
|
||||
.region_del = vhost_vdpa_listener_region_del,
|
||||
};
|
||||
|
||||
static int vhost_vdpa_call(struct vhost_dev *dev, unsigned long int request,
|
||||
void *arg)
|
||||
{
|
||||
struct vhost_vdpa *v = dev->opaque;
|
||||
int fd = v->device_fd;
|
||||
|
||||
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA);
|
||||
|
||||
return ioctl(fd, request, arg);
|
||||
}
|
||||
|
||||
static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status)
|
||||
{
|
||||
uint8_t s;
|
||||
|
||||
if (vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s |= status;
|
||||
|
||||
vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque)
|
||||
{
|
||||
struct vhost_vdpa *v;
|
||||
uint64_t features;
|
||||
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA);
|
||||
|
||||
v = opaque;
|
||||
dev->opaque = opaque ;
|
||||
vhost_vdpa_call(dev, VHOST_GET_FEATURES, &features);
|
||||
dev->backend_features = features;
|
||||
v->listener = vhost_vdpa_memory_listener;
|
||||
v->msg_type = VHOST_IOTLB_MSG_V2;
|
||||
|
||||
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
|
||||
VIRTIO_CONFIG_S_DRIVER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_cleanup(struct vhost_dev *dev)
|
||||
{
|
||||
struct vhost_vdpa *v;
|
||||
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA);
|
||||
v = dev->opaque;
|
||||
memory_listener_unregister(&v->listener);
|
||||
|
||||
dev->opaque = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_memslots_limit(struct vhost_dev *dev)
|
||||
{
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_mem_table(struct vhost_dev *dev,
|
||||
struct vhost_memory *mem)
|
||||
{
|
||||
|
||||
if (mem->padding) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_features(struct vhost_dev *dev,
|
||||
uint64_t features)
|
||||
{
|
||||
int ret;
|
||||
ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features);
|
||||
uint8_t status = 0;
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
|
||||
vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status);
|
||||
|
||||
return !(status & VIRTIO_CONFIG_S_FEATURES_OK);
|
||||
}
|
||||
|
||||
int vhost_vdpa_get_device_id(struct vhost_dev *dev,
|
||||
uint32_t *device_id)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_VDPA_GET_DEVICE_ID, device_id);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_reset_device(struct vhost_dev *dev)
|
||||
{
|
||||
uint8_t status = 0;
|
||||
|
||||
return vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx)
|
||||
{
|
||||
assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
|
||||
|
||||
return idx - dev->vq_index;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dev->nvqs; ++i) {
|
||||
struct vhost_vring_state state = {
|
||||
.index = dev->vq_index + i,
|
||||
.num = 1,
|
||||
};
|
||||
vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, &state);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_config(struct vhost_dev *dev, const uint8_t *data,
|
||||
uint32_t offset, uint32_t size,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct vhost_vdpa_config *config;
|
||||
int ret;
|
||||
unsigned long config_size = offsetof(struct vhost_vdpa_config, buf);
|
||||
config = g_malloc(size + config_size);
|
||||
if (config == NULL) {
|
||||
return -1;
|
||||
}
|
||||
config->off = offset;
|
||||
config->len = size;
|
||||
memcpy(config->buf, data, size);
|
||||
ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_CONFIG, config);
|
||||
g_free(config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config,
|
||||
uint32_t config_len)
|
||||
{
|
||||
struct vhost_vdpa_config *v_config;
|
||||
unsigned long config_size = offsetof(struct vhost_vdpa_config, buf);
|
||||
int ret;
|
||||
|
||||
v_config = g_malloc(config_len + config_size);
|
||||
if (v_config == NULL) {
|
||||
return -1;
|
||||
}
|
||||
v_config->len = config_len;
|
||||
v_config->off = 0;
|
||||
ret = vhost_vdpa_call(dev, VHOST_VDPA_GET_CONFIG, v_config);
|
||||
memcpy(config, v_config->buf, config_len);
|
||||
g_free(v_config);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
|
||||
{
|
||||
struct vhost_vdpa *v = dev->opaque;
|
||||
if (started) {
|
||||
uint8_t status = 0;
|
||||
memory_listener_register(&v->listener, &address_space_memory);
|
||||
vhost_vdpa_set_vring_ready(dev);
|
||||
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status);
|
||||
|
||||
return !(status & VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
} else {
|
||||
vhost_vdpa_reset_device(dev);
|
||||
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
|
||||
VIRTIO_CONFIG_S_DRIVER);
|
||||
memory_listener_unregister(&v->listener);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base,
|
||||
struct vhost_log *log)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_LOG_BASE, &base);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_vring_addr(struct vhost_dev *dev,
|
||||
struct vhost_vring_addr *addr)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_vring_num(struct vhost_dev *dev,
|
||||
struct vhost_vring_state *ring)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_VRING_NUM, ring);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_vring_base(struct vhost_dev *dev,
|
||||
struct vhost_vring_state *ring)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_get_vring_base(struct vhost_dev *dev,
|
||||
struct vhost_vring_state *ring)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_vring_kick(struct vhost_dev *dev,
|
||||
struct vhost_vring_file *file)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_vring_call(struct vhost_dev *dev,
|
||||
struct vhost_vring_file *file)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_get_features(struct vhost_dev *dev,
|
||||
uint64_t *features)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_GET_FEATURES, features);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_set_owner(struct vhost_dev *dev)
|
||||
{
|
||||
return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL);
|
||||
}
|
||||
|
||||
static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev,
|
||||
struct vhost_vring_addr *addr, struct vhost_virtqueue *vq)
|
||||
{
|
||||
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_VDPA);
|
||||
addr->desc_user_addr = (uint64_t)(unsigned long)vq->desc_phys;
|
||||
addr->avail_user_addr = (uint64_t)(unsigned long)vq->avail_phys;
|
||||
addr->used_user_addr = (uint64_t)(unsigned long)vq->used_phys;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool vhost_vdpa_force_iommu(struct vhost_dev *dev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const VhostOps vdpa_ops = {
|
||||
.backend_type = VHOST_BACKEND_TYPE_VDPA,
|
||||
.vhost_backend_init = vhost_vdpa_init,
|
||||
.vhost_backend_cleanup = vhost_vdpa_cleanup,
|
||||
.vhost_set_log_base = vhost_vdpa_set_log_base,
|
||||
.vhost_set_vring_addr = vhost_vdpa_set_vring_addr,
|
||||
.vhost_set_vring_num = vhost_vdpa_set_vring_num,
|
||||
.vhost_set_vring_base = vhost_vdpa_set_vring_base,
|
||||
.vhost_get_vring_base = vhost_vdpa_get_vring_base,
|
||||
.vhost_set_vring_kick = vhost_vdpa_set_vring_kick,
|
||||
.vhost_set_vring_call = vhost_vdpa_set_vring_call,
|
||||
.vhost_get_features = vhost_vdpa_get_features,
|
||||
.vhost_set_owner = vhost_vdpa_set_owner,
|
||||
.vhost_set_vring_endian = NULL,
|
||||
.vhost_backend_memslots_limit = vhost_vdpa_memslots_limit,
|
||||
.vhost_set_mem_table = vhost_vdpa_set_mem_table,
|
||||
.vhost_set_features = vhost_vdpa_set_features,
|
||||
.vhost_reset_device = vhost_vdpa_reset_device,
|
||||
.vhost_get_vq_index = vhost_vdpa_get_vq_index,
|
||||
.vhost_get_config = vhost_vdpa_get_config,
|
||||
.vhost_set_config = vhost_vdpa_set_config,
|
||||
.vhost_requires_shm_log = NULL,
|
||||
.vhost_migration_done = NULL,
|
||||
.vhost_backend_can_merge = NULL,
|
||||
.vhost_net_set_mtu = NULL,
|
||||
.vhost_set_iotlb_callback = NULL,
|
||||
.vhost_send_device_iotlb_msg = NULL,
|
||||
.vhost_dev_start = vhost_vdpa_dev_start,
|
||||
.vhost_get_device_id = vhost_vdpa_get_device_id,
|
||||
.vhost_vq_get_addr = vhost_vdpa_vq_get_addr,
|
||||
.vhost_force_iommu = vhost_vdpa_force_iommu,
|
||||
};
|
@ -17,7 +17,8 @@ typedef enum VhostBackendType {
|
||||
VHOST_BACKEND_TYPE_NONE = 0,
|
||||
VHOST_BACKEND_TYPE_KERNEL = 1,
|
||||
VHOST_BACKEND_TYPE_USER = 2,
|
||||
VHOST_BACKEND_TYPE_MAX = 3,
|
||||
VHOST_BACKEND_TYPE_VDPA = 3,
|
||||
VHOST_BACKEND_TYPE_MAX = 4,
|
||||
} VhostBackendType;
|
||||
|
||||
typedef enum VhostSetConfigType {
|
||||
@ -170,6 +171,7 @@ typedef struct VhostOps {
|
||||
} VhostOps;
|
||||
|
||||
extern const VhostOps user_ops;
|
||||
extern const VhostOps vdpa_ops;
|
||||
|
||||
int vhost_set_backend_type(struct vhost_dev *dev,
|
||||
VhostBackendType backend_type);
|
||||
|
26
include/hw/virtio/vhost-vdpa.h
Normal file
26
include/hw/virtio/vhost-vdpa.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* vhost-vdpa.h
|
||||
*
|
||||
* Copyright(c) 2017-2018 Intel Corporation.
|
||||
* Copyright(c) 2020 Red Hat, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_VIRTIO_VHOST_VDPA_H
|
||||
#define HW_VIRTIO_VHOST_VDPA_H
|
||||
|
||||
#include "hw/virtio/virtio.h"
|
||||
|
||||
typedef struct vhost_vdpa {
|
||||
int device_fd;
|
||||
uint32_t msg_type;
|
||||
MemoryListener listener;
|
||||
} VhostVDPA;
|
||||
|
||||
extern AddressSpace address_space_memory;
|
||||
extern int vhost_vdpa_get_device_id(struct vhost_dev *dev,
|
||||
uint32_t *device_id);
|
||||
#endif
|
@ -92,6 +92,13 @@ struct vhost_dev {
|
||||
const VhostDevConfigOps *config_ops;
|
||||
};
|
||||
|
||||
struct vhost_net {
|
||||
struct vhost_dev dev;
|
||||
struct vhost_virtqueue vqs[2];
|
||||
int backend;
|
||||
NetClientState *nc;
|
||||
};
|
||||
|
||||
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
|
||||
VhostBackendType backend_type,
|
||||
uint32_t busyloop_timeout);
|
||||
|
@ -2418,6 +2418,10 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
|
||||
#ifdef CONFIG_POSIX
|
||||
"-netdev vhost-user,id=str,chardev=dev[,vhostforce=on|off]\n"
|
||||
" configure a vhost-user network, backed by a chardev 'dev'\n"
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
"-netdev vhost-vdpa,id=str,vhostdev=/path/to/dev\n"
|
||||
" configure a vhost-vdpa network,Establish a vhost-vdpa netdev\n"
|
||||
#endif
|
||||
"-netdev hubport,id=str,hubid=n[,netdev=nd]\n"
|
||||
" configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL)
|
||||
@ -2897,6 +2901,14 @@ SRST
|
||||
-netdev type=vhost-user,id=net0,chardev=chr0 \
|
||||
-device virtio-net-pci,netdev=net0
|
||||
|
||||
``-netdev vhost-vdpa,vhostdev=/path/to/dev``
|
||||
Establish a vhost-vdpa netdev.
|
||||
|
||||
vDPA device is a device that uses a datapath which complies with
|
||||
the virtio specifications with a vendor specific control path.
|
||||
vDPA devices can be both physically located on the hardware or
|
||||
emulated by software.
|
||||
|
||||
``-netdev hubport,id=id,hubid=hubid[,netdev=nd]``
|
||||
Create a hub port on the emulated hub with ID hubid.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user