Browse Source

virtio-gpu: Support mapping hostmem blobs with map_fixed

Support mapping virgl blobs to a fixed location of a hostmem memory
region using new virglrenderer MAP_FIXED API.

This new feature closes multiple problems for virtio-gpu on QEMU:

- Having dedicated memory region for each mapped blob works notoriously
slow due to QEMU's memory region software design built around RCU that
isn't optimized for frequent removal of the regions

- KVM isn't optimized for a frequent slot changes too

- QEMU/KVM has a limit for a total number of created memory regions,
crashing QEMU when limit is reached

This patch makes virtio-gpu-gl to pre-create a single anonymous memory
region covering whole hostmem area to which blobs will be mapped using
the MAP_FIXED API.

Not all virgl resources will support mapping at a fixed memory address. For
them, we will continue to create individual nested memory sub-regions. In
particular, vrend resources may not have MAP_FIXED capability.

Venus and DRM native contexts will largely benefit from the MAP_FIXED
feature in terms of performance and stability improvement.

Tested-by: Yiwei Zhang <zzyiwei@gmail.com>
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Message-ID: <20260303151422.977399-19-dmitry.osipenko@collabora.com>
Message-ID: <20260304165043.1437519-21-alex.bennee@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
master
Dmitry Osipenko 4 weeks ago
committed by Alex Bennée
parent
commit
4eb0aace85
  1. 37
      hw/display/virtio-gpu-gl.c
  2. 59
      hw/display/virtio-gpu-virgl.c
  3. 3
      include/hw/virtio/virtio-gpu.h

37
hw/display/virtio-gpu-gl.c

@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "qemu/iov.h"
#include "qemu/mmap-alloc.h"
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
@ -106,7 +107,12 @@ static void virtio_gpu_gl_reset(VirtIODevice *vdev)
static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp)
{
ERRP_GUARD();
VirtIOGPU *g = VIRTIO_GPU(qdev);
VirtIOGPUBase *b = VIRTIO_GPU_BASE(qdev);
VirtIOGPU *g = VIRTIO_GPU(b);
#if !defined(CONFIG_WIN32)
VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
void *map;
#endif
#if HOST_BIG_ENDIAN
error_setg(errp, "virgl is not supported on bigendian platforms");
@ -136,6 +142,24 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp)
g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED;
#endif
#if !defined(CONFIG_WIN32)
if (virtio_gpu_hostmem_enabled(b->conf)) {
map = qemu_ram_mmap(-1, b->conf.hostmem, qemu_real_host_page_size(),
0, 0);
if (map == MAP_FAILED) {
error_setg_errno(errp, errno,
"virgl hostmem region could not be initialized");
return;
}
gl->hostmem_mmap = map;
memory_region_init_ram_ptr(&gl->hostmem_background, NULL,
"hostmem-background", b->conf.hostmem,
gl->hostmem_mmap);
memory_region_add_subregion(&b->hostmem, 0, &gl->hostmem_background);
}
#endif
virtio_gpu_device_realize(qdev, errp);
}
@ -172,6 +196,17 @@ static void virtio_gpu_gl_device_unrealize(DeviceState *qdev)
gl->renderer_state = RS_START;
g_array_unref(g->capset_ids);
/*
* It is not guaranteed that the memory region will be finalized
* immediately with memory_region_del_subregion(), there can be
* a remaining reference to gl->hostmem_mmap. VirtIO-GPU is not
* hotpluggable, hence no need to worry about the leaked mapping.
*
* The memory_region_del_subregion(gl->hostmem_background) is unnecessary
* because b->hostmem and gl->hostmem_background belong to the same
* device and will be gone at the same time.
*/
}
static void virtio_gpu_gl_class_init(ObjectClass *klass, const void *data)

59
hw/display/virtio-gpu-virgl.c

@ -41,9 +41,13 @@
VIRGL_VERSION_MICRO >= (micro))
#endif
#define VIRGL_HAS_MAP_FIXED \
(VIRGL_CHECK_VERSION(1, 3, 0) && !IS_ENABLED(CONFIG_WIN32))
struct virtio_gpu_virgl_resource {
struct virtio_gpu_simple_resource base;
MemoryRegion *mr;
void *map_fixed;
};
static struct virtio_gpu_virgl_resource *
@ -155,6 +159,9 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
g_autofree char *name = NULL;
struct virtio_gpu_virgl_hostmem_region *vmr;
VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
#if VIRGL_HAS_MAP_FIXED
VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
#endif
MemoryRegion *mr;
uint64_t size;
void *data;
@ -165,6 +172,41 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
return -EOPNOTSUPP;
}
#if VIRGL_HAS_MAP_FIXED
/*
* virgl_renderer_resource_map_fixed() allows to create multiple
* mappings of the same resource, while virgl_renderer_resource_map()
* not. Don't allow mapping same resource twice.
*/
if (res->map_fixed || res->mr) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: failed to map(fixed) virgl resource: already mapped\n",
__func__);
return -EBUSY;
}
ret = virgl_renderer_resource_map_fixed(res->base.resource_id,
gl->hostmem_mmap + offset);
switch (ret) {
case 0:
res->map_fixed = gl->hostmem_mmap + offset;
return 0;
case -EOPNOTSUPP:
/*
* MAP_FIXED is unsupported by this resource.
* Mapping falls back to a blob subregion method in that case.
*/
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: failed to map(fixed) virgl resource: %s\n",
__func__, strerror(-ret));
return ret;
}
#endif
ret = virgl_renderer_resource_map(res->base.resource_id, &data, &size);
if (ret) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map virgl resource: %s\n",
@ -181,7 +223,7 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
mr = &vmr->mr;
memory_region_init_ram_ptr(mr, OBJECT(vmr), "mr", size, data);
memory_region_add_subregion(&b->hostmem, offset, mr);
memory_region_add_subregion_overlap(&b->hostmem, offset, mr, 1);
res->mr = mr;
@ -200,6 +242,21 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
MemoryRegion *mr = res->mr;
int ret;
#if VIRGL_HAS_MAP_FIXED
if (res->map_fixed) {
if (mmap(res->map_fixed, res->base.blob_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0) == MAP_FAILED) {
ret = -errno;
error_report("%s: failed to unmap(fixed) virgl resource: %s",
__func__, strerror(-ret));
return ret;
}
res->map_fixed = NULL;
}
#endif
if (!mr) {
return 0;
}

3
include/hw/virtio/virtio-gpu.h

@ -263,6 +263,9 @@ struct VirtIOGPUGL {
QEMUBH *async_fence_bh;
QSLIST_HEAD(, virtio_gpu_virgl_context_fence) async_fenceq;
MemoryRegion hostmem_background;
void *hostmem_mmap;
};
struct VhostUserGPU {

Loading…
Cancel
Save