|
[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
> >
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |