@samitouri / QOSamiQemu / commits / 6698eae8a1

virtio-gpu: Support asynchronous fencing

Support asynchronous fencing feature of virglrenderer. It allows Qemu to handle fence as soon as it's signalled instead of periodically polling the fence status. This feature is required for enabling DRM context support in Qemu because legacy fencing mode isn't supported for DRM contexts in virglrenderer. Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Acked-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Reviewed-by: Yiwei Zhang <zzyiwei@gmail.com> Tested-by: Yiwei Zhang <zzyiwei@gmail.com> Signed-off-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Message-ID: <20260303151422.977399-10-dmitry.osipenko@collabora.com> Message-ID: <20260304165043.1437519-12-alex.bennee@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

Dmitry Osipenko committed Mar 4, 2026 at 16:50 UTC 6698eae8a1f6d897ea0ad62e47448c296eec0bf9
4 files changed +145
hw/display/virtio-gpu-gl.c
+5
@@ -169,6 +169,11 @@ static void virtio_gpu_gl_device_unrealize(DeviceState *qdev)
169 if (gl->renderer_state >= RS_INITED) {
170 #if VIRGL_VERSION_MAJOR >= 1
171 qemu_bh_delete(gl->cmdq_resume_bh);
172 +
173 + if (gl->async_fence_bh) {
174 + virtio_gpu_virgl_reset_async_fences(g);
175 + qemu_bh_delete(gl->async_fence_bh);
176 + }
177 #endif
178 if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
179 timer_free(gl->print_stats);
hw/display/virtio-gpu-virgl.c
+127
@@ -24,6 +24,23 @@
24
25 #include <virglrenderer.h>
26
27 +/*
28 + * VIRGL_CHECK_VERSION available since libvirglrenderer 1.0.1 and was fixed
29 + * in 1.1.0. Undefine bugged version of the macro and provide our own.
30 + */
31 +#if defined(VIRGL_CHECK_VERSION) && \
32 + VIRGL_VERSION_MAJOR == 1 && VIRGL_VERSION_MINOR < 1
33 +#undef VIRGL_CHECK_VERSION
34 +#endif
35 +
36 +#ifndef VIRGL_CHECK_VERSION
37 +#define VIRGL_CHECK_VERSION(major, minor, micro) \
38 + (VIRGL_VERSION_MAJOR > (major) || \
39 + VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR > (minor) || \
40 + VIRGL_VERSION_MAJOR == (major) && VIRGL_VERSION_MINOR == (minor) && \
41 + VIRGL_VERSION_MICRO >= (micro))
42 +#endif
43 +
44 struct virtio_gpu_virgl_resource {
45 struct virtio_gpu_simple_resource base;
46 MemoryRegion *mr;
@@ -1079,6 +1096,103 @@ static void virgl_write_context_fence(void *opaque, uint32_t ctx_id,
1096 }
1097 #endif
1098
1099 +void virtio_gpu_virgl_reset_async_fences(VirtIOGPU *g)
1100 +{
1101 + struct virtio_gpu_virgl_context_fence *f;
1102 + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1103 +
1104 + while (!QSLIST_EMPTY(&gl->async_fenceq)) {
1105 + f = QSLIST_FIRST(&gl->async_fenceq);
1106 +
1107 + QSLIST_REMOVE_HEAD(&gl->async_fenceq, next);
1108 +
1109 + g_free(f);
1110 + }
1111 +}
1112 +
1113 +#if VIRGL_CHECK_VERSION(1, 1, 2)
1114 +static void virtio_gpu_virgl_async_fence_bh(void *opaque)
1115 +{
1116 + QSLIST_HEAD(, virtio_gpu_virgl_context_fence) async_fenceq;
1117 + struct virtio_gpu_ctrl_command *cmd, *tmp;
1118 + struct virtio_gpu_virgl_context_fence *f;
1119 + VirtIOGPU *g = opaque;
1120 + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1121 +
1122 + if (gl->renderer_state != RS_INITED) {
1123 + return;
1124 + }
1125 +
1126 + QSLIST_MOVE_ATOMIC(&async_fenceq, &gl->async_fenceq);
1127 +
1128 + while (!QSLIST_EMPTY(&async_fenceq)) {
1129 + f = QSLIST_FIRST(&async_fenceq);
1130 +
1131 + QSLIST_REMOVE_HEAD(&async_fenceq, next);
1132 +
1133 + QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
1134 + /*
1135 + * the guest can end up emitting fences out of order
1136 + * so we should check all fenced cmds not just the first one.
1137 + */
1138 + if (cmd->cmd_hdr.fence_id > f->fence_id) {
1139 + continue;
1140 + }
1141 + if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX) {
1142 + if (cmd->cmd_hdr.ring_idx != f->ring_idx) {
1143 + continue;
1144 + }
1145 + if (cmd->cmd_hdr.ctx_id != f->ctx_id) {
1146 + continue;
1147 + }
1148 + }
1149 + virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
1150 + QTAILQ_REMOVE(&g->fenceq, cmd, next);
1151 + g_free(cmd);
1152 + }
1153 +
1154 + trace_virtio_gpu_fence_resp(f->fence_id);
1155 + g_free(f);
1156 + g->inflight--;
1157 + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
1158 + trace_virtio_gpu_dec_inflight_fences(g->inflight);
1159 + }
1160 + }
1161 +}
1162 +
1163 +static void
1164 +virtio_gpu_virgl_push_async_fence(VirtIOGPU *g, uint32_t ctx_id,
1165 + uint32_t ring_idx, uint64_t fence_id)
1166 +{
1167 + struct virtio_gpu_virgl_context_fence *f;
1168 + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
1169 +
1170 + f = g_new(struct virtio_gpu_virgl_context_fence, 1);
1171 + f->ctx_id = ctx_id;
1172 + f->ring_idx = ring_idx;
1173 + f->fence_id = fence_id;
1174 +
1175 + QSLIST_INSERT_HEAD_ATOMIC(&gl->async_fenceq, f, next);
1176 +
1177 + qemu_bh_schedule(gl->async_fence_bh);
1178 +}
1179 +
1180 +static void virgl_write_async_fence(void *opaque, uint32_t fence)
1181 +{
1182 + VirtIOGPU *g = opaque;
1183 +
1184 + virtio_gpu_virgl_push_async_fence(g, 0, UINT32_MAX, fence);
1185 +}
1186 +
1187 +static void virgl_write_async_context_fence(void *opaque, uint32_t ctx_id,
1188 + uint32_t ring_idx, uint64_t fence)
1189 +{
1190 + VirtIOGPU *g = opaque;
1191 +
1192 + virtio_gpu_virgl_push_async_fence(g, ctx_id, ring_idx, fence);
1193 +}
1194 +#endif
1195 +
1196 static virgl_renderer_gl_context
1197 virgl_create_context(void *opaque, int scanout_idx,
1198 struct virgl_renderer_gl_ctx_param *params)
@@ -1178,6 +1292,8 @@ void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g)
1292 void virtio_gpu_virgl_reset(VirtIOGPU *g)
1293 {
1294 virgl_renderer_reset();
1295 +
1296 + virtio_gpu_virgl_reset_async_fences(g);
1297 }
1298
1299 int virtio_gpu_virgl_init(VirtIOGPU *g)
@@ -1190,6 +1306,12 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
1306 if (qemu_egl_display) {
1307 virtio_gpu_3d_cbs.version = 4;
1308 virtio_gpu_3d_cbs.get_egl_display = virgl_get_egl_display;
1309 +#if VIRGL_CHECK_VERSION(1, 1, 2)
1310 + virtio_gpu_3d_cbs.write_fence = virgl_write_async_fence;
1311 + virtio_gpu_3d_cbs.write_context_fence = virgl_write_async_context_fence;
1312 + flags |= VIRGL_RENDERER_ASYNC_FENCE_CB;
1313 + flags |= VIRGL_RENDERER_THREAD_SYNC;
1314 +#endif
1315 }
1316 #endif
1317 #ifdef VIRGL_RENDERER_D3D11_SHARE_TEXTURE
@@ -1223,6 +1345,11 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
1345 gl->cmdq_resume_bh = virtio_bh_io_new_guarded(DEVICE(g),
1346 virtio_gpu_virgl_resume_cmdq_bh,
1347 g);
1348 +#if VIRGL_CHECK_VERSION(1, 1, 2)
1349 + gl->async_fence_bh = virtio_bh_io_new_guarded(DEVICE(g),
1350 + virtio_gpu_virgl_async_fence_bh,
1351 + g);
1352 +#endif
1353 #endif
1354
1355 return 0;
include/hw/virtio/virtio-gpu.h
+11
@@ -233,6 +233,13 @@ struct VirtIOGPUClass {
233 Error **errp);
234 };
235
236 +struct virtio_gpu_virgl_context_fence {
237 + uint32_t ctx_id;
238 + uint32_t ring_idx;
239 + uint64_t fence_id;
240 + QSLIST_ENTRY(virtio_gpu_virgl_context_fence) next;
241 +};
242 +
243 /* VirtIOGPUGL renderer states */
244 typedef enum {
245 RS_START, /* starting state */
@@ -250,6 +257,9 @@ struct VirtIOGPUGL {
257 QEMUTimer *print_stats;
258
259 QEMUBH *cmdq_resume_bh;
260 +
261 + QEMUBH *async_fence_bh;
262 + QSLIST_HEAD(, virtio_gpu_virgl_context_fence) async_fenceq;
263 };
264
265 struct VhostUserGPU {
@@ -380,5 +390,6 @@ void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g);
390 void virtio_gpu_virgl_reset(VirtIOGPU *g);
391 int virtio_gpu_virgl_init(VirtIOGPU *g);
392 GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g);
393 +void virtio_gpu_virgl_reset_async_fences(VirtIOGPU *g);
394
395 #endif
meson.build
+2
@@ -2524,6 +2524,8 @@ config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
2524 config_host_data.set('CONFIG_VNC_SASL', sasl.found())
2525 if virgl.found()
2526 config_host_data.set('VIRGL_VERSION_MAJOR', virgl.version().split('.')[0])
2527 + config_host_data.set('VIRGL_VERSION_MINOR', virgl.version().split('.')[1])
2528 + config_host_data.set('VIRGL_VERSION_MICRO', virgl.version().split('.')[2])
2529 endif
2530 config_host_data.set('CONFIG_VIRTFS', have_virtfs)
2531 config_host_data.set('CONFIG_VTE', vte.found())