@samitouri / QOSamiQemu / commits / 4eb0aace85

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>

Dmitry Osipenko committed Mar 4, 2026 at 16:50 UTC 4eb0aace85f507e21bb8ac323ad81c5860f7a52d
3 files changed +97 -2
hw/display/virtio-gpu-gl.c
+36 -1
@@ -13,6 +13,7 @@
13
14 #include "qemu/osdep.h"
15 #include "qemu/iov.h"
16 +#include "qemu/mmap-alloc.h"
17 #include "qemu/module.h"
18 #include "qemu/error-report.h"
19 #include "qapi/error.h"
@@ -106,7 +107,12 @@ static void virtio_gpu_gl_reset(VirtIODevice *vdev)
107 static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp)
108 {
109 ERRP_GUARD();
109 - VirtIOGPU *g = VIRTIO_GPU(qdev);
110 + VirtIOGPUBase *b = VIRTIO_GPU_BASE(qdev);
111 + VirtIOGPU *g = VIRTIO_GPU(b);
112 +#if !defined(CONFIG_WIN32)
113 + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
114 + void *map;
115 +#endif
116
117 #if HOST_BIG_ENDIAN
118 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)
142 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED;
143 #endif
144
145 +#if !defined(CONFIG_WIN32)
146 + if (virtio_gpu_hostmem_enabled(b->conf)) {
147 + map = qemu_ram_mmap(-1, b->conf.hostmem, qemu_real_host_page_size(),
148 + 0, 0);
149 + if (map == MAP_FAILED) {
150 + error_setg_errno(errp, errno,
151 + "virgl hostmem region could not be initialized");
152 + return;
153 + }
154 +
155 + gl->hostmem_mmap = map;
156 + memory_region_init_ram_ptr(&gl->hostmem_background, NULL,
157 + "hostmem-background", b->conf.hostmem,
158 + gl->hostmem_mmap);
159 + memory_region_add_subregion(&b->hostmem, 0, &gl->hostmem_background);
160 + }
161 +#endif
162 +
163 virtio_gpu_device_realize(qdev, errp);
164 }
165
@@ -172,6 +196,17 @@ static void virtio_gpu_gl_device_unrealize(DeviceState *qdev)
196 gl->renderer_state = RS_START;
197
198 g_array_unref(g->capset_ids);
199 +
200 + /*
201 + * It is not guaranteed that the memory region will be finalized
202 + * immediately with memory_region_del_subregion(), there can be
203 + * a remaining reference to gl->hostmem_mmap. VirtIO-GPU is not
204 + * hotpluggable, hence no need to worry about the leaked mapping.
205 + *
206 + * The memory_region_del_subregion(gl->hostmem_background) is unnecessary
207 + * because b->hostmem and gl->hostmem_background belong to the same
208 + * device and will be gone at the same time.
209 + */
210 }
211
212 static void virtio_gpu_gl_class_init(ObjectClass *klass, const void *data)
hw/display/virtio-gpu-virgl.c
+58 -1
@@ -41,9 +41,13 @@
41 VIRGL_VERSION_MICRO >= (micro))
42 #endif
43
44 +#define VIRGL_HAS_MAP_FIXED \
45 + (VIRGL_CHECK_VERSION(1, 3, 0) && !IS_ENABLED(CONFIG_WIN32))
46 +
47 struct virtio_gpu_virgl_resource {
48 struct virtio_gpu_simple_resource base;
49 MemoryRegion *mr;
50 + void *map_fixed;
51 };
52
53 static struct virtio_gpu_virgl_resource *
@@ -155,6 +159,9 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
159 g_autofree char *name = NULL;
160 struct virtio_gpu_virgl_hostmem_region *vmr;
161 VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
162 +#if VIRGL_HAS_MAP_FIXED
163 + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
164 +#endif
165 MemoryRegion *mr;
166 uint64_t size;
167 void *data;
@@ -165,6 +172,41 @@ virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
172 return -EOPNOTSUPP;
173 }
174
175 +#if VIRGL_HAS_MAP_FIXED
176 + /*
177 + * virgl_renderer_resource_map_fixed() allows to create multiple
178 + * mappings of the same resource, while virgl_renderer_resource_map()
179 + * not. Don't allow mapping same resource twice.
180 + */
181 + if (res->map_fixed || res->mr) {
182 + qemu_log_mask(LOG_GUEST_ERROR,
183 + "%s: failed to map(fixed) virgl resource: already mapped\n",
184 + __func__);
185 + return -EBUSY;
186 + }
187 +
188 + ret = virgl_renderer_resource_map_fixed(res->base.resource_id,
189 + gl->hostmem_mmap + offset);
190 + switch (ret) {
191 + case 0:
192 + res->map_fixed = gl->hostmem_mmap + offset;
193 + return 0;
194 +
195 + case -EOPNOTSUPP:
196 + /*
197 + * MAP_FIXED is unsupported by this resource.
198 + * Mapping falls back to a blob subregion method in that case.
199 + */
200 + break;
201 +
202 + default:
203 + qemu_log_mask(LOG_GUEST_ERROR,
204 + "%s: failed to map(fixed) virgl resource: %s\n",
205 + __func__, strerror(-ret));
206 + return ret;
207 + }
208 +#endif
209 +
210 ret = virgl_renderer_resource_map(res->base.resource_id, &data, &size);
211 if (ret) {
212 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,
223
224 mr = &vmr->mr;
225 memory_region_init_ram_ptr(mr, OBJECT(vmr), "mr", size, data);
184 - memory_region_add_subregion(&b->hostmem, offset, mr);
226 + memory_region_add_subregion_overlap(&b->hostmem, offset, mr, 1);
227
228 res->mr = mr;
229
@@ -200,6 +242,21 @@ virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
242 MemoryRegion *mr = res->mr;
243 int ret;
244
245 +#if VIRGL_HAS_MAP_FIXED
246 + if (res->map_fixed) {
247 + if (mmap(res->map_fixed, res->base.blob_size, PROT_READ | PROT_WRITE,
248 + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
249 + -1, 0) == MAP_FAILED) {
250 + ret = -errno;
251 + error_report("%s: failed to unmap(fixed) virgl resource: %s",
252 + __func__, strerror(-ret));
253 + return ret;
254 + }
255 +
256 + res->map_fixed = NULL;
257 + }
258 +#endif
259 +
260 if (!mr) {
261 return 0;
262 }
include/hw/virtio/virtio-gpu.h
+3
@@ -263,6 +263,9 @@ struct VirtIOGPUGL {
263
264 QEMUBH *async_fence_bh;
265 QSLIST_HEAD(, virtio_gpu_virgl_context_fence) async_fenceq;
266 +
267 + MemoryRegion hostmem_background;
268 + void *hostmem_mmap;
269 };
270
271 struct VhostUserGPU {