From 239b7c5705a40bc051ab9bd9a9672cc1862a3e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 23 Jun 2026 11:44:20 +0400 Subject: [PATCH] vfio/pci: close display console during unrealize, not finalize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QemuGraphicConsole holds a strong QOM link back to the device via its "device" property (OBJ_PROP_LINK_STRONG). When graphic_console_close() is only called from vfio_display_finalize() during object finalize, this creates a ref-cycle deadlock: the device can't reach refcount 0 because the console holds a strong ref, but the console's ref is only dropped by graphic_console_close() which runs inside finalize. Split the display teardown into two phases: - vfio_display_exit(): called during unrealize (vfio_exitfn), closes the graphic console to break the ref cycle, and removes display region subregions while the parent memory regions are still alive. - vfio_display_finalize(): remains in finalize (vfio_pci_put_device), frees display region memory, dmabuf, and edid resources. The region memory contains QOM child objects (MemoryRegions) that must stay alive until QOM finalization has processed them. Acked-by: Cédric Le Goater Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Message-ID: <20260623-b4-ui-v4-5-4656aec3398d@redhat.com> --- MAINTAINERS | 1 + hw/vfio/display.c | 29 +++++++++++++++++------------ hw/vfio/pci.c | 2 ++ hw/vfio/pci.h | 1 + 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 93df53d87f..35f87b7c9a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2347,6 +2347,7 @@ S: Supported F: hw/vfio/* F: util/vfio-helpers.c F: include/hw/vfio/ +F: docs/devel/vfio-mdpy.rst F: docs/devel/migration/vfio.rst F: qapi/vfio.json F: migration/vfio-stub.c diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 8f91e83da8..cb83d98e9a 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -505,15 +505,6 @@ static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) return true; } -static void vfio_display_region_exit(VFIODisplay *dpy) -{ - if (!dpy->region.buffer.size) { - return; - } - - vfio_region_exit(&dpy->region.buffer); - vfio_region_finalize(&dpy->region.buffer); -} /* ---------------------------------------------------------------------- */ @@ -547,17 +538,31 @@ bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) return false; } +void vfio_display_exit(VFIOPCIDevice *vdev) +{ + if (!vdev->dpy) { + return; + } + + vfio_display_dmabuf_exit(vdev->dpy); + qemu_graphic_console_close(vdev->dpy->con); + if (vdev->dpy->region.buffer.size) { + vfio_region_exit(&vdev->dpy->region.buffer); + } +} + void vfio_display_finalize(VFIOPCIDevice *vdev) { if (!vdev->dpy) { return; } - qemu_graphic_console_close(vdev->dpy->con); - vfio_display_dmabuf_exit(vdev->dpy); - vfio_display_region_exit(vdev->dpy); + if (vdev->dpy->region.buffer.size) { + vfio_region_finalize(&vdev->dpy->region.buffer); + } vfio_display_edid_exit(vdev->dpy); g_free(vdev->dpy); + vdev->dpy = NULL; } static bool migrate_needed(void *opaque) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9c06b25e63..7d57eb4f47 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3591,6 +3591,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) return; out_deregister: + vfio_display_exit(vdev); if (vdev->interrupt == VFIO_INT_INTx) { vfio_intx_disable(vdev); } @@ -3624,6 +3625,7 @@ static void vfio_exitfn(PCIDevice *pdev) VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; + vfio_display_exit(vdev); vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); pci_device_set_intx_routing_notifier(pdev, NULL); diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index c3a1f53d35..cf56711587 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -270,6 +270,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); void vfio_display_reset(VFIOPCIDevice *vdev); bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); +void vfio_display_exit(VFIOPCIDevice *vdev); void vfio_display_finalize(VFIOPCIDevice *vdev); extern const VMStateDescription vfio_display_vmstate;