[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v6 07/11] virtio-gpu: Handle resource blob commands



On Wed, Jan 10, 2024 at 04:51:31PM +0800, Pierre-Eric Pelloux-Prayer wrote:
> 
> 
> Le 09/01/2024 à 17:50, Pierre-Eric Pelloux-Prayer a écrit :
> > 
> > 
> > Le 19/12/2023 à 08:53, Huang Rui a écrit :
> >> From: Antonio Caggiano <antonio.caggiano@xxxxxxxxxxxxx>
> >>
> >> Support BLOB resources creation, mapping and unmapping by calling the
> >> new stable virglrenderer 0.10 interface. Only enabled when available and
> >> via the blob config. E.g. -device virtio-vga-gl,blob=true
> >>
> >> Signed-off-by: Antonio Caggiano <antonio.caggiano@xxxxxxxxxxxxx>
> >> Signed-off-by: Dmitry Osipenko <dmitry.osipenko@xxxxxxxxxxxxx>
> >> Signed-off-by: Xenia Ragiadakou <xenia.ragiadakou@xxxxxxx>
> >> Signed-off-by: Huang Rui <ray.huang@xxxxxxx>
> >> ---
> >>
> >> Changes in v6:
> >> - Use new struct virgl_gpu_resource.
> >> - Unmap, unref and destroy the resource only after the memory region
> >>    has been completely removed.
> >> - In unref check whether the resource is still mapped.
> >> - In unmap_blob check whether the resource has been already unmapped.
> >> - Fix coding style
> >>
> >>   hw/display/virtio-gpu-virgl.c | 274 +++++++++++++++++++++++++++++++++-
> >>   hw/display/virtio-gpu.c       |   4 +-
> >>   meson.build                   |   4 +
> >>   3 files changed, 276 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
> >> index faab374336..5a3a292f79 100644
> >> --- a/hw/display/virtio-gpu-virgl.c
> >> +++ b/hw/display/virtio-gpu-virgl.c
> >> @@ -17,6 +17,7 @@
> >>   #include "trace.h"
> >>   #include "hw/virtio/virtio.h"
> >>   #include "hw/virtio/virtio-gpu.h"
> >> +#include "hw/virtio/virtio-gpu-bswap.h"
> >>   #include "ui/egl-helpers.h"
> >> @@ -24,8 +25,62 @@
> >>   struct virgl_gpu_resource {
> >>       struct virtio_gpu_simple_resource res;
> >> +    uint32_t ref;
> >> +    VirtIOGPU *g;
> >> +
> >> +#ifdef HAVE_VIRGL_RESOURCE_BLOB
> >> +    /* only blob resource needs this region to be mapped as guest mmio */
> >> +    MemoryRegion *region;
> >> +#endif
> >>   };
> >> +static void vres_get_ref(struct virgl_gpu_resource *vres)
> >> +{
> >> +    uint32_t ref;
> >> +
> >> +    ref = qatomic_fetch_inc(&vres->ref);
> >> +    g_assert(ref < INT_MAX);
> >> +}
> >> +
> >> +static void virgl_resource_destroy(struct virgl_gpu_resource *vres)
> >> +{
> >> +    struct virtio_gpu_simple_resource *res;
> >> +    VirtIOGPU *g;
> >> +
> >> +    if (!vres) {
> >> +        return;
> >> +    }
> >> +
> >> +    g = vres->g;
> >> +    res = &vres->res;
> >> +    QTAILQ_REMOVE(&g->reslist, res, next);
> >> +    virtio_gpu_cleanup_mapping(g, res);
> >> +    g_free(vres);
> >> +}
> >> +
> >> +static void virgl_resource_unref(struct virgl_gpu_resource *vres)
> >> +{
> >> +    struct virtio_gpu_simple_resource *res;
> >> +
> >> +    if (!vres) {
> >> +        return;
> >> +    }
> >> +
> >> +    res = &vres->res;
> >> +    virgl_renderer_resource_detach_iov(res->resource_id, NULL, NULL);
> >> +    virgl_renderer_resource_unref(res->resource_id);
> >> +}
> >> +
> >> +static void vres_put_ref(struct virgl_gpu_resource *vres)
> >> +{
> >> +    g_assert(vres->ref > 0);
> >> +
> >> +    if (qatomic_fetch_dec(&vres->ref) == 1) {
> >> +        virgl_resource_unref(vres);
> >> +        virgl_resource_destroy(vres);
> >> +    }
> >> +}
> >> +
> >>   static struct virgl_gpu_resource *
> >>   virgl_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id)
> >>   {
> >> @@ -59,6 +114,8 @@ static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
> >>                                          c2d.width, c2d.height);
> >>       vres = g_new0(struct virgl_gpu_resource, 1);
> >> +    vres_get_ref(vres);
> >> +    vres->g = g;
> >>       vres->res.width = c2d.width;
> >>       vres->res.height = c2d.height;
> >>       vres->res.format = c2d.format;
> >> @@ -91,6 +148,8 @@ static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
> >>                                          c3d.width, c3d.height, c3d.depth);
> >>       vres = g_new0(struct virgl_gpu_resource, 1);
> >> +    vres_get_ref(vres);
> >> +    vres->g = g;
> >>       vres->res.width = c3d.width;
> >>       vres->res.height = c3d.height;
> >>       vres->res.format = c3d.format;
> >> @@ -126,12 +185,21 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g,
> >>           return;
> >>       }
> >> -    virgl_renderer_resource_detach_iov(unref.resource_id, NULL, NULL);
> >> -    virgl_renderer_resource_unref(unref.resource_id);
> >> +#ifdef HAVE_VIRGL_RESOURCE_BLOB
> >> +    if (vres->region) {
> >> +        VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
> >> +        MemoryRegion *mr = vres->region;
> >> +
> >> +        warn_report("%s: blob resource %d not unmapped",
> >> +                    __func__, unref.resource_id);
> >> +        vres->region = NULL;
> > 
> > Shouldn't there be a call to memory_region_unref(mr)?
> > 
> >> +        memory_region_set_enabled(mr, false);
> >> +        memory_region_del_subregion(&b->hostmem, mr);
> >> +        object_unparent(OBJECT(mr));
> >> +    }
> >> +#endif /* HAVE_VIRGL_RESOURCE_BLOB */
> >> -    QTAILQ_REMOVE(&g->reslist, &vres->res, next);
> >> -    virtio_gpu_cleanup_mapping(g, &vres->res);
> >> -    g_free(vres);
> >> +    vres_put_ref(vres);
> >>   }
> >>   static void virgl_cmd_context_create(VirtIOGPU *g,
> >> @@ -470,6 +538,191 @@ static void virgl_cmd_get_capset(VirtIOGPU *g,
> >>       g_free(resp);
> >>   }
> >> +#ifdef HAVE_VIRGL_RESOURCE_BLOB
> >> +
> >> +static void virgl_resource_unmap(struct virgl_gpu_resource *vres)
> >> +{
> >> +    if (!vres) {
> >> +        return;
> >> +    }
> >> +
> >> +    virgl_renderer_resource_unmap(vres->res.resource_id);
> >> +
> >> +    vres_put_ref(vres);
> >> +}
> >> +
> >> +static void virgl_resource_blob_async_unmap(void *obj)
> >> +{
> >> +    MemoryRegion *mr = MEMORY_REGION(obj);
> >> +    struct virgl_gpu_resource *vres = mr->opaque;
> >> +
> >> +    virgl_resource_unmap(vres);
> >> +
> >> +    g_free(obj);
> >> +}
> >> +
> >> +static void virgl_cmd_resource_create_blob(VirtIOGPU *g,
> >> +                                           struct virtio_gpu_ctrl_command 
> >> *cmd)
> >> +{
> >> +    struct virgl_gpu_resource *vres;
> >> +    struct virtio_gpu_resource_create_blob cblob;
> >> +    struct virgl_renderer_resource_create_blob_args virgl_args = { 0 };
> >> +    int ret;
> >> +
> >> +    VIRTIO_GPU_FILL_CMD(cblob);
> >> +    virtio_gpu_create_blob_bswap(&cblob);
> >> +    trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size);
> >> +
> >> +    if (cblob.resource_id == 0) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not 
> >> allowed\n",
> >> +                      __func__);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    vres = virgl_gpu_find_resource(g, cblob.resource_id);
> >> +    if (vres) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
> >> +                      __func__, cblob.resource_id);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    vres = g_new0(struct virgl_gpu_resource, 1);
> >> +    vres_get_ref(vres);
> >> +    vres->g = g;
> >> +    vres->res.resource_id = cblob.resource_id;
> >> +    vres->res.blob_size = cblob.size;
> >> +
> >> +    if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D) {
> >> +        ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, 
> >> sizeof(cblob),
> >> +                                            cmd, &vres->res.addrs,
> >> +                                            &vres->res.iov, 
> >> &vres->res.iov_cnt);
> >> +        if (!ret) {
> >> +            g_free(vres);
> >> +            cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
> >> +            return;
> >> +        }
> >> +    }
> >> +
> >> +    QTAILQ_INSERT_HEAD(&g->reslist, &vres->res, next);
> >> +
> >> +    virgl_args.res_handle = cblob.resource_id;
> >> +    virgl_args.ctx_id = cblob.hdr.ctx_id;
> >> +    virgl_args.blob_mem = cblob.blob_mem;
> >> +    virgl_args.blob_id = cblob.blob_id;
> >> +    virgl_args.blob_flags = cblob.blob_flags;
> >> +    virgl_args.size = cblob.size;
> >> +    virgl_args.iovecs = vres->res.iov;
> >> +    virgl_args.num_iovs = vres->res.iov_cnt;
> >> +
> >> +    ret = virgl_renderer_resource_create_blob(&virgl_args);
> >> +    if (ret) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: virgl blob create error: 
> >> %s\n",
> >> +                      __func__, strerror(-ret));
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
> >> +    }
> >> +}
> >> +
> >> +static void virgl_cmd_resource_map_blob(VirtIOGPU *g,
> >> +                                        struct virtio_gpu_ctrl_command 
> >> *cmd)
> >> +{
> >> +    struct virgl_gpu_resource *vres;
> >> +    struct virtio_gpu_resource_map_blob mblob;
> >> +    int ret;
> >> +    void *data;
> >> +    uint64_t size;
> >> +    struct virtio_gpu_resp_map_info resp;
> >> +    VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
> >> +
> >> +    VIRTIO_GPU_FILL_CMD(mblob);
> >> +    virtio_gpu_map_blob_bswap(&mblob);
> >> +
> >> +    if (mblob.resource_id == 0) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not 
> >> allowed\n",
> >> +                      __func__);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    vres = virgl_gpu_find_resource(g, mblob.resource_id);
> >> +    if (!vres) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
> >> +                      __func__, mblob.resource_id);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +    if (vres->region) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already mapped %d\n",
> >> +                      __func__, mblob.resource_id);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    ret = virgl_renderer_resource_map(vres->res.resource_id, &data, 
> >> &size);
> >> +    if (ret) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource map error: %s\n",
> >> +                      __func__, strerror(-ret));
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    vres_get_ref(vres);
> > 
> > Why is this needed? And if it is, shouldn't virgl_cmd_resource_unmap_blob
> > call "vres_put_ref(vres)" ?
> > 
> >> +    vres->region = g_new0(MemoryRegion, 1);
> >> +    memory_region_init_ram_ptr(vres->region, OBJECT(g), NULL, size, data);
> >> +    vres->region->opaque = vres;
> >> +    OBJECT(vres->region)->free = virgl_resource_blob_async_unmap;
> >> +    memory_region_add_subregion(&b->hostmem, mblob.offset, vres->region);
> >> +    memory_region_set_enabled(vres->region, true);
> >> +
> >> +    memset(&resp, 0, sizeof(resp));
> >> +    resp.hdr.type = VIRTIO_GPU_RESP_OK_MAP_INFO;
> >> +    virgl_renderer_resource_get_map_info(mblob.resource_id, 
> >> &resp.map_info);
> >> +    virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
> >> +}
> >> +
> >> +static void virgl_cmd_resource_unmap_blob(VirtIOGPU *g,
> >> +                                          struct virtio_gpu_ctrl_command 
> >> *cmd)
> >> +{
> >> +    struct virgl_gpu_resource *vres;
> >> +    struct virtio_gpu_resource_unmap_blob ublob;
> >> +    VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
> >> +    MemoryRegion *mr;
> >> +
> >> +    VIRTIO_GPU_FILL_CMD(ublob);
> >> +    virtio_gpu_unmap_blob_bswap(&ublob);
> >> +
> >> +    if (ublob.resource_id == 0) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not 
> >> allowed\n",
> >> +                      __func__);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    vres = virgl_gpu_find_resource(g, ublob.resource_id);
> >> +    if (!vres) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
> >> +                      __func__, ublob.resource_id);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    if (!vres->region) {
> >> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already unmapped 
> >> %d\n",
> >> +                      __func__, ublob.resource_id);
> >> +        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
> >> +        return;
> >> +    }
> >> +
> >> +    mr = vres->region;
> >> +    vres->region = NULL;
> > 
> > memory_region_unref(mr)?
> > 
> > Note that AFAICT without the added memory_region_unref() calls 
> > virgl_resource_unmap()
> > was never called.
> 
> 
> Xenia and I figured out the refcounting issue: this code is written based on 
> the
> assumption that:
>   
>     object_unparent(OBJECT(mr));
> 
> 
> Will decrement the refcount. But this assumption is only true if 
> mr->parent_obj.parent
> is non-NULL.
> 
> The map_blob function uses the following arguments:
> 
>     memory_region_init_ram_ptr(vres->region, OBJECT(g), NULL, size, data);
> 
> Since name is NULL, mr won't be added as a child of 'g' and thus 
> object_unparent()
> does nothing.
> 
> I'd suggest 2 changes:
>     * use a name ("blob_memory"?) to so mr can be a child of g
>     * increment mr's refcount when setting vres->region and decrement it when 
> clearing it.
>       This change is not needed technically but when a variable is refcounted 
> it seems
>       clearer to increment/decrement the refcount in these situations.
> 

Yes, issue is caused by NULL in name field, I will update according to your
suggestions in V7.

Thanks,
Ray

> 
> Pierre-Eric
> 
> 
> > 
> >> +    memory_region_set_enabled(mr, false);
> >> +    memory_region_del_subregion(&b->hostmem, mr);
> >> +    object_unparent(OBJECT(mr));
> >> +}
> >> +
> >> +#endif /* HAVE_VIRGL_RESOURCE_BLOB */
> >> +
> >>   void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
> >>                                         struct virtio_gpu_ctrl_command 
> >> *cmd)
> >>   {
> >> @@ -536,6 +789,17 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
> >>       case VIRTIO_GPU_CMD_GET_EDID:
> >>           virtio_gpu_get_edid(g, cmd);
> >>           break;
> >> +#ifdef HAVE_VIRGL_RESOURCE_BLOB
> >> +    case VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB:
> >> +        virgl_cmd_resource_create_blob(g, cmd);
> >> +        break;
> >> +    case VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB:
> >> +        virgl_cmd_resource_map_blob(g, cmd);
> >> +        break;
> >> +    case VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB:
> >> +        virgl_cmd_resource_unmap_blob(g, cmd);
> >> +        break;
> >> +#endif /* HAVE_VIRGL_RESOURCE_BLOB */
> >>       default:
> >>           cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
> >>           break;
> >> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> >> index 4c3ec9d0ea..8189c392dc 100644
> >> --- a/hw/display/virtio-gpu.c
> >> +++ b/hw/display/virtio-gpu.c
> >> @@ -1449,10 +1449,12 @@ void virtio_gpu_device_realize(DeviceState *qdev, 
> >> Error **errp)
> >>               return;
> >>           }
> >> +#ifndef HAVE_VIRGL_RESOURCE_BLOB
> >>           if (virtio_gpu_virgl_enabled(g->parent_obj.conf)) {
> >> -            error_setg(errp, "blobs and virgl are not compatible (yet)");
> >> +            error_setg(errp, "Linked virglrenderer does not support blob 
> >> resources");
> >>               return;
> >>           }
> >> +#endif
> >>       }
> >>       if (!virtio_gpu_base_device_realize(qdev,
> >> diff --git a/meson.build b/meson.build
> >> index ea52ef1b9c..629407128e 100644
> >> --- a/meson.build
> >> +++ b/meson.build
> >> @@ -1054,6 +1054,10 @@ if not get_option('virglrenderer').auto() or 
> >> have_system or have_vhost_user_gpu
> >>                            
> >> cc.has_function('virgl_renderer_context_create_with_flags',
> >>                                            prefix: '#include 
> >> <virglrenderer.h>',
> >>                                            dependencies: virgl))
> >> +    config_host_data.set('HAVE_VIRGL_RESOURCE_BLOB',
> >> +                         
> >> cc.has_function('virgl_renderer_resource_create_blob',
> >> +                                         prefix: '#include 
> >> <virglrenderer.h>',
> >> +                                         dependencies: virgl))
> >>     endif
> >>   endif
> >>   rutabaga = not_found
> > 



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.