Add vfio-group type and allow user to create such object via
'-object' command line argument or 'object-add' qmp command.
Parameters are:
- @fd - file descriptor
- @container - id of vfio-container object which will be used for
this VFIO group
- @groupid - number representing IOMMU group (no needed if @fd
and @container were provided)
E.g.:
-object vfio-container,id=ct,fd=5 \
-object vfio-group,id=group,fd=6,container=ct
Signed-off-by: Andrey Ryabinin <[email protected]>
---
hw/vfio/common.c | 267 +++++++++++++++++++++++-----------
hw/vfio/trace-events | 2 +-
include/hw/vfio/vfio-common.h | 4 +
qapi/qom.json | 15 ++
4 files changed, 205 insertions(+), 83 deletions(-)
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 392057d3025..95722ecf96a 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -1911,31 +1911,40 @@ static int vfio_init_container(VFIOContainer
*container, int group_fd,
Error **errp)
{
int iommu_type, ret;
+ struct vfio_group_status status = { .argsz = sizeof(status) };
iommu_type = vfio_get_iommu_type(container, errp);
if (iommu_type < 0) {
return iommu_type;
}
- ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd);
+
+ ret = ioctl(group_fd, VFIO_GROUP_GET_STATUS, &status);
if (ret) {
- error_setg_errno(errp, errno, "Failed to set group container");
+ error_setg_errno(errp, errno, "Failed to get group status");
return -errno;
}
-
- while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) {
- if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
- /*
- * On sPAPR, despite the IOMMU subdriver always advertises v1 and
- * v2, the running platform may not support v2 and there is no
- * way to guess it until an IOMMU group gets added to the
container.
- * So in case it fails with v2, try v1 as a fallback.
- */
- iommu_type = VFIO_SPAPR_TCE_IOMMU;
- continue;
+ if (!(status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) {
+ ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd);
+ if (ret) {
+ error_setg_errno(errp, errno, "Failed to set group container");
+ return -errno;
+ }
+
+ while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) {
+ if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ /*
+ * On sPAPR, despite the IOMMU subdriver always advertises v1
and
+ * v2, the running platform may not support v2 and there is no
+ * way to guess it until an IOMMU group gets added to the
container.
+ * So in case it fails with v2, try v1 as a fallback.
+ */
+ iommu_type = VFIO_SPAPR_TCE_IOMMU;
+ continue;
+ }
+ error_setg_errno(errp, errno, "Failed to set iommu for container");
+ return -errno;
}
- error_setg_errno(errp, errno, "Failed to set iommu for container");
- return -errno;
}
container->iommu_type = iommu_type;
@@ -2050,34 +2059,44 @@ static int vfio_connect_container(VFIOGroup *group,
AddressSpace *as,
* with some IOMMU types. vfio_ram_block_discard_disable() handles the
* details once we know which type of IOMMU we are using.
*/
-
- QLIST_FOREACH(container, &space->containers, next) {
- if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
- ret = vfio_ram_block_discard_disable(container, true);
- if (ret) {
- error_setg_errno(errp, -ret,
- "Cannot set discarding of RAM broken");
- if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER,
- &container->fd)) {
- error_report("vfio: error disconnecting group %d from"
- " container", group->groupid);
+ if (!group->container) {
+ QLIST_FOREACH(container, &space->containers, next) {
+ if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
+ ret = vfio_ram_block_discard_disable(container, true);
+ if (ret) {
+ error_setg_errno(errp, -ret,
+ "Cannot set discarding of RAM broken");
+ if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER,
+ &container->fd)) {
+ error_report("vfio: error disconnecting group %d from"
+ " container", group->groupid);
+ }
+ return ret;
}
- return ret;
+ group->container = container;
+ QLIST_INSERT_HEAD(&container->group_list, group,
container_next);
+ vfio_kvm_device_add_group(group);
+ return 0;
}
- group->container = container;
- QLIST_INSERT_HEAD(&container->group_list, group, container_next);
- vfio_kvm_device_add_group(group);
- return 0;
}
- }
+ container = VFIO_CONTAINER(object_new(TYPE_VFIO_CONTAINER));
+ container->space = space;
- container = VFIO_CONTAINER(object_new(TYPE_VFIO_CONTAINER));
- container->space = space;
-
- user_creatable_complete(USER_CREATABLE(container), errp);
- if (*errp) {
- ret = -1;
- goto free_container_exit;
+ user_creatable_complete(USER_CREATABLE(container), errp);
+ if (*errp) {
+ ret = -1;
+ goto free_container_exit;
+ }
+ group->container = container;
+ } else if (group->container->initialized) {
+ object_ref(OBJECT(group->container));
+ QLIST_INSERT_HEAD(&group->container->group_list, group,
container_next);
+ vfio_kvm_device_add_group(group);
+ return 0;
+ } else {
+ container = group->container;
+ container->space = space;
+ object_ref(OBJECT(container));
}
ret = vfio_init_container(container, group->fd, errp);
@@ -2228,6 +2247,10 @@ static void vfio_disconnect_container(VFIOGroup *group)
{
VFIOContainer *container = group->container;
+ if (!group->container) {
+ return;
+ }
+
QLIST_REMOVE(group, container_next);
group->container = NULL;
@@ -2251,7 +2274,6 @@ static void vfio_disconnect_container(VFIOGroup *group)
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp)
{
VFIOGroup *group;
- char path[32];
struct vfio_group_status status = { .argsz = sizeof(status) };
QLIST_FOREACH(group, &vfio_group_list, next) {
@@ -2267,31 +2289,14 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace
*as, Error **errp)
}
}
- group = g_malloc0(sizeof(*group));
-
- snprintf(path, sizeof(path), "/dev/vfio/%d", groupid);
- group->fd = qemu_open_old(path, O_RDWR);
- if (group->fd < 0) {
- error_setg_errno(errp, errno, "failed to open %s", path);
- goto free_group_exit;
- }
-
- if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
- error_setg_errno(errp, errno, "failed to get group %d status",
groupid);
- goto close_fd_exit;
- }
-
- if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
- error_setg(errp, "group %d is not viable", groupid);
- error_append_hint(errp,
- "Please ensure all devices within the iommu_group "
- "are bound to their vfio bus driver.\n");
- goto close_fd_exit;
+ group = VFIO_GROUP(object_new(TYPE_VFIO_GROUP));
+ object_property_set_int(OBJECT(group), "groupid", groupid, errp);
+ user_creatable_complete(USER_CREATABLE(group), errp);
+ if (*errp) {
+ object_unref(OBJECT(group));
+ return NULL;
}
- group->groupid = groupid;
- QLIST_INIT(&group->device_list);
-
if (vfio_connect_container(group, as, errp)) {
error_prepend(errp, "failed to setup container for group %d: ",
groupid);
@@ -2302,15 +2307,10 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace
*as, Error **errp)
qemu_register_reset(vfio_reset_handler, NULL);
}
- QLIST_INSERT_HEAD(&vfio_group_list, group, next);
-
return group;
close_fd_exit:
- close(group->fd);
-
-free_group_exit:
- g_free(group);
+ object_unref(OBJECT(group));
return NULL;
}
@@ -2321,19 +2321,7 @@ void vfio_put_group(VFIOGroup *group)
return;
}
- if (!group->ram_block_discard_allowed) {
- vfio_ram_block_discard_disable(group->container, false);
- }
- vfio_kvm_device_del_group(group);
- vfio_disconnect_container(group);
- QLIST_REMOVE(group, next);
- trace_vfio_put_group(group->fd);
- close(group->fd);
- g_free(group);
-
- if (QLIST_EMPTY(&vfio_group_list)) {
- qemu_unregister_reset(vfio_reset_handler, NULL);
- }
+ object_unref(OBJECT(group));
}
int vfio_get_device(VFIOGroup *group, const char *name,
@@ -2676,8 +2664,123 @@ static const TypeInfo vfio_container_info = {
},
};
+static void vfio_group_set_fd(Object *obj, const char *value,
+ Error **errp)
+{
+ VFIOGroup *group = VFIO_GROUP(obj);
+
+ group->fd = monitor_fd_param(monitor_cur(), value, errp);
+}
+
+static void vfio_group_set_groupid(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ VFIOGroup *group = VFIO_GROUP(obj);
+ Error *error = NULL;
+ uint32_t groupid;
+
+ visit_type_uint32(v, name, &groupid, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
+ group->groupid = groupid;
+}
+
+static void vfio_group_complete(UserCreatable *uc, Error **errp)
+{
+ VFIOGroup *group = VFIO_GROUP(uc);
+ struct vfio_group_status status = { .argsz = sizeof(status) };
+
+ if (group->fd < 0 && group->groupid >= 0) {
+ char path[32];
+
+ snprintf(path, sizeof(path), "/dev/vfio/%d", group->groupid);
+
+ group->fd = qemu_open_old(path, O_RDWR);
+ if (group->fd < 0) {
+ error_setg_errno(errp, errno, "failed to open %s", path);
+ return;
+ }
+ }
+
+ if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
+ error_setg_errno(errp, errno, "failed to get group %d status",
group->groupid);
+ return;
+ }
+
+ if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
+ error_setg(errp, "group %d is not viable", group->groupid);
+ error_append_hint(errp,
+ "Please ensure all devices within the iommu_group "
+ "are bound to their vfio bus driver.\n");
+ }
+}
+
+static void vfio_group_class_init(ObjectClass *class, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(class);
+ ucc->complete = vfio_group_complete;
+
+ object_class_property_add_link(class, "container", TYPE_VFIO_CONTAINER,
+ offsetof(VFIOGroup, container),
+ object_property_allow_set_link, 0);
+ object_class_property_add_str(class, "fd", NULL, vfio_group_set_fd);
+ object_class_property_add(class, "groupid", "int", NULL,
+ vfio_group_set_groupid,
+ NULL, NULL);
+}
+
+static void vfio_group_instance_init(Object *obj)
+{
+ VFIOGroup *group = VFIO_GROUP(obj);
+
+ QLIST_INIT(&group->device_list);
+ group->fd = -1;
+ group->groupid = -1;
+ QLIST_INSERT_HEAD(&vfio_group_list, group, next);
+}
+
+static void
+vfio_group_instance_finalize(Object *obj)
+{
+ VFIOGroup *group = VFIO_GROUP(obj);
+
+ if (!group->ram_block_discard_allowed) {
+ vfio_ram_block_discard_disable(group->container, false);
+ }
+
+ vfio_kvm_device_del_group(group);
+ vfio_disconnect_container(group);
+ QLIST_REMOVE(group, next);
+ trace_vfio_group_instance_finalize(group->fd);
+ if (group->fd >= 0) {
+ close(group->fd);
+ }
+
+ if (QLIST_EMPTY(&vfio_group_list)) {
+ qemu_unregister_reset(vfio_reset_handler, NULL);
+ }
+}
+
+static const TypeInfo vfio_group_info = {
+ .name = TYPE_VFIO_GROUP,
+ .parent = TYPE_OBJECT,
+ .class_init = vfio_group_class_init,
+ .instance_size = sizeof(VFIOGroup),
+ .instance_init = vfio_group_instance_init,
+ .instance_finalize = vfio_group_instance_finalize,
+ .interfaces = (InterfaceInfo[]) {
+ {TYPE_USER_CREATABLE},
+ {}
+ },
+};
+
static void register_vfio_types(void)
{
type_register_static(&vfio_container_info);
+ type_register_static(&vfio_group_info);
}
type_init(register_vfio_types)
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index 8b79cf33a33..6ae0ed09acd 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -105,7 +105,7 @@ vfio_listener_region_add_no_dma_map(const char *name,
uint64_t iova, uint64_t si
vfio_listener_region_del_skip(uint64_t start, uint64_t end) "SKIPPING
region_del 0x%"PRIx64" - 0x%"PRIx64
vfio_listener_region_del(uint64_t start, uint64_t end) "region_del 0x%"PRIx64"
- 0x%"PRIx64
vfio_container_instance_finalize(int fd) "close container->fd=%d"
-vfio_put_group(int fd) "close group->fd=%d"
+vfio_group_instance_finalize(int fd) "close group->fd=%d"
vfio_get_device(const char * name, unsigned int flags, unsigned int
num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u"
vfio_put_base_device(int fd) "close vdev->fd=%d"
vfio_region_setup(const char *dev, int index, const char *name, unsigned long
flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\",
flags: 0x%lx, offset: 0x%lx, size: 0x%lx"
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 0ab99060e44..f2d67093f44 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -156,6 +156,7 @@ struct VFIODeviceOps {
};
typedef struct VFIOGroup {
+ Object parent;
int fd;
int groupid;
VFIOContainer *container;
@@ -194,6 +195,9 @@ typedef struct VFIODisplay {
#define TYPE_VFIO_CONTAINER "vfio-container"
OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_CONTAINER)
+#define TYPE_VFIO_GROUP "vfio-group"
+OBJECT_DECLARE_SIMPLE_TYPE(VFIOGroup, VFIO_GROUP)
+
void vfio_put_base_device(VFIODevice *vbasedev);
void vfio_disable_irqindex(VFIODevice *vbasedev, int index);
void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index);
diff --git a/qapi/qom.json b/qapi/qom.json
index d1a88e10b52..f46dd6b8034 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -746,6 +746,19 @@
{ 'struct': 'VFIOContainerProperties',
'data': { 'fd': 'str' } }
+##
+# @VFIOGroupProperties:
+#
+# Properties for vfio-group objects.
+#
+# @fd: file descriptor of vfio group
+# @container: container
+#
+# Since: 7.2
+##
+{ 'struct': 'VFIOGroupProperties',
+ 'data': { 'fd': 'str', 'container': 'str'} }
+
##
# @VfioUserServerProperties:
#
@@ -901,6 +914,7 @@
'tls-creds-x509',
'tls-cipher-suites',
'vfio-container',
+ 'vfio-group',
{ 'name': 'x-remote-object', 'features': [ 'unstable' ] },
{ 'name': 'x-vfio-user-server', 'features': [ 'unstable' ] }
] }
@@ -967,6 +981,7 @@
'tls-creds-x509': 'TlsCredsX509Properties',
'tls-cipher-suites': 'TlsCredsProperties',
'vfio-container': 'VFIOContainerProperties',
+ 'vfio-group': 'VFIOGroupProperties',
'x-remote-object': 'RemoteObjectProperties',
'x-vfio-user-server': 'VfioUserServerProperties'
} }
--
2.37.3