virtio-gpu: reset gfx resources in main thread

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.

Acked-by: Dongwon Kim <dongwon.kim@intel.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20230726173929.690601-3-marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2023-07-26 21:39:29 +04:00
parent 957d77863e
commit a41e2d97f9
2 changed files with 35 additions and 8 deletions

View file

@ -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)
&qdev->mem_reentrancy_guard);
g->cursor_bh = qemu_bh_new_guarded(virtio_gpu_cursor_bh, g,
&qdev->mem_reentrancy_guard);
g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g);
qemu_cond_init(&g->reset_cond);
QTAILQ_INIT(&g->reslist);
QTAILQ_INIT(&g->cmdq);
QTAILQ_INIT(&g->fenceq);
@ -1398,18 +1402,42 @@ static void virtio_gpu_device_unrealize(DeviceState *qdev)
g_clear_pointer(&g->ctrl_bh, qemu_bh_delete);
g_clear_pointer(&g->cursor_bh, qemu_bh_delete);
g_clear_pointer(&g->reset_bh, qemu_bh_delete);
qemu_cond_destroy(&g->reset_cond);
virtio_gpu_base_device_unrealize(qdev);
}
static void virtio_gpu_reset_bh(void *opaque)
{
VirtIOGPU *g = VIRTIO_GPU(opaque);
struct virtio_gpu_simple_resource *res, *tmp;
int i = 0;
QTAILQ_FOREACH_SAFE(res, &g->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(&g->reset_cond);
}
void virtio_gpu_reset(VirtIODevice *vdev)
{
VirtIOGPU *g = VIRTIO_GPU(vdev);
struct virtio_gpu_simple_resource *res, *tmp;
struct virtio_gpu_ctrl_command *cmd;
int i = 0;
QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
virtio_gpu_resource_destroy(g, res);
if (qemu_in_vcpu_thread()) {
g->reset_finished = false;
qemu_bh_schedule(g->reset_bh);
while (!g->reset_finished) {
qemu_cond_wait_iothread(&g->reset_cond);
}
} else {
virtio_gpu_reset_bh(g);
}
while (!QTAILQ_EMPTY(&g->cmdq)) {
@ -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));
}

View file

@ -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;