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


Reply via email to