@samitouri / QOSamiQemu / commits / 239b7c5705

vfio/pci: close display console during unrealize, not finalize

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 <clg@redhat.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260623-b4-ui-v4-5-4656aec3398d@redhat.com>

Marc-André Lureau committed Jun 23, 2026 at 11:44 UTC 239b7c5705a40bc051ab9bd9a9672cc1862a3e9e
4 files changed +21 -12
MAINTAINERS
+1
@@ -2347,6 +2347,7 @@ S: Supported
2347 F: hw/vfio/*
2348 F: util/vfio-helpers.c
2349 F: include/hw/vfio/
2350 +F: docs/devel/vfio-mdpy.rst
2351 F: docs/devel/migration/vfio.rst
2352 F: qapi/vfio.json
2353 F: migration/vfio-stub.c
hw/vfio/display.c
+17 -12
@@ -505,15 +505,6 @@ static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
505 return true;
506 }
507
508 -static void vfio_display_region_exit(VFIODisplay *dpy)
509 -{
510 - if (!dpy->region.buffer.size) {
511 - return;
512 - }
513 -
514 - vfio_region_exit(&dpy->region.buffer);
515 - vfio_region_finalize(&dpy->region.buffer);
516 -}
508
509 /* ---------------------------------------------------------------------- */
510
@@ -547,17 +538,31 @@ bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
538 return false;
539 }
540
550 -void vfio_display_finalize(VFIOPCIDevice *vdev)
541 +void vfio_display_exit(VFIOPCIDevice *vdev)
542 {
543 if (!vdev->dpy) {
544 return;
545 }
546
556 - qemu_graphic_console_close(vdev->dpy->con);
547 vfio_display_dmabuf_exit(vdev->dpy);
558 - vfio_display_region_exit(vdev->dpy);
548 + qemu_graphic_console_close(vdev->dpy->con);
549 + if (vdev->dpy->region.buffer.size) {
550 + vfio_region_exit(&vdev->dpy->region.buffer);
551 + }
552 +}
553 +
554 +void vfio_display_finalize(VFIOPCIDevice *vdev)
555 +{
556 + if (!vdev->dpy) {
557 + return;
558 + }
559 +
560 + if (vdev->dpy->region.buffer.size) {
561 + vfio_region_finalize(&vdev->dpy->region.buffer);
562 + }
563 vfio_display_edid_exit(vdev->dpy);
564 g_free(vdev->dpy);
565 + vdev->dpy = NULL;
566 }
567
568 static bool migrate_needed(void *opaque)
hw/vfio/pci.c
+2
@@ -3591,6 +3591,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp)
3591 return;
3592
3593 out_deregister:
3594 + vfio_display_exit(vdev);
3595 if (vdev->interrupt == VFIO_INT_INTx) {
3596 vfio_intx_disable(vdev);
3597 }
@@ -3624,6 +3625,7 @@ static void vfio_exitfn(PCIDevice *pdev)
3625 VFIOPCIDevice *vdev = VFIO_PCI_DEVICE(pdev);
3626 VFIODevice *vbasedev = &vdev->vbasedev;
3627
3628 + vfio_display_exit(vdev);
3629 vfio_unregister_req_notifier(vdev);
3630 vfio_unregister_err_notifier(vdev);
3631 pci_device_set_intx_routing_notifier(pdev, NULL);
hw/vfio/pci.h
+1
@@ -270,6 +270,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp);
270
271 void vfio_display_reset(VFIOPCIDevice *vdev);
272 bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
273 +void vfio_display_exit(VFIOPCIDevice *vdev);
274 void vfio_display_finalize(VFIOPCIDevice *vdev);
275
276 extern const VMStateDescription vfio_display_vmstate;