It is a common requirement of qdev to delete memory subregions to hide them from address spaces during unrealization. pci automatically deletes the IO subregions, but this process is manually implemented in other places, which is tedious and error-prone.
Let qdev enumerate all child memory regions, check if they have containers, and delete them if so. The automatic deletion happens only after the device-specific unrealization code. This allows that the device-specific can continue to assume that subregions they added are present until unrealization finishes. So existing memory_region_del_subregion() calls in device-specific code will not fail but will be no-op. Signed-off-by: Akihiko Odaki <[email protected]> --- MAINTAINERS | 1 + include/hw/qdev-core.h | 1 + hw/core/qdev.c | 14 ++++++++++++++ stubs/memory.c | 9 +++++++++ stubs/meson.build | 1 + 5 files changed, 26 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 84cfd85e1fa1..f2ef02858e30 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3241,6 +3241,7 @@ F: include/system/physmem.h F: include/system/ram_addr.h F: include/system/ramblock.h F: include/system/memory_mapping.h +F: stubs/memory.c F: system/dma-helpers.c F: system/ioport.c F: system/memory.c diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index a7bfb10dc70c..bd4d8049c435 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -526,6 +526,7 @@ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); * - unrealize any child buses by calling qbus_unrealize() * (this will recursively unrealize any devices on those buses) * - call the unrealize method of @dev + * - remove @dev from memory * * The device can then be freed by causing its reference count to go * to zero. diff --git a/hw/core/qdev.c b/hw/core/qdev.c index fab42a727059..622d4451e637 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -480,6 +480,17 @@ static bool check_only_migratable(Object *obj, Error **errp) return true; } +static int del_memory_region(Object *child, void *opaque) +{ + MemoryRegion *mr = (MemoryRegion *)object_dynamic_cast(child, TYPE_MEMORY_REGION); + + if (mr && mr->container) { + memory_region_del_subregion(mr->container, mr); + } + + return 0; +} + static void device_set_realized(Object *obj, bool value, Error **errp) { DeviceState *dev = DEVICE(obj); @@ -611,6 +622,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) if (dc->unrealize) { dc->unrealize(dev); } + object_child_foreach(OBJECT(dev), del_memory_region, NULL); dev->pending_deleted_event = true; DEVICE_LISTENER_CALL(unrealize, Reverse, dev); } @@ -635,6 +647,8 @@ post_realize_fail: } fail: + object_child_foreach(OBJECT(dev), del_memory_region, NULL); + error_propagate(errp, local_err); if (unattached_parent) { /* diff --git a/stubs/memory.c b/stubs/memory.c new file mode 100644 index 000000000000..9c36531ae542 --- /dev/null +++ b/stubs/memory.c @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "system/memory.h" + +void memory_region_del_subregion(MemoryRegion *mr, + MemoryRegion *subregion) +{ +} diff --git a/stubs/meson.build b/stubs/meson.build index 5d577467bfdd..d3af15963ae1 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -96,5 +96,6 @@ if have_system or have_user # Also included in have_system for tests/unit/test-qdev-global-props stub_ss.add(files('hotplug-stubs.c')) + stub_ss.add(files('memory.c')) stub_ss.add(files('sysbus.c')) endif -- 2.51.0
