virtio-gpu: Destroy virgl resources on virtio-gpu reset
Properly destroy virgl resources on virtio-gpu reset to not leak resources on a hot reboot of a VM. Suggested-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Message-ID: <20260303151422.977399-18-dmitry.osipenko@collabora.com> Message-ID: <20260304165043.1437519-20-alex.bennee@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Dmitry Osipenko committed
Mar 4, 2026 at 16:50 UTC
bd9258917fbf57eb0de030d03cd5bbb4acc88402
3 files changed
+107
-40
hw/display/virtio-gpu-gl.c
+2
-16
@@ -63,29 +63,14 @@ static void virtio_gpu_gl_flushed(VirtIOGPUBase *b)
63
static void virtio_gpu_gl_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
64
{
65
VirtIOGPU *g = VIRTIO_GPU(vdev);
66
- VirtIOGPUGL *gl = VIRTIO_GPU_GL(vdev);
66
struct virtio_gpu_ctrl_command *cmd;
67
68
if (!virtio_queue_ready(vq)) {
69
return;
70
}
71
73
- switch (gl->renderer_state) {
74
- case RS_RESET:
75
- virtio_gpu_virgl_reset(g);
76
- /* fallthrough */
77
- case RS_START:
78
- if (virtio_gpu_virgl_init(g)) {
79
- gl->renderer_state = RS_INIT_FAILED;
80
- return;
81
- }
82
-
83
- gl->renderer_state = RS_INITED;
84
- break;
85
- case RS_INIT_FAILED:
72
+ if (!virtio_gpu_virgl_update_render_state(g)) {
73
return;
87
- case RS_INITED:
88
- break;
74
}
75
76
cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command));
@@ -201,6 +186,7 @@ static void virtio_gpu_gl_class_init(ObjectClass *klass, const void *data)
186
vgc->process_cmd = virtio_gpu_virgl_process_cmd;
187
vgc->update_cursor_data = virtio_gpu_gl_update_cursor_data;
188
189
+ vgc->resource_destroy = virtio_gpu_virgl_resource_destroy;
190
vdc->realize = virtio_gpu_gl_device_realize;
191
vdc->unrealize = virtio_gpu_gl_device_unrealize;
192
vdc->reset = virtio_gpu_gl_reset;
hw/display/virtio-gpu-virgl.c
+101
-22
@@ -96,6 +96,10 @@ static void virtio_gpu_virgl_resume_cmdq_bh(void *opaque)
96
{
97
VirtIOGPU *g = opaque;
98
99
+ if (!virtio_gpu_virgl_update_render_state(g)) {
100
+ return;
101
+ }
102
+
103
virtio_gpu_process_cmdq(g);
104
}
105
@@ -344,14 +348,46 @@ static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
348
virgl_renderer_resource_create(&args, NULL, 0);
349
}
350
351
+static int
352
+virtio_gpu_virgl_resource_unref(VirtIOGPU *g,
353
+ struct virtio_gpu_virgl_resource *res,
354
+ bool *suspended)
355
+{
356
+ struct iovec *res_iovs = NULL;
357
+ int num_iovs = 0;
358
+#if VIRGL_VERSION_MAJOR >= 1
359
+ int ret;
360
+
361
+ ret = virtio_gpu_virgl_unmap_resource_blob(g, res, suspended);
362
+ if (ret) {
363
+ return ret;
364
+ }
365
+ if (*suspended) {
366
+ return 0;
367
+ }
368
+#endif
369
+
370
+ virgl_renderer_resource_detach_iov(res->base.resource_id,
371
+ &res_iovs,
372
+ &num_iovs);
373
+ if (res_iovs != NULL && num_iovs != 0) {
374
+ virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs);
375
+ }
376
+ virgl_renderer_resource_unref(res->base.resource_id);
377
+
378
+ QTAILQ_REMOVE(&g->reslist, &res->base, next);
379
+
380
+ g_free(res);
381
+
382
+ return 0;
383
+}
384
+
385
static void virgl_cmd_resource_unref(VirtIOGPU *g,
386
struct virtio_gpu_ctrl_command *cmd,
387
bool *cmd_suspended)
388
{
389
struct virtio_gpu_resource_unref unref;
390
struct virtio_gpu_virgl_resource *res;
353
- struct iovec *res_iovs = NULL;
354
- int num_iovs = 0;
391
392
VIRTIO_GPU_FILL_CMD(unref);
393
trace_virtio_gpu_cmd_res_unref(unref.resource_id);
@@ -364,27 +400,21 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g,
400
return;
401
}
402
367
-#if VIRGL_VERSION_MAJOR >= 1
368
- if (virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended)) {
369
- cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
370
- return;
371
- }
372
- if (*cmd_suspended) {
373
- return;
374
- }
375
-#endif
403
+ virtio_gpu_virgl_resource_unref(g, res, cmd_suspended);
404
+}
405
377
- virgl_renderer_resource_detach_iov(unref.resource_id,
378
- &res_iovs,
379
- &num_iovs);
380
- if (res_iovs != NULL && num_iovs != 0) {
381
- virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs);
382
- }
383
- virgl_renderer_resource_unref(unref.resource_id);
406
+void virtio_gpu_virgl_resource_destroy(VirtIOGPU *g,
407
+ struct virtio_gpu_simple_resource *base,
408
+ Error **errp)
409
+{
410
+ struct virtio_gpu_virgl_resource *res;
411
+ bool suspended = false;
412
385
- QTAILQ_REMOVE(&g->reslist, &res->base, next);
413
+ res = container_of(base, struct virtio_gpu_virgl_resource, base);
414
387
- g_free(res);
415
+ if (virtio_gpu_virgl_resource_unref(g, res, &suspended)) {
416
+ error_setg(errp, "failed to destroy virgl resource");
417
+ }
418
}
419
420
static void virgl_cmd_context_create(VirtIOGPU *g,
@@ -1291,6 +1321,10 @@ static void virtio_gpu_fence_poll(void *opaque)
1321
VirtIOGPU *g = opaque;
1322
VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1323
1324
+ if (!virtio_gpu_virgl_update_render_state(g)) {
1325
+ return;
1326
+ }
1327
+
1328
virgl_renderer_poll();
1329
virtio_gpu_process_cmdq(g);
1330
if (!QTAILQ_EMPTY(&g->cmdq) || !QTAILQ_EMPTY(&g->fenceq)) {
@@ -1313,14 +1347,30 @@ void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g)
1347
}
1348
}
1349
1316
-void virtio_gpu_virgl_reset(VirtIOGPU *g)
1350
+static bool virtio_gpu_virgl_reset(VirtIOGPU *g)
1351
{
1352
+ struct virtio_gpu_simple_resource *res, *tmp;
1353
+
1354
+ /*
1355
+ * Virgl blob resource unmapping can be suspended and
1356
+ * deferred on unref, ensure that destruction is completed.
1357
+ */
1358
+ QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
1359
+ virtio_gpu_virgl_resource_destroy(g, res, NULL);
1360
+ }
1361
+
1362
+ if (!QTAILQ_EMPTY(&g->reslist)) {
1363
+ return false;
1364
+ }
1365
+
1366
virgl_renderer_reset();
1367
1368
virtio_gpu_virgl_reset_async_fences(g);
1369
+
1370
+ return true;
1371
}
1372
1323
-int virtio_gpu_virgl_init(VirtIOGPU *g)
1373
+static int virtio_gpu_virgl_init(VirtIOGPU *g)
1374
{
1375
int ret;
1376
uint32_t flags = 0;
@@ -1398,6 +1448,35 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
1448
return 0;
1449
}
1450
1451
+bool virtio_gpu_virgl_update_render_state(VirtIOGPU *g)
1452
+{
1453
+ VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1454
+
1455
+ switch (gl->renderer_state) {
1456
+ case RS_RESET:
1457
+ virgl_renderer_force_ctx_0();
1458
+
1459
+ if (!virtio_gpu_virgl_reset(g)) {
1460
+ return false;
1461
+ }
1462
+ /* fallthrough */
1463
+ case RS_START:
1464
+ if (virtio_gpu_virgl_init(g)) {
1465
+ gl->renderer_state = RS_INIT_FAILED;
1466
+ return false;
1467
+ }
1468
+
1469
+ gl->renderer_state = RS_INITED;
1470
+ break;
1471
+ case RS_INIT_FAILED:
1472
+ return false;
1473
+ case RS_INITED:
1474
+ break;
1475
+ }
1476
+
1477
+ return true;
1478
+}
1479
+
1480
static void virtio_gpu_virgl_add_capset(GArray *capset_ids, uint32_t capset_id)
1481
{
1482
g_array_append_val(capset_ids, capset_id);
include/hw/virtio/virtio-gpu.h
+4
-2
@@ -390,9 +390,11 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
390
struct virtio_gpu_ctrl_command *cmd);
391
void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
392
void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g);
393
-void virtio_gpu_virgl_reset(VirtIOGPU *g);
394
-int virtio_gpu_virgl_init(VirtIOGPU *g);
393
GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g);
394
void virtio_gpu_virgl_reset_async_fences(VirtIOGPU *g);
395
+void virtio_gpu_virgl_resource_destroy(VirtIOGPU *g,
396
+ struct virtio_gpu_simple_resource *res,
397
+ Error **errp);
398
+bool virtio_gpu_virgl_update_render_state(VirtIOGPU *g);
399
400
#endif