@samitouri / QOSamiQemu / commits / bd9258917f

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