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 <a...@yandex-team.com> --- 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