Merge tag 'pull-vfio-20250626' of https://github.com/legoater/qemu into staging

vfio queue:

* Added several small fixes and cleanups
* Added support for vfio-user client device

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmhc+mwACgkQUaNDx8/7
# 7KHQShAAwGTjc6yzBaFr9DTKaL6Vszwby5tYdV3vWTsnFj2zBks+5BswohxbnYdk
# Smy5E/F+yCiHI6X4ohI4FRjJgBJplDxZ6bbEgTwZa6ADs7xWAwFWv349KQY9uLpJ
# XV/ACot2b6FZUskv3w1SxQrpzho4ICm3DuLOdAFAvBPRtxyC2aQMLsXTlT+7+5cC
# X8zJB/9mtjWIomYWKRXnYUP/uM1g7QLtyU7d01szvqCfSVUilVlg6Ys7RxnqLG0k
# A1/kxYOrEPHHxMO+YwFuapIfE8Gqihes2K1GfM871JaBT14dMIAZkajmVasbKD16
# Iljz89nEV3UehDP9HADhx3QuXO7fhJ3cxcHvTH0xhUeoks3EgTlUq0VNRRYzu6rQ
# 3P1E3cVaPTmwfoSrhecNIFcln4v/bENdwzYcjh96r9fcFwE+ro4oUTGNKCPYv2t0
# yOoc6PqgiZN7DM89/N2hcesgOun7oOVpMnKhiqHjVe53HoM8bfLojWECKNq9Cz1u
# m0YEHn2gEuEB5l03IguRnAywZq76Jivd6WFmAeXGrHRZ9sfxQCwvImbqMa7QxYpI
# rt+j7RAyP57WVoBPoW8hlaIQmLuIvIgdWwWkwQd2BTIprLpdHJd4SWkL6eqGozpE
# rsaHw+WQZqFoddrl7EUSVY/Z2CfIRr1g/Zo5z4RU9YLtxVxjSPw=
# =sX2P
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 26 Jun 2025 03:44:44 EDT
# gpg:                using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1
# gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full]
# gpg:                 aka "Cédric Le Goater <clg@kaod.org>" [full]
# Primary key fingerprint: A0F6 6548 F048 95EB FE6B  0B60 51A3 43C7 CFFB ECA1

* tag 'pull-vfio-20250626' of https://github.com/legoater/qemu: (25 commits)
  vfio-user: introduce vfio-user protocol specification
  docs: add vfio-user documentation
  vfio-user: add coalesced posted writes
  vfio-user: support posted writes
  vfio-user: add 'x-msg-timeout' option
  vfio-user: implement VFIO_USER_DMA_READ/WRITE
  vfio-user: implement VFIO_USER_DMA_MAP/UNMAP
  vfio-user: implement VFIO_USER_DEVICE_RESET
  vfio-user: set up container access to the proxy
  vfio-user: forward MSI-X PBA BAR accesses to server
  vfio-user: implement VFIO_USER_DEVICE_GET/SET_IRQ*
  vfio-user: set up PCI in vfio_user_pci_realize()
  vfio-user: implement VFIO_USER_REGION_READ/WRITE
  vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO
  vfio-user: implement VFIO_USER_DEVICE_GET_INFO
  vfio-user: implement message send infrastructure
  vfio-user: implement message receive infrastructure
  vfio-user: connect vfio proxy to remote server
  vfio-user: add vfio-user class and container
  vfio/container: fails mdev hotplug if add migration blocker failed
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2025-06-26 10:25:01 -04:00
31 changed files with 4717 additions and 15 deletions
+10 -1
View File
@@ -4258,13 +4258,22 @@ F: hw/remote/proxy-memory-listener.c
F: include/hw/remote/proxy-memory-listener.h
F: hw/remote/iohub.c
F: include/hw/remote/iohub.h
F: subprojects/libvfio-user
F: hw/remote/vfio-user-obj.c
F: include/hw/remote/vfio-user-obj.h
F: hw/remote/iommu.c
F: include/hw/remote/iommu.h
F: tests/functional/test_multiprocess.py
VFIO-USER:
M: John Levon <john.levon@nutanix.com>
M: Thanos Makatos <thanos.makatos@nutanix.com>
S: Supported
F: docs/interop/vfio-user.rst
F: docs/system/devices/vfio-user.rst
F: hw/vfio-user/*
F: include/hw/vfio-user/*
F: subprojects/libvfio-user
EBPF:
M: Jason Wang <jasowang@redhat.com>
R: Andrew Melnychenko <andrew@daynix.com>
+1
View File
@@ -25,6 +25,7 @@ are useful for making QEMU interoperate with other software.
qemu-ga-ref
qemu-qmp-ref
qemu-storage-daemon-qmp-ref
vfio-user
vhost-user
vhost-user-gpu
vhost-vdpa
File diff suppressed because it is too large Load Diff
+1
View File
@@ -85,6 +85,7 @@ Emulated Devices
devices/can.rst
devices/ccid.rst
devices/cxl.rst
devices/vfio-user.rst
devices/ivshmem.rst
devices/ivshmem-flat.rst
devices/keyboard.rst
+26
View File
@@ -0,0 +1,26 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
=========
vfio-user
=========
QEMU includes a ``vfio-user`` client. The ``vfio-user`` specification allows for
implementing (PCI) devices in userspace outside of QEMU; it is similar to
``vhost-user`` in this respect (see :doc:`vhost-user`), but can emulate arbitrary
PCI devices, not just ``virtio``. Whereas ``vfio`` is handled by the host
kernel, ``vfio-user``, while similar in implementation, is handled entirely in
userspace.
For example, SPDK includes a virtual PCI NVMe controller implementation; by
setting up a ``vfio-user`` UNIX socket between QEMU and SPDK, a VM can send NVMe
I/O to the SPDK process.
Presuming a suitable ``vfio-user`` server has opened a socket at
``/tmp/vfio-user.sock``, a device can be configured with for example:
.. code-block:: console
-device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}'
See `libvfio-user <https://github.com/nutanix/libvfio-user/>`_ for further
information.
+1
View File
@@ -42,6 +42,7 @@ source ufs/Kconfig
source usb/Kconfig
source virtio/Kconfig
source vfio/Kconfig
source vfio-user/Kconfig
source vmapple/Kconfig
source xen/Kconfig
source watchdog/Kconfig
+1
View File
@@ -39,6 +39,7 @@ subdir('uefi')
subdir('ufs')
subdir('usb')
subdir('vfio')
subdir('vfio-user')
subdir('virtio')
subdir('vmapple')
subdir('watchdog')
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
config VFIO_USER
bool
default y
depends on VFIO_PCI
+370
View File
@@ -0,0 +1,370 @@
/*
* Container for vfio-user IOMMU type: rather than communicating with the kernel
* vfio driver, we communicate over a socket to a server using the vfio-user
* protocol.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <sys/ioctl.h>
#include <linux/vfio.h>
#include "qemu/osdep.h"
#include "hw/vfio-user/container.h"
#include "hw/vfio-user/device.h"
#include "hw/vfio-user/trace.h"
#include "hw/vfio/vfio-cpr.h"
#include "hw/vfio/vfio-device.h"
#include "hw/vfio/vfio-listener.h"
#include "qapi/error.h"
/*
* When DMA space is the physical address space, the region add/del listeners
* will fire during memory update transactions. These depend on BQL being held,
* so do any resulting map/demap ops async while keeping BQL.
*/
static void vfio_user_listener_begin(VFIOContainerBase *bcontainer)
{
VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer,
bcontainer);
container->proxy->async_ops = true;
}
static void vfio_user_listener_commit(VFIOContainerBase *bcontainer)
{
VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer,
bcontainer);
/* wait here for any async requests sent during the transaction */
container->proxy->async_ops = false;
vfio_user_wait_reqs(container->proxy);
}
static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
IOMMUTLBEntry *iotlb, bool unmap_all)
{
VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer,
bcontainer);
Error *local_err = NULL;
int ret = 0;
VFIOUserDMAUnmap *msgp = g_malloc(sizeof(*msgp));
vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_UNMAP, sizeof(*msgp), 0);
msgp->argsz = sizeof(struct vfio_iommu_type1_dma_unmap);
msgp->flags = unmap_all ? VFIO_DMA_UNMAP_FLAG_ALL : 0;
msgp->iova = iova;
msgp->size = size;
trace_vfio_user_dma_unmap(msgp->iova, msgp->size, msgp->flags,
container->proxy->async_ops);
if (container->proxy->async_ops) {
if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, NULL,
0, &local_err)) {
error_report_err(local_err);
ret = -EFAULT;
} else {
ret = 0;
}
} else {
if (!vfio_user_send_wait(container->proxy, &msgp->hdr, NULL,
0, &local_err)) {
error_report_err(local_err);
ret = -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
ret = -msgp->hdr.error_reply;
}
g_free(msgp);
}
return ret;
}
static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
ram_addr_t size, void *vaddr, bool readonly,
MemoryRegion *mrp)
{
VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer,
bcontainer);
int fd = memory_region_get_fd(mrp);
Error *local_err = NULL;
int ret;
VFIOUserFDs *fds = NULL;
VFIOUserDMAMap *msgp = g_malloc0(sizeof(*msgp));
vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_MAP, sizeof(*msgp), 0);
msgp->argsz = sizeof(struct vfio_iommu_type1_dma_map);
msgp->flags = VFIO_DMA_MAP_FLAG_READ;
msgp->offset = 0;
msgp->iova = iova;
msgp->size = size;
/*
* vaddr enters as a QEMU process address; make it either a file offset
* for mapped areas or leave as 0.
*/
if (fd != -1) {
msgp->offset = qemu_ram_block_host_offset(mrp->ram_block, vaddr);
}
if (!readonly) {
msgp->flags |= VFIO_DMA_MAP_FLAG_WRITE;
}
trace_vfio_user_dma_map(msgp->iova, msgp->size, msgp->offset, msgp->flags,
container->proxy->async_ops);
/*
* The async_ops case sends without blocking. They're later waited for in
* vfio_send_wait_reqs.
*/
if (container->proxy->async_ops) {
/* can't use auto variable since we don't block */
if (fd != -1) {
fds = vfio_user_getfds(1);
fds->send_fds = 1;
fds->fds[0] = fd;
}
if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, fds,
0, &local_err)) {
error_report_err(local_err);
ret = -EFAULT;
} else {
ret = 0;
}
} else {
VFIOUserFDs local_fds = { 1, 0, &fd };
fds = fd != -1 ? &local_fds : NULL;
if (!vfio_user_send_wait(container->proxy, &msgp->hdr, fds,
0, &local_err)) {
error_report_err(local_err);
ret = -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
ret = -msgp->hdr.error_reply;
}
g_free(msgp);
}
return ret;
}
static int
vfio_user_set_dirty_page_tracking(const VFIOContainerBase *bcontainer,
bool start, Error **errp)
{
error_setg_errno(errp, ENOTSUP, "Not supported");
return -ENOTSUP;
}
static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
VFIOBitmap *vbmap, hwaddr iova,
hwaddr size, Error **errp)
{
error_setg_errno(errp, ENOTSUP, "Not supported");
return -ENOTSUP;
}
static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp)
{
VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer,
bcontainer);
assert(container->proxy->dma_pgsizes != 0);
bcontainer->pgsizes = container->proxy->dma_pgsizes;
bcontainer->dma_max_mappings = container->proxy->max_dma;
/* No live migration support yet. */
bcontainer->dirty_pages_supported = false;
bcontainer->max_dirty_bitmap_size = container->proxy->max_bitmap;
bcontainer->dirty_pgsizes = container->proxy->migr_pgsize;
return true;
}
static VFIOUserContainer *vfio_user_create_container(VFIODevice *vbasedev,
Error **errp)
{
VFIOUserContainer *container;
container = VFIO_IOMMU_USER(object_new(TYPE_VFIO_IOMMU_USER));
container->proxy = vbasedev->proxy;
return container;
}
/*
* Try to mirror vfio_container_connect() as much as possible.
*/
static VFIOUserContainer *
vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev,
Error **errp)
{
VFIOContainerBase *bcontainer;
VFIOUserContainer *container;
VFIOAddressSpace *space;
VFIOIOMMUClass *vioc;
int ret;
space = vfio_address_space_get(as);
container = vfio_user_create_container(vbasedev, errp);
if (!container) {
goto put_space_exit;
}
bcontainer = &container->bcontainer;
if (!vfio_cpr_register_container(bcontainer, errp)) {
goto free_container_exit;
}
ret = ram_block_uncoordinated_discard_disable(true);
if (ret) {
error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
goto unregister_container_exit;
}
vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
assert(vioc->setup);
if (!vioc->setup(bcontainer, errp)) {
goto enable_discards_exit;
}
vfio_address_space_insert(space, bcontainer);
if (!vfio_listener_register(bcontainer, errp)) {
goto listener_release_exit;
}
bcontainer->initialized = true;
return container;
listener_release_exit:
vfio_listener_unregister(bcontainer);
if (vioc->release) {
vioc->release(bcontainer);
}
enable_discards_exit:
ram_block_uncoordinated_discard_disable(false);
unregister_container_exit:
vfio_cpr_unregister_container(bcontainer);
free_container_exit:
object_unref(container);
put_space_exit:
vfio_address_space_put(space);
return NULL;
}
static void vfio_user_container_disconnect(VFIOUserContainer *container)
{
VFIOContainerBase *bcontainer = &container->bcontainer;
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
VFIOAddressSpace *space = bcontainer->space;
ram_block_uncoordinated_discard_disable(false);
vfio_listener_unregister(bcontainer);
if (vioc->release) {
vioc->release(bcontainer);
}
vfio_cpr_unregister_container(bcontainer);
object_unref(container);
vfio_address_space_put(space);
}
static bool vfio_user_device_get(VFIOUserContainer *container,
VFIODevice *vbasedev, Error **errp)
{
struct vfio_device_info info = { .argsz = sizeof(info) };
if (!vfio_user_get_device_info(vbasedev->proxy, &info, errp)) {
return false;
}
vbasedev->fd = -1;
vfio_device_prepare(vbasedev, &container->bcontainer, &info);
return true;
}
/*
* vfio_user_device_attach: attach a device to a new container.
*/
static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev,
AddressSpace *as, Error **errp)
{
VFIOUserContainer *container;
container = vfio_user_container_connect(as, vbasedev, errp);
if (container == NULL) {
error_prepend(errp, "failed to connect proxy");
return false;
}
return vfio_user_device_get(container, vbasedev, errp);
}
static void vfio_user_device_detach(VFIODevice *vbasedev)
{
VFIOUserContainer *container = container_of(vbasedev->bcontainer,
VFIOUserContainer, bcontainer);
vfio_device_unprepare(vbasedev);
vfio_user_container_disconnect(container);
}
static int vfio_user_pci_hot_reset(VFIODevice *vbasedev, bool single)
{
/* ->needs_reset is always false for vfio-user. */
return 0;
}
static void vfio_iommu_user_class_init(ObjectClass *klass, const void *data)
{
VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass);
vioc->setup = vfio_user_setup;
vioc->listener_begin = vfio_user_listener_begin,
vioc->listener_commit = vfio_user_listener_commit,
vioc->dma_map = vfio_user_dma_map;
vioc->dma_unmap = vfio_user_dma_unmap;
vioc->attach_device = vfio_user_device_attach;
vioc->detach_device = vfio_user_device_detach;
vioc->set_dirty_page_tracking = vfio_user_set_dirty_page_tracking;
vioc->query_dirty_bitmap = vfio_user_query_dirty_bitmap;
vioc->pci_hot_reset = vfio_user_pci_hot_reset;
};
static const TypeInfo types[] = {
{
.name = TYPE_VFIO_IOMMU_USER,
.parent = TYPE_VFIO_IOMMU,
.instance_size = sizeof(VFIOUserContainer),
.class_init = vfio_iommu_user_class_init,
},
};
DEFINE_TYPES(types)
+23
View File
@@ -0,0 +1,23 @@
/*
* vfio-user specific definitions.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef HW_VFIO_USER_CONTAINER_H
#define HW_VFIO_USER_CONTAINER_H
#include "qemu/osdep.h"
#include "hw/vfio/vfio-container-base.h"
#include "hw/vfio-user/proxy.h"
/* MMU container sub-class for vfio-user. */
typedef struct VFIOUserContainer {
VFIOContainerBase bcontainer;
VFIOUserProxy *proxy;
} VFIOUserContainer;
OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserContainer, VFIO_IOMMU_USER);
#endif /* HW_VFIO_USER_CONTAINER_H */
+441
View File
@@ -0,0 +1,441 @@
/*
* vfio protocol over a UNIX socket device handling.
*
* Copyright © 2018, 2021 Oracle and/or its affiliates.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/lockable.h"
#include "qemu/thread.h"
#include "hw/vfio-user/device.h"
#include "hw/vfio-user/trace.h"
/*
* These are to defend against a malign server trying
* to force us to run out of memory.
*/
#define VFIO_USER_MAX_REGIONS 100
#define VFIO_USER_MAX_IRQS 50
bool vfio_user_get_device_info(VFIOUserProxy *proxy,
struct vfio_device_info *info, Error **errp)
{
VFIOUserDeviceInfo msg;
uint32_t argsz = sizeof(msg) - sizeof(msg.hdr);
memset(&msg, 0, sizeof(msg));
vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_INFO, sizeof(msg), 0);
msg.argsz = argsz;
if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, errp)) {
return false;
}
if (msg.hdr.flags & VFIO_USER_ERROR) {
error_setg_errno(errp, -msg.hdr.error_reply,
"VFIO_USER_DEVICE_GET_INFO failed");
return false;
}
trace_vfio_user_get_info(msg.num_regions, msg.num_irqs);
memcpy(info, &msg.argsz, argsz);
/* defend against a malicious server */
if (info->num_regions > VFIO_USER_MAX_REGIONS ||
info->num_irqs > VFIO_USER_MAX_IRQS) {
error_setg_errno(errp, EINVAL, "invalid reply");
return false;
}
return true;
}
void vfio_user_device_reset(VFIOUserProxy *proxy)
{
Error *local_err = NULL;
VFIOUserHdr hdr;
vfio_user_request_msg(&hdr, VFIO_USER_DEVICE_RESET, sizeof(hdr), 0);
if (!vfio_user_send_wait(proxy, &hdr, NULL, 0, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return;
}
if (hdr.flags & VFIO_USER_ERROR) {
error_printf("reset reply error %d\n", hdr.error_reply);
}
}
static int vfio_user_get_region_info(VFIOUserProxy *proxy,
struct vfio_region_info *info,
VFIOUserFDs *fds)
{
g_autofree VFIOUserRegionInfo *msgp = NULL;
Error *local_err = NULL;
uint32_t size;
/* data returned can be larger than vfio_region_info */
if (info->argsz < sizeof(*info)) {
error_printf("vfio_user_get_region_info argsz too small\n");
return -E2BIG;
}
if (fds != NULL && fds->send_fds != 0) {
error_printf("vfio_user_get_region_info can't send FDs\n");
return -EINVAL;
}
size = info->argsz + sizeof(VFIOUserHdr);
msgp = g_malloc0(size);
vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO,
sizeof(*msgp), 0);
msgp->argsz = info->argsz;
msgp->index = info->index;
if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
return -msgp->hdr.error_reply;
}
trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size);
memcpy(info, &msgp->argsz, info->argsz);
/*
* If at least one region is directly mapped into the VM, then we can no
* longer rely on the sequential nature of vfio-user request handling to
* ensure that posted writes are completed before a subsequent read. In this
* case, disable posted write support. This is a per-device property, not
* per-region.
*/
if (info->flags & VFIO_REGION_INFO_FLAG_MMAP) {
vfio_user_disable_posted_writes(proxy);
}
return 0;
}
static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
struct vfio_region_info *info,
int *fd)
{
VFIOUserFDs fds = { 0, 1, fd};
int ret;
if (info->index > vbasedev->num_regions) {
return -EINVAL;
}
ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
if (ret) {
return ret;
}
/* cap_offset in valid area */
if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
(info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) {
return -EINVAL;
}
return 0;
}
static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
struct vfio_irq_info *info)
{
VFIOUserProxy *proxy = vbasedev->proxy;
Error *local_err = NULL;
VFIOUserIRQInfo msg;
memset(&msg, 0, sizeof(msg));
vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO,
sizeof(msg), 0);
msg.argsz = info->argsz;
msg.index = info->index;
if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return -EFAULT;
}
if (msg.hdr.flags & VFIO_USER_ERROR) {
return -msg.hdr.error_reply;
}
trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count);
memcpy(info, &msg.argsz, sizeof(*info));
return 0;
}
static int irq_howmany(int *fdp, uint32_t cur, uint32_t max)
{
int n = 0;
if (fdp[cur] != -1) {
do {
n++;
} while (n < max && fdp[cur + n] != -1);
} else {
do {
n++;
} while (n < max && fdp[cur + n] == -1);
}
return n;
}
static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev,
struct vfio_irq_set *irq)
{
VFIOUserProxy *proxy = vbasedev->proxy;
g_autofree VFIOUserIRQSet *msgp = NULL;
uint32_t size, nfds, send_fds, sent_fds, max;
Error *local_err = NULL;
if (irq->argsz < sizeof(*irq)) {
error_printf("vfio_user_set_irqs argsz too small\n");
return -EINVAL;
}
/*
* Handle simple case
*/
if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) {
size = sizeof(VFIOUserHdr) + irq->argsz;
msgp = g_malloc0(size);
vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0);
msgp->argsz = irq->argsz;
msgp->flags = irq->flags;
msgp->index = irq->index;
msgp->start = irq->start;
msgp->count = irq->count;
trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
msgp->flags);
if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
return -msgp->hdr.error_reply;
}
return 0;
}
/*
* Calculate the number of FDs to send
* and adjust argsz
*/
nfds = (irq->argsz - sizeof(*irq)) / sizeof(int);
irq->argsz = sizeof(*irq);
msgp = g_malloc0(sizeof(*msgp));
/*
* Send in chunks if over max_send_fds
*/
for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) {
VFIOUserFDs *arg_fds, loop_fds;
/* must send all valid FDs or all invalid FDs in single msg */
max = nfds - sent_fds;
if (max > proxy->max_send_fds) {
max = proxy->max_send_fds;
}
send_fds = irq_howmany((int *)irq->data, sent_fds, max);
vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS,
sizeof(*msgp), 0);
msgp->argsz = irq->argsz;
msgp->flags = irq->flags;
msgp->index = irq->index;
msgp->start = irq->start + sent_fds;
msgp->count = send_fds;
trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
msgp->flags);
loop_fds.send_fds = send_fds;
loop_fds.recv_fds = 0;
loop_fds.fds = (int *)irq->data + sent_fds;
arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL;
if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
return -msgp->hdr.error_reply;
}
}
return 0;
}
static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index,
off_t off, uint32_t count,
void *data)
{
g_autofree VFIOUserRegionRW *msgp = NULL;
VFIOUserProxy *proxy = vbasedev->proxy;
int size = sizeof(*msgp) + count;
Error *local_err = NULL;
if (count > proxy->max_xfer_size) {
return -EINVAL;
}
msgp = g_malloc0(size);
vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0);
msgp->offset = off;
msgp->region = index;
msgp->count = count;
trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
return -msgp->hdr.error_reply;
} else if (msgp->count > count) {
return -E2BIG;
} else {
memcpy(data, &msgp->data, msgp->count);
}
return msgp->count;
}
/*
* If this is a posted write, and VFIO_PROXY_NO_POST is not set, then we are OK
* to send the write to the socket without waiting for the server's reply:
* a subsequent read (of any region) will not pass the posted write, as all
* messages are handled sequentially.
*/
static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
off_t off, unsigned count,
void *data, bool post)
{
VFIOUserRegionRW *msgp = NULL;
VFIOUserProxy *proxy = vbasedev->proxy;
int size = sizeof(*msgp) + count;
Error *local_err = NULL;
bool can_multi;
int flags = 0;
int ret;
if (count > proxy->max_xfer_size) {
return -EINVAL;
}
if (proxy->flags & VFIO_PROXY_NO_POST) {
post = false;
}
if (post) {
flags |= VFIO_USER_NO_REPLY;
}
/* write eligible to be in a WRITE_MULTI msg ? */
can_multi = (proxy->flags & VFIO_PROXY_USE_MULTI) && post &&
count <= VFIO_USER_MULTI_DATA;
/*
* This should be a rare case, so first check without the lock,
* if we're wrong, vfio_send_queued() will flush any posted writes
* we missed here
*/
if (proxy->wr_multi != NULL ||
(proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi)) {
/*
* re-check with lock
*
* if already building a WRITE_MULTI msg,
* add this one if possible else flush pending before
* sending the current one
*
* else if outgoing queue is over the highwater,
* start a new WRITE_MULTI message
*/
WITH_QEMU_LOCK_GUARD(&proxy->lock) {
if (proxy->wr_multi != NULL) {
if (can_multi) {
vfio_user_add_multi(proxy, index, off, count, data);
return count;
}
vfio_user_flush_multi(proxy);
} else if (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi) {
vfio_user_create_multi(proxy);
vfio_user_add_multi(proxy, index, off, count, data);
return count;
}
}
}
msgp = g_malloc0(size);
vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, flags);
msgp->offset = off;
msgp->region = index;
msgp->count = count;
memcpy(&msgp->data, data, count);
trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
/* async send will free msg after it's sent */
if (post) {
if (!vfio_user_send_async(proxy, &msgp->hdr, NULL, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
return -EFAULT;
}
return count;
}
if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
error_prepend(&local_err, "%s: ", __func__);
error_report_err(local_err);
g_free(msgp);
return -EFAULT;
}
if (msgp->hdr.flags & VFIO_USER_ERROR) {
ret = -msgp->hdr.error_reply;
} else {
ret = count;
}
g_free(msgp);
return ret;
}
/*
* Socket-based io_ops
*/
VFIODeviceIOOps vfio_user_device_io_ops_sock = {
.get_region_info = vfio_user_device_io_get_region_info,
.get_irq_info = vfio_user_device_io_get_irq_info,
.set_irqs = vfio_user_device_io_set_irqs,
.region_read = vfio_user_device_io_region_read,
.region_write = vfio_user_device_io_region_write,
};
+24
View File
@@ -0,0 +1,24 @@
#ifndef VFIO_USER_DEVICE_H
#define VFIO_USER_DEVICE_H
/*
* vfio protocol over a UNIX socket device handling.
*
* Copyright © 2018, 2021 Oracle and/or its affiliates.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "linux/vfio.h"
#include "hw/vfio-user/proxy.h"
bool vfio_user_get_device_info(VFIOUserProxy *proxy,
struct vfio_device_info *info, Error **errp);
void vfio_user_device_reset(VFIOUserProxy *proxy);
extern VFIODeviceIOOps vfio_user_device_io_ops_sock;
#endif /* VFIO_USER_DEVICE_H */
+11
View File
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-or-later
vfio_user_ss = ss.source_set()
vfio_user_ss.add(files(
'container.c',
'device.c',
'pci.c',
'proxy.c',
))
system_ss.add_all(when: 'CONFIG_VFIO_USER', if_true: vfio_user_ss)
+475
View File
@@ -0,0 +1,475 @@
/*
* vfio PCI device over a UNIX socket.
*
* Copyright © 2018, 2021 Oracle and/or its affiliates.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <sys/ioctl.h>
#include "qemu/osdep.h"
#include "qapi-visit-sockets.h"
#include "qemu/error-report.h"
#include "hw/qdev-properties.h"
#include "hw/vfio/pci.h"
#include "hw/vfio-user/device.h"
#include "hw/vfio-user/proxy.h"
#define TYPE_VFIO_USER_PCI "vfio-user-pci"
OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI)
struct VFIOUserPCIDevice {
VFIOPCIDevice device;
SocketAddress *socket;
bool send_queued; /* all sends are queued */
uint32_t wait_time; /* timeout for message replies */
bool no_post; /* all region writes are sync */
};
/*
* The server maintains the device's pending interrupts,
* via its MSIX table and PBA, so we treat these accesses
* like PCI config space and forward them.
*/
static uint64_t vfio_user_pba_read(void *opaque, hwaddr addr,
unsigned size)
{
VFIOPCIDevice *vdev = opaque;
VFIORegion *region = &vdev->bars[vdev->msix->pba_bar].region;
uint64_t data;
/* server copy is what matters */
data = vfio_region_read(region, addr + vdev->msix->pba_offset, size);
return data;
}
static void vfio_user_pba_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
/* dropped */
}
static const MemoryRegionOps vfio_user_pba_ops = {
.read = vfio_user_pba_read,
.write = vfio_user_pba_write,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static void vfio_user_msix_setup(VFIOPCIDevice *vdev)
{
MemoryRegion *vfio_reg, *msix_reg, *pba_reg;
pba_reg = g_new0(MemoryRegion, 1);
vdev->msix->pba_region = pba_reg;
vfio_reg = vdev->bars[vdev->msix->pba_bar].mr;
msix_reg = &vdev->pdev.msix_pba_mmio;
memory_region_init_io(pba_reg, OBJECT(vdev), &vfio_user_pba_ops, vdev,
"VFIO MSIX PBA", int128_get64(msix_reg->size));
memory_region_add_subregion_overlap(vfio_reg, vdev->msix->pba_offset,
pba_reg, 1);
}
static void vfio_user_msix_teardown(VFIOPCIDevice *vdev)
{
MemoryRegion *mr, *sub;
mr = vdev->bars[vdev->msix->pba_bar].mr;
sub = vdev->msix->pba_region;
memory_region_del_subregion(mr, sub);
g_free(vdev->msix->pba_region);
vdev->msix->pba_region = NULL;
}
static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg)
{
PCIDevice *pdev = &vdev->pdev;
VFIOUserProxy *proxy = vdev->vbasedev.proxy;
VFIOUserDMARW *res;
MemTxResult r;
size_t size;
if (msg->hdr.size < sizeof(*msg)) {
vfio_user_send_error(proxy, &msg->hdr, EINVAL);
return;
}
if (msg->count > proxy->max_xfer_size) {
vfio_user_send_error(proxy, &msg->hdr, E2BIG);
return;
}
/* switch to our own message buffer */
size = msg->count + sizeof(VFIOUserDMARW);
res = g_malloc0(size);
memcpy(res, msg, sizeof(*res));
g_free(msg);
r = pci_dma_read(pdev, res->offset, &res->data, res->count);
switch (r) {
case MEMTX_OK:
if (res->hdr.flags & VFIO_USER_NO_REPLY) {
g_free(res);
return;
}
vfio_user_send_reply(proxy, &res->hdr, size);
break;
case MEMTX_ERROR:
vfio_user_send_error(proxy, &res->hdr, EFAULT);
break;
case MEMTX_DECODE_ERROR:
vfio_user_send_error(proxy, &res->hdr, ENODEV);
break;
case MEMTX_ACCESS_ERROR:
vfio_user_send_error(proxy, &res->hdr, EPERM);
break;
default:
error_printf("vfio_user_dma_read unknown error %d\n", r);
vfio_user_send_error(vdev->vbasedev.proxy, &res->hdr, EINVAL);
}
}
static void vfio_user_dma_write(VFIOPCIDevice *vdev, VFIOUserDMARW *msg)
{
PCIDevice *pdev = &vdev->pdev;
VFIOUserProxy *proxy = vdev->vbasedev.proxy;
MemTxResult r;
if (msg->hdr.size < sizeof(*msg)) {
vfio_user_send_error(proxy, &msg->hdr, EINVAL);
return;
}
/* make sure transfer count isn't larger than the message data */
if (msg->count > msg->hdr.size - sizeof(*msg)) {
vfio_user_send_error(proxy, &msg->hdr, E2BIG);
return;
}
r = pci_dma_write(pdev, msg->offset, &msg->data, msg->count);
switch (r) {
case MEMTX_OK:
if ((msg->hdr.flags & VFIO_USER_NO_REPLY) == 0) {
vfio_user_send_reply(proxy, &msg->hdr, sizeof(msg->hdr));
} else {
g_free(msg);
}
break;
case MEMTX_ERROR:
vfio_user_send_error(proxy, &msg->hdr, EFAULT);
break;
case MEMTX_DECODE_ERROR:
vfio_user_send_error(proxy, &msg->hdr, ENODEV);
break;
case MEMTX_ACCESS_ERROR:
vfio_user_send_error(proxy, &msg->hdr, EPERM);
break;
default:
error_printf("vfio_user_dma_write unknown error %d\n", r);
vfio_user_send_error(vdev->vbasedev.proxy, &msg->hdr, EINVAL);
}
}
/*
* Incoming request message callback.
*
* Runs off main loop, so BQL held.
*/
static void vfio_user_pci_process_req(void *opaque, VFIOUserMsg *msg)
{
VFIOPCIDevice *vdev = opaque;
VFIOUserHdr *hdr = msg->hdr;
/* no incoming PCI requests pass FDs */
if (msg->fds != NULL) {
vfio_user_send_error(vdev->vbasedev.proxy, hdr, EINVAL);
vfio_user_putfds(msg);
return;
}
switch (hdr->command) {
case VFIO_USER_DMA_READ:
vfio_user_dma_read(vdev, (VFIOUserDMARW *)hdr);
break;
case VFIO_USER_DMA_WRITE:
vfio_user_dma_write(vdev, (VFIOUserDMARW *)hdr);
break;
default:
error_printf("vfio_user_pci_process_req unknown cmd %d\n",
hdr->command);
vfio_user_send_error(vdev->vbasedev.proxy, hdr, ENOSYS);
}
}
/*
* Emulated devices don't use host hot reset
*/
static void vfio_user_compute_needs_reset(VFIODevice *vbasedev)
{
vbasedev->needs_reset = false;
}
static Object *vfio_user_pci_get_object(VFIODevice *vbasedev)
{
VFIOUserPCIDevice *vdev = container_of(vbasedev, VFIOUserPCIDevice,
device.vbasedev);
return OBJECT(vdev);
}
static VFIODeviceOps vfio_user_pci_ops = {
.vfio_compute_needs_reset = vfio_user_compute_needs_reset,
.vfio_eoi = vfio_pci_intx_eoi,
.vfio_get_object = vfio_user_pci_get_object,
/* No live migration support yet. */
.vfio_save_config = NULL,
.vfio_load_config = NULL,
};
static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp)
{
ERRP_GUARD();
VFIOUserPCIDevice *udev = VFIO_USER_PCI(pdev);
VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev);
VFIODevice *vbasedev = &vdev->vbasedev;
const char *sock_name;
AddressSpace *as;
SocketAddress addr;
VFIOUserProxy *proxy;
if (!udev->socket) {
error_setg(errp, "No socket specified");
error_append_hint(errp, "e.g. -device '{"
"\"driver\":\"vfio-user-pci\", "
"\"socket\": {\"path\": \"/tmp/vfio-user.sock\", "
"\"type\": \"unix\"}'"
"}'\n");
return;
}
sock_name = udev->socket->u.q_unix.path;
vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name);
memset(&addr, 0, sizeof(addr));
addr.type = SOCKET_ADDRESS_TYPE_UNIX;
addr.u.q_unix.path = (char *)sock_name;
proxy = vfio_user_connect_dev(&addr, errp);
if (!proxy) {
return;
}
vbasedev->proxy = proxy;
vfio_user_set_handler(vbasedev, vfio_user_pci_process_req, vdev);
vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name);
if (udev->send_queued) {
proxy->flags |= VFIO_PROXY_FORCE_QUEUED;
}
if (udev->no_post) {
proxy->flags |= VFIO_PROXY_NO_POST;
}
/* user specified or 5 sec default */
proxy->wait_time = udev->wait_time;
if (!vfio_user_validate_version(proxy, errp)) {
goto error;
}
/*
* Use socket-based device I/O instead of vfio kernel driver.
*/
vbasedev->io_ops = &vfio_user_device_io_ops_sock;
/*
* vfio-user devices are effectively mdevs (don't use a host iommu).
*/
vbasedev->mdev = true;
/*
* Enable per-region fds.
*/
vbasedev->use_region_fds = true;
as = pci_device_iommu_address_space(pdev);
if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER,
vbasedev->name, vbasedev,
as, errp)) {
goto error;
}
if (!vfio_pci_populate_device(vdev, errp)) {
goto error;
}
if (!vfio_pci_config_setup(vdev, errp)) {
goto error;
}
/*
* vfio_pci_config_setup will have registered the device's BARs
* and setup any MSIX BARs, so errors after it succeeds must
* use out_teardown
*/
if (!vfio_pci_add_capabilities(vdev, errp)) {
goto out_teardown;
}
if (vdev->msix != NULL) {
vfio_user_msix_setup(vdev);
}
if (!vfio_pci_interrupt_setup(vdev, errp)) {
goto out_teardown;
}
vfio_pci_register_err_notifier(vdev);
vfio_pci_register_req_notifier(vdev);
return;
out_teardown:
vfio_pci_teardown_msi(vdev);
vfio_pci_bars_exit(vdev);
error:
error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name);
vfio_pci_put_device(vdev);
}
static void vfio_user_instance_init(Object *obj)
{
PCIDevice *pci_dev = PCI_DEVICE(obj);
VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj);
VFIODevice *vbasedev = &vdev->vbasedev;
device_add_bootindex_property(obj, &vdev->bootindex,
"bootindex", NULL,
&pci_dev->qdev);
vdev->host.domain = ~0U;
vdev->host.bus = ~0U;
vdev->host.slot = ~0U;
vdev->host.function = ~0U;
vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PCI, &vfio_user_pci_ops,
DEVICE(vdev), false);
vdev->nv_gpudirect_clique = 0xFF;
/*
* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command
* line, therefore, no need to wait to realize like other devices.
*/
pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
}
static void vfio_user_instance_finalize(Object *obj)
{
VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj);
VFIODevice *vbasedev = &vdev->vbasedev;
if (vdev->msix != NULL) {
vfio_user_msix_teardown(vdev);
}
vfio_pci_put_device(vdev);
if (vbasedev->proxy != NULL) {
vfio_user_disconnect(vbasedev->proxy);
}
}
static void vfio_user_pci_reset(DeviceState *dev)
{
VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev);
VFIODevice *vbasedev = &vdev->vbasedev;
vfio_pci_pre_reset(vdev);
if (vbasedev->reset_works) {
vfio_user_device_reset(vbasedev->proxy);
}
vfio_pci_post_reset(vdev);
}
static const Property vfio_user_pci_dev_properties[] = {
DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice,
vendor_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice,
device_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice,
sub_vendor_id, PCI_ANY_ID),
DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
sub_device_id, PCI_ANY_ID),
DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false),
DEFINE_PROP_UINT32("x-msg-timeout", VFIOUserPCIDevice, wait_time, 5000),
DEFINE_PROP_BOOL("x-no-posted-writes", VFIOUserPCIDevice, no_post, false),
};
static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj);
bool success;
if (udev->device.vbasedev.proxy) {
error_setg(errp, "Proxy is connected");
return;
}
qapi_free_SocketAddress(udev->socket);
udev->socket = NULL;
success = visit_type_SocketAddress(v, name, &udev->socket, errp);
if (!success) {
return;
}
if (udev->socket->type != SOCKET_ADDRESS_TYPE_UNIX) {
error_setg(errp, "Unsupported socket type %s",
SocketAddressType_str(udev->socket->type));
qapi_free_SocketAddress(udev->socket);
udev->socket = NULL;
return;
}
}
static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass);
device_class_set_legacy_reset(dc, vfio_user_pci_reset);
device_class_set_props(dc, vfio_user_pci_dev_properties);
object_class_property_add(klass, "socket", "SocketAddress", NULL,
vfio_user_pci_set_socket, NULL, NULL);
object_class_property_set_description(klass, "socket",
"SocketAddress (UNIX sockets only)");
dc->desc = "VFIO over socket PCI device assignment";
pdc->realize = vfio_user_pci_realize;
}
static const TypeInfo vfio_user_pci_dev_info = {
.name = TYPE_VFIO_USER_PCI,
.parent = TYPE_VFIO_PCI_BASE,
.instance_size = sizeof(VFIOUserPCIDevice),
.class_init = vfio_user_pci_dev_class_init,
.instance_init = vfio_user_instance_init,
.instance_finalize = vfio_user_instance_finalize,
};
static void register_vfio_user_dev_type(void)
{
type_register_static(&vfio_user_pci_dev_info);
}
type_init(register_vfio_user_dev_type)
+242
View File
@@ -0,0 +1,242 @@
#ifndef VFIO_USER_PROTOCOL_H
#define VFIO_USER_PROTOCOL_H
/*
* vfio protocol over a UNIX socket.
*
* Copyright © 2018, 2021 Oracle and/or its affiliates.
*
* Each message has a standard header that describes the command
* being sent, which is almost always a VFIO ioctl().
*
* The header may be followed by command-specific data, such as the
* region and offset info for read and write commands.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
typedef struct {
uint16_t id;
uint16_t command;
uint32_t size;
uint32_t flags;
uint32_t error_reply;
} VFIOUserHdr;
/* VFIOUserHdr commands */
enum vfio_user_command {
VFIO_USER_VERSION = 1,
VFIO_USER_DMA_MAP = 2,
VFIO_USER_DMA_UNMAP = 3,
VFIO_USER_DEVICE_GET_INFO = 4,
VFIO_USER_DEVICE_GET_REGION_INFO = 5,
VFIO_USER_DEVICE_GET_REGION_IO_FDS = 6,
VFIO_USER_DEVICE_GET_IRQ_INFO = 7,
VFIO_USER_DEVICE_SET_IRQS = 8,
VFIO_USER_REGION_READ = 9,
VFIO_USER_REGION_WRITE = 10,
VFIO_USER_DMA_READ = 11,
VFIO_USER_DMA_WRITE = 12,
VFIO_USER_DEVICE_RESET = 13,
VFIO_USER_DIRTY_PAGES = 14,
VFIO_USER_REGION_WRITE_MULTI = 15,
VFIO_USER_MAX,
};
/* VFIOUserHdr flags */
#define VFIO_USER_REQUEST 0x0
#define VFIO_USER_REPLY 0x1
#define VFIO_USER_TYPE 0xF
#define VFIO_USER_NO_REPLY 0x10
#define VFIO_USER_ERROR 0x20
/*
* VFIO_USER_VERSION
*/
typedef struct {
VFIOUserHdr hdr;
uint16_t major;
uint16_t minor;
char capabilities[];
} VFIOUserVersion;
#define VFIO_USER_MAJOR_VER 0
#define VFIO_USER_MINOR_VER 0
#define VFIO_USER_CAP "capabilities"
/* "capabilities" members */
#define VFIO_USER_CAP_MAX_FDS "max_msg_fds"
#define VFIO_USER_CAP_MAX_XFER "max_data_xfer_size"
#define VFIO_USER_CAP_PGSIZES "pgsizes"
#define VFIO_USER_CAP_MAP_MAX "max_dma_maps"
#define VFIO_USER_CAP_MIGR "migration"
#define VFIO_USER_CAP_MULTI "write_multiple"
/* "migration" members */
#define VFIO_USER_CAP_PGSIZE "pgsize"
#define VFIO_USER_CAP_MAX_BITMAP "max_bitmap_size"
/*
* Max FDs mainly comes into play when a device supports multiple interrupts
* where each ones uses an eventfd to inject it into the guest.
* It is clamped by the the number of FDs the qio channel supports in a
* single message.
*/
#define VFIO_USER_DEF_MAX_FDS 8
#define VFIO_USER_MAX_MAX_FDS 16
/*
* Max transfer limits the amount of data in region and DMA messages.
* Region R/W will be very small (limited by how much a single instruction
* can process) so just use a reasonable limit here.
*/
#define VFIO_USER_DEF_MAX_XFER (1024 * 1024)
#define VFIO_USER_MAX_MAX_XFER (64 * 1024 * 1024)
/*
* Default pagesizes supported is 4k.
*/
#define VFIO_USER_DEF_PGSIZE 4096
/*
* Default max number of DMA mappings is stolen from the
* linux kernel "dma_entry_limit"
*/
#define VFIO_USER_DEF_MAP_MAX 65535
/*
* Default max bitmap size is also take from the linux kernel,
* where usage of signed ints limits the VA range to 2^31 bytes.
* Dividing that by the number of bits per byte yields 256MB
*/
#define VFIO_USER_DEF_MAX_BITMAP (256 * 1024 * 1024)
/*
* VFIO_USER_DMA_MAP
* imported from struct vfio_iommu_type1_dma_map
*/
typedef struct {
VFIOUserHdr hdr;
uint32_t argsz;
uint32_t flags;
uint64_t offset; /* FD offset */
uint64_t iova;
uint64_t size;
} VFIOUserDMAMap;
/*
* VFIO_USER_DMA_UNMAP
* imported from struct vfio_iommu_type1_dma_unmap
*/
typedef struct {
VFIOUserHdr hdr;
uint32_t argsz;
uint32_t flags;
uint64_t iova;
uint64_t size;
} VFIOUserDMAUnmap;
/*
* VFIO_USER_DEVICE_GET_INFO
* imported from struct vfio_device_info
*/
typedef struct {
VFIOUserHdr hdr;
uint32_t argsz;
uint32_t flags;
uint32_t num_regions;
uint32_t num_irqs;
} VFIOUserDeviceInfo;
/*
* VFIO_USER_DEVICE_GET_REGION_INFO
* imported from struct vfio_region_info
*/
typedef struct {
VFIOUserHdr hdr;
uint32_t argsz;
uint32_t flags;
uint32_t index;
uint32_t cap_offset;
uint64_t size;
uint64_t offset;
} VFIOUserRegionInfo;
/*
* VFIO_USER_DEVICE_GET_IRQ_INFO
* imported from struct vfio_irq_info
*/
typedef struct {
VFIOUserHdr hdr;
uint32_t argsz;
uint32_t flags;
uint32_t index;
uint32_t count;
} VFIOUserIRQInfo;
/*
* VFIO_USER_DEVICE_SET_IRQS
* imported from struct vfio_irq_set
*/
typedef struct {
VFIOUserHdr hdr;
uint32_t argsz;
uint32_t flags;
uint32_t index;
uint32_t start;
uint32_t count;
} VFIOUserIRQSet;
/*
* VFIO_USER_REGION_READ
* VFIO_USER_REGION_WRITE
*/
typedef struct {
VFIOUserHdr hdr;
uint64_t offset;
uint32_t region;
uint32_t count;
char data[];
} VFIOUserRegionRW;
/*
* VFIO_USER_DMA_READ
* VFIO_USER_DMA_WRITE
*/
typedef struct {
VFIOUserHdr hdr;
uint64_t offset;
uint32_t count;
char data[];
} VFIOUserDMARW;
/* imported from struct vfio_bitmap */
typedef struct {
uint64_t pgsize;
uint64_t size;
char data[];
} VFIOUserBitmap;
/*
* VFIO_USER_REGION_WRITE_MULTI
*/
#define VFIO_USER_MULTI_DATA 8
#define VFIO_USER_MULTI_MAX 200
typedef struct {
uint64_t offset;
uint32_t region;
uint32_t count;
char data[VFIO_USER_MULTI_DATA];
} VFIOUserWROne;
typedef struct {
VFIOUserHdr hdr;
uint64_t wr_cnt;
VFIOUserWROne wrs[VFIO_USER_MULTI_MAX];
} VFIOUserWRMulti;
#endif /* VFIO_USER_PROTOCOL_H */
+1356
View File
File diff suppressed because it is too large Load Diff
+135
View File
@@ -0,0 +1,135 @@
#ifndef VFIO_USER_PROXY_H
#define VFIO_USER_PROXY_H
/*
* vfio protocol over a UNIX socket.
*
* Copyright © 2018, 2021 Oracle and/or its affiliates.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "io/channel.h"
#include "io/channel-socket.h"
#include "qemu/queue.h"
#include "qemu/sockets.h"
#include "qemu/thread.h"
#include "hw/vfio/vfio-device.h"
#include "hw/vfio-user/protocol.h"
typedef struct {
int send_fds;
int recv_fds;
int *fds;
} VFIOUserFDs;
enum msg_type {
VFIO_MSG_NONE,
VFIO_MSG_ASYNC,
VFIO_MSG_WAIT,
VFIO_MSG_NOWAIT,
VFIO_MSG_REQ,
};
typedef struct VFIOUserMsg {
QTAILQ_ENTRY(VFIOUserMsg) next;
VFIOUserHdr *hdr;
VFIOUserFDs *fds;
uint32_t rsize;
uint32_t id;
QemuCond cv;
bool complete;
bool pending;
enum msg_type type;
} VFIOUserMsg;
enum proxy_state {
VFIO_PROXY_CONNECTED = 1,
VFIO_PROXY_ERROR = 2,
VFIO_PROXY_CLOSING = 3,
VFIO_PROXY_CLOSED = 4,
};
typedef QTAILQ_HEAD(VFIOUserMsgQ, VFIOUserMsg) VFIOUserMsgQ;
typedef struct VFIOUserProxy {
QLIST_ENTRY(VFIOUserProxy) next;
char *sockname;
struct QIOChannel *ioc;
void (*request)(void *opaque, VFIOUserMsg *msg);
void *req_arg;
uint64_t max_xfer_size;
uint64_t max_send_fds;
uint64_t max_dma;
uint64_t dma_pgsizes;
uint64_t max_bitmap;
uint64_t migr_pgsize;
int flags;
uint32_t wait_time;
QemuCond close_cv;
AioContext *ctx;
QEMUBH *req_bh;
bool async_ops;
/*
* above only changed when BQL is held
* below are protected by per-proxy lock
*/
QemuMutex lock;
VFIOUserMsgQ free;
VFIOUserMsgQ pending;
VFIOUserMsgQ incoming;
VFIOUserMsgQ outgoing;
VFIOUserMsg *last_nowait;
VFIOUserMsg *part_recv;
size_t recv_left;
VFIOUserWRMulti *wr_multi;
int num_outgoing;
enum proxy_state state;
} VFIOUserProxy;
/* VFIOProxy flags */
#define VFIO_PROXY_CLIENT 0x1
#define VFIO_PROXY_FORCE_QUEUED 0x4
#define VFIO_PROXY_NO_POST 0x8
#define VFIO_PROXY_USE_MULTI 0x16
/* coalescing high and low water marks for VFIOProxy num_outgoing */
#define VFIO_USER_OUT_HIGH 1024
#define VFIO_USER_OUT_LOW 128
typedef struct VFIODevice VFIODevice;
VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp);
void vfio_user_disconnect(VFIOUserProxy *proxy);
void vfio_user_set_handler(VFIODevice *vbasedev,
void (*handler)(void *opaque, VFIOUserMsg *msg),
void *reqarg);
bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp);
VFIOUserFDs *vfio_user_getfds(int numfds);
void vfio_user_putfds(VFIOUserMsg *msg);
void vfio_user_disable_posted_writes(VFIOUserProxy *proxy);
void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd,
uint32_t size, uint32_t flags);
void vfio_user_wait_reqs(VFIOUserProxy *proxy);
bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr,
VFIOUserFDs *fds, int rsize, Error **errp);
bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr,
VFIOUserFDs *fds, int rsize, Error **errp);
bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr,
VFIOUserFDs *fds, Error **errp);
void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size);
void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error);
void vfio_user_flush_multi(VFIOUserProxy *proxy);
void vfio_user_create_multi(VFIOUserProxy *proxy);
void vfio_user_add_multi(VFIOUserProxy *proxy, uint8_t index,
off_t offset, uint32_t count, void *data);
#endif /* VFIO_USER_PROXY_H */
+20
View File
@@ -0,0 +1,20 @@
# See docs/devel/tracing.rst for syntax documentation.
#
# SPDX-License-Identifier: GPL-2.0-or-later
# common.c
vfio_user_recv_hdr(const char *name, uint16_t id, uint16_t cmd, uint32_t size, uint32_t flags) " (%s) id 0x%x cmd 0x%x size 0x%x flags 0x%x"
vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x"
vfio_user_recv_request(uint16_t cmd) " command 0x%x"
vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x"
vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s"
vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d"
vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64
vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d"
vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d"
vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x"
vfio_user_wrmulti(const char *s, uint64_t wr_cnt) " %s count 0x%"PRIx64
# container.c
vfio_user_dma_map(uint64_t iova, uint64_t size, uint64_t off, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" off 0x%"PRIx64" flags 0x%x async_ops %d"
vfio_user_dma_unmap(uint64_t iova, uint64_t size, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" flags 0x%x async_ops %d"
+4
View File
@@ -0,0 +1,4 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "trace/trace-hw_vfio_user.h"
+2
View File
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-or-later
config VFIO
bool
depends on LINUX

Some files were not shown because too many files have changed in this diff Show More