aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/ppc/spapr_pci.c6
-rw-r--r--include/exec/memory.h10
-rw-r--r--memory.c7
3 files changed, 23 insertions, 0 deletions
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 5591723bb2..9003fe9010 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1788,6 +1788,12 @@ static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
memory_region_del_subregion(&sphb->iommu_root, &sphb->msiwindow);
+ /*
+ * An attached PCI device may have memory listeners, eg. VFIO PCI. We have
+ * unmapped all sections. Remove the listeners now, before destroying the
+ * address space.
+ */
+ address_space_remove_listeners(&sphb->iommu_as);
address_space_destroy(&sphb->iommu_as);
qbus_set_hotplug_handler(BUS(phb->bus), NULL, &error_abort);
diff --git a/include/exec/memory.h b/include/exec/memory.h
index e6140e8a04..2c5cdffa31 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -1758,6 +1758,16 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name);
void address_space_destroy(AddressSpace *as);
/**
+ * address_space_remove_listeners: unregister all listeners of an address space
+ *
+ * Removes all callbacks previously registered with memory_listener_register()
+ * for @as.
+ *
+ * @as: an initialized #AddressSpace
+ */
+void address_space_remove_listeners(AddressSpace *as);
+
+/**
* address_space_rw: read from or write to an address space.
*
* Return a MemTxResult indicating whether the operation succeeded
diff --git a/memory.c b/memory.c
index 0a089a73ae..480f3d989b 100644
--- a/memory.c
+++ b/memory.c
@@ -2723,6 +2723,13 @@ void memory_listener_unregister(MemoryListener *listener)
listener->address_space = NULL;
}
+void address_space_remove_listeners(AddressSpace *as)
+{
+ while (!QTAILQ_EMPTY(&as->listeners)) {
+ memory_listener_unregister(QTAILQ_FIRST(&as->listeners));
+ }
+}
+
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
{
memory_region_ref(root);