Re: [PATCH 2/2] virtio-gpu: reset gfx resources in main thread
Hi On Thu, Aug 3, 2023 at 10:23 PM Kim, Dongwon wrote: > > Looking good. By the way, what does 'BH' stand for? > BH: bottom-half, it's a kind of delayed callback. > Acked-by: Dongwon Kim thanks > > From: Marc-André Lureau > > Calling OpenGL from different threads can have bad consequences if not > carefully reviewed. It's not generally supported. In my case, I was > debugging a crash in glDeleteTextures from OPENGL32.DLL, where I asked > qemu for gl=es, and thus ANGLE implementation was expected. libepoxy did > resolution of the global pointer for glGenTexture to the GLES version > from the main thread. But it resolved glDeleteTextures to the GL > version, because it was done from a different thread without correct > context. Oops. > > Let's stick to the main thread for GL calls by using a BH. > > Note: I didn't use atomics for reset_finished check, assuming the BQL > will provide enough of sync, but I might be wrong. > > Signed-off-by: Marc-André Lureau > --- > include/hw/virtio/virtio-gpu.h | 3 +++ > hw/display/virtio-gpu.c| 38 +++--- > 2 files changed, 34 insertions(+), 7 deletions(-) > > diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h > index 05bee09e1a..390c4642b8 100644 > --- a/include/hw/virtio/virtio-gpu.h > +++ b/include/hw/virtio/virtio-gpu.h > @@ -169,6 +169,9 @@ struct VirtIOGPU { > > QEMUBH *ctrl_bh; > QEMUBH *cursor_bh; > +QEMUBH *reset_bh; > +QemuCond reset_cond; > +bool reset_finished; > > QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; > QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; > diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c > index b1f5d392bb..bbd5c6561a 100644 > --- a/hw/display/virtio-gpu.c > +++ b/hw/display/virtio-gpu.c > @@ -14,6 +14,7 @@ > #include "qemu/osdep.h" > #include "qemu/units.h" > #include "qemu/iov.h" > +#include "sysemu/cpus.h" > #include "ui/console.h" > #include "trace.h" > #include "sysemu/dma.h" > @@ -41,6 +42,7 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t > resource_id, > > static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, > struct virtio_gpu_simple_resource > *res); > +static void virtio_gpu_reset_bh(void *opaque); > > void virtio_gpu_update_cursor_data(VirtIOGPU *g, > struct virtio_gpu_scanout *s, > @@ -1387,6 +1389,8 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error > **errp) >>mem_reentrancy_guard); > g->cursor_bh = qemu_bh_new_guarded(virtio_gpu_cursor_bh, g, > >mem_reentrancy_guard); > +g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g); > +qemu_cond_init(>reset_cond); > QTAILQ_INIT(>reslist); > QTAILQ_INIT(>cmdq); > QTAILQ_INIT(>fenceq); > @@ -1398,20 +1402,44 @@ static void virtio_gpu_device_unrealize(DeviceState > *qdev) > > g_clear_pointer(>ctrl_bh, qemu_bh_delete); > g_clear_pointer(>cursor_bh, qemu_bh_delete); > +g_clear_pointer(>reset_bh, qemu_bh_delete); > +qemu_cond_destroy(>reset_cond); > virtio_gpu_base_device_unrealize(qdev); > } > > -void virtio_gpu_reset(VirtIODevice *vdev) > +static void virtio_gpu_reset_bh(void *opaque) > { > -VirtIOGPU *g = VIRTIO_GPU(vdev); > +VirtIOGPU *g = VIRTIO_GPU(opaque); > struct virtio_gpu_simple_resource *res, *tmp; > -struct virtio_gpu_ctrl_command *cmd; > int i = 0; > > QTAILQ_FOREACH_SAFE(res, >reslist, next, tmp) { > virtio_gpu_resource_destroy(g, res); > } > > +for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { > +dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); > +} > + > +g->reset_finished = true; > +qemu_cond_signal(>reset_cond); > +} > + > +void virtio_gpu_reset(VirtIODevice *vdev) > +{ > +VirtIOGPU *g = VIRTIO_GPU(vdev); > +struct virtio_gpu_ctrl_command *cmd; > + > +if (qemu_in_vcpu_thread()) { > +g->reset_finished = false; > +qemu_bh_schedule(g->reset_bh); > +while (!g->reset_finished) { > +qemu_cond_wait_iothread(>reset_cond); > +} > +} else { > +virtio_gpu_reset_bh(g); > +} > + > while (!QTAILQ_EMPTY(>cmdq)) { > cmd = QTAILQ_FIRST(>cmdq); > QTAILQ_REMOVE(>cmdq, cmd, next); > @@ -1425,10 +1453,6 @@ void virtio_gpu_reset(VirtIODevice *vdev) > g_free(cmd); > } > > -for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { > -dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); > -} > - > virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); > } > > -- > 2.41.0 > >
Re: [PATCH 2/2] virtio-gpu: reset gfx resources in main thread
Looking good. By the way, what does 'BH' stand for? Acked-by: Dongwon Kim From: Marc-André Lureau Calling OpenGL from different threads can have bad consequences if not carefully reviewed. It's not generally supported. In my case, I was debugging a crash in glDeleteTextures from OPENGL32.DLL, where I asked qemu for gl=es, and thus ANGLE implementation was expected. libepoxy did resolution of the global pointer for glGenTexture to the GLES version from the main thread. But it resolved glDeleteTextures to the GL version, because it was done from a different thread without correct context. Oops. Let's stick to the main thread for GL calls by using a BH. Note: I didn't use atomics for reset_finished check, assuming the BQL will provide enough of sync, but I might be wrong. Signed-off-by: Marc-André Lureau --- include/hw/virtio/virtio-gpu.h | 3 +++ hw/display/virtio-gpu.c| 38 +++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 05bee09e1a..390c4642b8 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -169,6 +169,9 @@ struct VirtIOGPU { QEMUBH *ctrl_bh; QEMUBH *cursor_bh; +QEMUBH *reset_bh; +QemuCond reset_cond; +bool reset_finished; QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index b1f5d392bb..bbd5c6561a 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/iov.h" +#include "sysemu/cpus.h" #include "ui/console.h" #include "trace.h" #include "sysemu/dma.h" @@ -41,6 +42,7 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, struct virtio_gpu_simple_resource *res); +static void virtio_gpu_reset_bh(void *opaque); void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, @@ -1387,6 +1389,8 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) >mem_reentrancy_guard); g->cursor_bh = qemu_bh_new_guarded(virtio_gpu_cursor_bh, g, >mem_reentrancy_guard); +g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g); +qemu_cond_init(>reset_cond); QTAILQ_INIT(>reslist); QTAILQ_INIT(>cmdq); QTAILQ_INIT(>fenceq); @@ -1398,20 +1402,44 @@ static void virtio_gpu_device_unrealize(DeviceState *qdev) g_clear_pointer(>ctrl_bh, qemu_bh_delete); g_clear_pointer(>cursor_bh, qemu_bh_delete); +g_clear_pointer(>reset_bh, qemu_bh_delete); +qemu_cond_destroy(>reset_cond); virtio_gpu_base_device_unrealize(qdev); } -void virtio_gpu_reset(VirtIODevice *vdev) +static void virtio_gpu_reset_bh(void *opaque) { -VirtIOGPU *g = VIRTIO_GPU(vdev); +VirtIOGPU *g = VIRTIO_GPU(opaque); struct virtio_gpu_simple_resource *res, *tmp; -struct virtio_gpu_ctrl_command *cmd; int i = 0; QTAILQ_FOREACH_SAFE(res, >reslist, next, tmp) { virtio_gpu_resource_destroy(g, res); } +for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { +dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); +} + +g->reset_finished = true; +qemu_cond_signal(>reset_cond); +} + +void virtio_gpu_reset(VirtIODevice *vdev) +{ +VirtIOGPU *g = VIRTIO_GPU(vdev); +struct virtio_gpu_ctrl_command *cmd; + +if (qemu_in_vcpu_thread()) { +g->reset_finished = false; +qemu_bh_schedule(g->reset_bh); +while (!g->reset_finished) { +qemu_cond_wait_iothread(>reset_cond); +} +} else { +virtio_gpu_reset_bh(g); +} + while (!QTAILQ_EMPTY(>cmdq)) { cmd = QTAILQ_FIRST(>cmdq); QTAILQ_REMOVE(>cmdq, cmd, next); @@ -1425,10 +1453,6 @@ void virtio_gpu_reset(VirtIODevice *vdev) g_free(cmd); } -for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { -dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); -} - virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); } -- 2.41.0
[PATCH 2/2] virtio-gpu: reset gfx resources in main thread
From: Marc-André Lureau Calling OpenGL from different threads can have bad consequences if not carefully reviewed. It's not generally supported. In my case, I was debugging a crash in glDeleteTextures from OPENGL32.DLL, where I asked qemu for gl=es, and thus ANGLE implementation was expected. libepoxy did resolution of the global pointer for glGenTexture to the GLES version from the main thread. But it resolved glDeleteTextures to the GL version, because it was done from a different thread without correct context. Oops. Let's stick to the main thread for GL calls by using a BH. Note: I didn't use atomics for reset_finished check, assuming the BQL will provide enough of sync, but I might be wrong. Signed-off-by: Marc-André Lureau --- include/hw/virtio/virtio-gpu.h | 3 +++ hw/display/virtio-gpu.c| 38 +++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 05bee09e1a..390c4642b8 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -169,6 +169,9 @@ struct VirtIOGPU { QEMUBH *ctrl_bh; QEMUBH *cursor_bh; +QEMUBH *reset_bh; +QemuCond reset_cond; +bool reset_finished; QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index b1f5d392bb..bbd5c6561a 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/iov.h" +#include "sysemu/cpus.h" #include "ui/console.h" #include "trace.h" #include "sysemu/dma.h" @@ -41,6 +42,7 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, struct virtio_gpu_simple_resource *res); +static void virtio_gpu_reset_bh(void *opaque); void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, @@ -1387,6 +1389,8 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) >mem_reentrancy_guard); g->cursor_bh = qemu_bh_new_guarded(virtio_gpu_cursor_bh, g, >mem_reentrancy_guard); +g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g); +qemu_cond_init(>reset_cond); QTAILQ_INIT(>reslist); QTAILQ_INIT(>cmdq); QTAILQ_INIT(>fenceq); @@ -1398,20 +1402,44 @@ static void virtio_gpu_device_unrealize(DeviceState *qdev) g_clear_pointer(>ctrl_bh, qemu_bh_delete); g_clear_pointer(>cursor_bh, qemu_bh_delete); +g_clear_pointer(>reset_bh, qemu_bh_delete); +qemu_cond_destroy(>reset_cond); virtio_gpu_base_device_unrealize(qdev); } -void virtio_gpu_reset(VirtIODevice *vdev) +static void virtio_gpu_reset_bh(void *opaque) { -VirtIOGPU *g = VIRTIO_GPU(vdev); +VirtIOGPU *g = VIRTIO_GPU(opaque); struct virtio_gpu_simple_resource *res, *tmp; -struct virtio_gpu_ctrl_command *cmd; int i = 0; QTAILQ_FOREACH_SAFE(res, >reslist, next, tmp) { virtio_gpu_resource_destroy(g, res); } +for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { +dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); +} + +g->reset_finished = true; +qemu_cond_signal(>reset_cond); +} + +void virtio_gpu_reset(VirtIODevice *vdev) +{ +VirtIOGPU *g = VIRTIO_GPU(vdev); +struct virtio_gpu_ctrl_command *cmd; + +if (qemu_in_vcpu_thread()) { +g->reset_finished = false; +qemu_bh_schedule(g->reset_bh); +while (!g->reset_finished) { +qemu_cond_wait_iothread(>reset_cond); +} +} else { +virtio_gpu_reset_bh(g); +} + while (!QTAILQ_EMPTY(>cmdq)) { cmd = QTAILQ_FIRST(>cmdq); QTAILQ_REMOVE(>cmdq, cmd, next); @@ -1425,10 +1453,6 @@ void virtio_gpu_reset(VirtIODevice *vdev) g_free(cmd); } -for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { -dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); -} - virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); } -- 2.41.0