@samitouri / QOSamiQemu / commits / 43d1320c6c

system/memory: split RamDiscardManager into source and manager

Refactor the RamDiscardManager interface into two distinct components: - RamDiscardSource: An interface that state providers (virtio-mem, RamBlockAttributes) implement to provide discard state information (granularity, populated/discarded ranges, replay callbacks). - RamDiscardManager: A concrete QOM object that wraps a source, owns the listener list, and handles listener registration/unregistration and notifications. This separation moves the listener management logic from individual source implementations into the central RamDiscardManager, reducing code duplication between virtio-mem and RamBlockAttributes. The change prepares for future work where a RamDiscardManager could aggregate multiple sources. Note, the original virtio-mem code had conditions before discard: if (vmem->size) { rdl->notify_discard(rdl, rdl->section); } however, the new code calls discard unconditionally. This is considered safe, since the populate/discard of sections are already asymmetrical (unplug & unregister all listener section unconditionally). Reviewed-by: Peter Xu <peterx@redhat.com> Acked-by: David Hildenbrand <david@kernel.org> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Link: https://lore.kernel.org/r/20260604-rdm5-v5-1-5768e6a0943d@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com>

Marc-André Lureau committed Jun 4, 2026 at 17:43 UTC 43d1320c6ca812a16078132634cad571923e9180
6 files changed +385 -369
hw/virtio/virtio-mem.c
+32 -131
@@ -16,6 +16,7 @@
16 #include "qemu/error-report.h"
17 #include "qemu/units.h"
18 #include "qemu/target-info-qapi.h"
19 +#include "system/memory.h"
20 #include "system/numa.h"
21 #include "system/system.h"
22 #include "system/ramblock.h"
@@ -324,74 +325,31 @@ static int virtio_mem_for_each_unplugged_section(const VirtIOMEM *vmem,
325 return ret;
326 }
327
327 -static int virtio_mem_notify_populate_cb(MemoryRegionSection *s, void *arg)
328 -{
329 - RamDiscardListener *rdl = arg;
330 -
331 - return rdl->notify_populate(rdl, s);
332 -}
333 -
328 static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset,
329 uint64_t size)
330 {
337 - RamDiscardListener *rdl;
331 + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(&vmem->memdev->mr);
332
339 - QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
340 - MemoryRegionSection tmp = *rdl->section;
341 -
342 - if (!memory_region_section_intersect_range(&tmp, offset, size)) {
343 - continue;
344 - }
345 - rdl->notify_discard(rdl, &tmp);
346 - }
333 + ram_discard_manager_notify_discard(rdm, offset, size);
334 }
335
336 static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset,
337 uint64_t size)
338 {
352 - RamDiscardListener *rdl, *rdl2;
353 - int ret = 0;
354 -
355 - QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
356 - MemoryRegionSection tmp = *rdl->section;
339 + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(&vmem->memdev->mr);
340
358 - if (!memory_region_section_intersect_range(&tmp, offset, size)) {
359 - continue;
360 - }
361 - ret = rdl->notify_populate(rdl, &tmp);
362 - if (ret) {
363 - break;
364 - }
365 - }
366 -
367 - if (ret) {
368 - /* Notify all already-notified listeners. */
369 - QLIST_FOREACH(rdl2, &vmem->rdl_list, next) {
370 - MemoryRegionSection tmp = *rdl2->section;
371 -
372 - if (rdl2 == rdl) {
373 - break;
374 - }
375 - if (!memory_region_section_intersect_range(&tmp, offset, size)) {
376 - continue;
377 - }
378 - rdl2->notify_discard(rdl2, &tmp);
379 - }
380 - }
381 - return ret;
341 + return ram_discard_manager_notify_populate(rdm, offset, size);
342 }
343
344 static void virtio_mem_notify_unplug_all(VirtIOMEM *vmem)
345 {
386 - RamDiscardListener *rdl;
346 + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(&vmem->memdev->mr);
347
348 if (!vmem->size) {
349 return;
350 }
351
392 - QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
393 - rdl->notify_discard(rdl, rdl->section);
394 - }
352 + ram_discard_manager_notify_discard_all(rdm);
353 }
354
355 static bool virtio_mem_is_range_plugged(const VirtIOMEM *vmem,
@@ -1037,13 +995,9 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
995 return;
996 }
997
1040 - /*
1041 - * Set ourselves as RamDiscardManager before the plug handler maps the
1042 - * memory region and exposes it via an address space.
1043 - */
1044 - if (memory_region_set_ram_discard_manager(&vmem->memdev->mr,
1045 - RAM_DISCARD_MANAGER(vmem))) {
1046 - error_setg(errp, "Failed to set RamDiscardManager");
998 + if (memory_region_add_ram_discard_source(&vmem->memdev->mr,
999 + RAM_DISCARD_SOURCE(vmem))) {
1000 + error_setg(errp, "Failed to add RAM discard source");
1001 ram_block_coordinated_discard_require(false);
1002 return;
1003 }
@@ -1062,7 +1016,8 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
1016 ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb));
1017 if (ret) {
1018 error_setg_errno(errp, -ret, "Unexpected error discarding RAM");
1065 - memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
1019 + memory_region_del_ram_discard_source(&vmem->memdev->mr,
1020 + RAM_DISCARD_SOURCE(vmem));
1021 ram_block_coordinated_discard_require(false);
1022 return;
1023 }
@@ -1147,7 +1102,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
1102 * The unplug handler unmapped the memory region, it cannot be
1103 * found via an address space anymore. Unset ourselves.
1104 */
1150 - memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
1105 + memory_region_del_ram_discard_source(&vmem->memdev->mr, RAM_DISCARD_SOURCE(vmem));
1106 ram_block_coordinated_discard_require(false);
1107 }
1108
@@ -1175,9 +1130,7 @@ static int virtio_mem_activate_memslot_range_cb(VirtIOMEM *vmem, void *arg,
1130
1131 static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem)
1132 {
1178 - RamDiscardListener *rdl;
1179 - int ret;
1180 -
1133 + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(&vmem->memdev->mr);
1134 /*
1135 * We restored the bitmap and updated the requested size; activate all
1136 * memslots (so listeners register) before notifying about plugged blocks.
@@ -1195,14 +1148,7 @@ static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem)
1148 * We started out with all memory discarded and our memory region is mapped
1149 * into an address space. Replay, now that we updated the bitmap.
1150 */
1198 - QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
1199 - ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
1200 - virtio_mem_notify_populate_cb);
1201 - if (ret) {
1202 - return ret;
1203 - }
1204 - }
1205 - return 0;
1151 + return ram_discard_manager_replay_populated_to_listeners(rdm);
1152 }
1153
1154 static int virtio_mem_post_load(void *opaque, int version_id)
@@ -1650,7 +1596,6 @@ static void virtio_mem_instance_init(Object *obj)
1596 VirtIOMEM *vmem = VIRTIO_MEM(obj);
1597
1598 notifier_list_init(&vmem->size_change_notifiers);
1653 - QLIST_INIT(&vmem->rdl_list);
1599
1600 object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_size,
1601 NULL, NULL, NULL);
@@ -1694,19 +1639,19 @@ static const Property virtio_mem_legacy_guests_properties[] = {
1639 unplugged_inaccessible, ON_OFF_AUTO_ON),
1640 };
1641
1697 -static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm,
1642 +static uint64_t virtio_mem_rds_get_min_granularity(const RamDiscardSource *rds,
1643 const MemoryRegion *mr)
1644 {
1700 - const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1645 + const VirtIOMEM *vmem = VIRTIO_MEM(rds);
1646
1647 g_assert(mr == &vmem->memdev->mr);
1648 return vmem->block_size;
1649 }
1650
1706 -static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm,
1651 +static bool virtio_mem_rds_is_populated(const RamDiscardSource *rds,
1652 const MemoryRegionSection *s)
1653 {
1709 - const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1654 + const VirtIOMEM *vmem = VIRTIO_MEM(rds);
1655 uint64_t start_gpa = vmem->addr + s->offset_within_region;
1656 uint64_t end_gpa = start_gpa + int128_get64(s->size);
1657
@@ -1727,19 +1672,19 @@ struct VirtIOMEMReplayData {
1672 void *opaque;
1673 };
1674
1730 -static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void *arg)
1675 +static int virtio_mem_rds_replay_cb(MemoryRegionSection *s, void *arg)
1676 {
1677 struct VirtIOMEMReplayData *data = arg;
1678
1679 return data->fn(s, data->opaque);
1680 }
1681
1737 -static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm,
1682 +static int virtio_mem_rds_replay_populated(const RamDiscardSource *rds,
1683 MemoryRegionSection *s,
1684 ReplayRamDiscardState replay_fn,
1685 void *opaque)
1686 {
1742 - const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1687 + const VirtIOMEM *vmem = VIRTIO_MEM(rds);
1688 struct VirtIOMEMReplayData data = {
1689 .fn = replay_fn,
1690 .opaque = opaque,
@@ -1747,23 +1692,15 @@ static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm,
1692
1693 g_assert(s->mr == &vmem->memdev->mr);
1694 return virtio_mem_for_each_plugged_section(vmem, s, &data,
1750 - virtio_mem_rdm_replay_populated_cb);
1751 -}
1752 -
1753 -static int virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s,
1754 - void *arg)
1755 -{
1756 - struct VirtIOMEMReplayData *data = arg;
1757 -
1758 - return data->fn(s, data->opaque);
1695 + virtio_mem_rds_replay_cb);
1696 }
1697
1761 -static int virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm,
1698 +static int virtio_mem_rds_replay_discarded(const RamDiscardSource *rds,
1699 MemoryRegionSection *s,
1700 ReplayRamDiscardState replay_fn,
1701 void *opaque)
1702 {
1766 - const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1703 + const VirtIOMEM *vmem = VIRTIO_MEM(rds);
1704 struct VirtIOMEMReplayData data = {
1705 .fn = replay_fn,
1706 .opaque = opaque,
@@ -1771,41 +1708,7 @@ static int virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm,
1708
1709 g_assert(s->mr == &vmem->memdev->mr);
1710 return virtio_mem_for_each_unplugged_section(vmem, s, &data,
1774 - virtio_mem_rdm_replay_discarded_cb);
1775 -}
1776 -
1777 -static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm,
1778 - RamDiscardListener *rdl,
1779 - MemoryRegionSection *s)
1780 -{
1781 - VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1782 - int ret;
1783 -
1784 - g_assert(s->mr == &vmem->memdev->mr);
1785 - rdl->section = memory_region_section_new_copy(s);
1786 -
1787 - QLIST_INSERT_HEAD(&vmem->rdl_list, rdl, next);
1788 - ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
1789 - virtio_mem_notify_populate_cb);
1790 - if (ret) {
1791 - error_report("%s: Replaying plugged ranges failed: %s", __func__,
1792 - strerror(-ret));
1793 - }
1794 -}
1795 -
1796 -static void virtio_mem_rdm_unregister_listener(RamDiscardManager *rdm,
1797 - RamDiscardListener *rdl)
1798 -{
1799 - VirtIOMEM *vmem = VIRTIO_MEM(rdm);
1800 -
1801 - g_assert(rdl->section->mr == &vmem->memdev->mr);
1802 - if (vmem->size) {
1803 - rdl->notify_discard(rdl, rdl->section);
1804 - }
1805 -
1806 - memory_region_section_free_copy(rdl->section);
1807 - rdl->section = NULL;
1808 - QLIST_REMOVE(rdl, next);
1711 + virtio_mem_rds_replay_cb);
1712 }
1713
1714 static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp)
@@ -1837,7 +1740,7 @@ static void virtio_mem_class_init(ObjectClass *klass, const void *data)
1740 DeviceClass *dc = DEVICE_CLASS(klass);
1741 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1742 VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass);
1840 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
1743 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_CLASS(klass);
1744
1745 device_class_set_props(dc, virtio_mem_properties);
1746 if (virtio_mem_has_legacy_guests()) {
@@ -1861,12 +1764,10 @@ static void virtio_mem_class_init(ObjectClass *klass, const void *data)
1764 vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier;
1765 vmc->unplug_request_check = virtio_mem_unplug_request_check;
1766
1864 - rdmc->get_min_granularity = virtio_mem_rdm_get_min_granularity;
1865 - rdmc->is_populated = virtio_mem_rdm_is_populated;
1866 - rdmc->replay_populated = virtio_mem_rdm_replay_populated;
1867 - rdmc->replay_discarded = virtio_mem_rdm_replay_discarded;
1868 - rdmc->register_listener = virtio_mem_rdm_register_listener;
1869 - rdmc->unregister_listener = virtio_mem_rdm_unregister_listener;
1767 + rdsc->get_min_granularity = virtio_mem_rds_get_min_granularity;
1768 + rdsc->is_populated = virtio_mem_rds_is_populated;
1769 + rdsc->replay_populated = virtio_mem_rds_replay_populated;
1770 + rdsc->replay_discarded = virtio_mem_rds_replay_discarded;
1771 }
1772
1773 static const TypeInfo virtio_mem_info = {
@@ -1878,7 +1779,7 @@ static const TypeInfo virtio_mem_info = {
1779 .class_init = virtio_mem_class_init,
1780 .class_size = sizeof(VirtIOMEMClass),
1781 .interfaces = (const InterfaceInfo[]) {
1881 - { TYPE_RAM_DISCARD_MANAGER },
1782 + { TYPE_RAM_DISCARD_SOURCE },
1783 { }
1784 },
1785 };
include/hw/virtio/virtio-mem.h
-3
@@ -118,9 +118,6 @@ struct VirtIOMEM {
118 /* notifiers to notify when "size" changes */
119 NotifierList size_change_notifiers;
120
121 - /* listeners to notify on plug/unplug activity. */
122 - QLIST_HEAD(, RamDiscardListener) rdl_list;
123 -
121 /* Catch system resets -> qemu_devices_reset() only. */
122 VirtioMemSystemReset *system_reset;
123 };
include/system/memory.h
+111 -86
@@ -51,6 +51,12 @@ typedef struct RamDiscardManager RamDiscardManager;
51 DECLARE_OBJ_CHECKERS(RamDiscardManager, RamDiscardManagerClass,
52 RAM_DISCARD_MANAGER, TYPE_RAM_DISCARD_MANAGER);
53
54 +#define TYPE_RAM_DISCARD_SOURCE "ram-discard-source"
55 +typedef struct RamDiscardSourceClass RamDiscardSourceClass;
56 +typedef struct RamDiscardSource RamDiscardSource;
57 +DECLARE_OBJ_CHECKERS(RamDiscardSource, RamDiscardSourceClass,
58 + RAM_DISCARD_SOURCE, TYPE_RAM_DISCARD_SOURCE);
59 +
60 #ifdef CONFIG_FUZZ
61 void fuzz_dma_read_cb(size_t addr,
62 size_t len,
@@ -592,8 +598,8 @@ static inline void ram_discard_listener_init(RamDiscardListener *rdl,
598 /**
599 * typedef ReplayRamDiscardState:
600 *
595 - * The callback handler for #RamDiscardManagerClass.replay_populated/
596 - * #RamDiscardManagerClass.replay_discarded to invoke on populated/discarded
601 + * The callback handler for #RamDiscardSourceClass.replay_populated/
602 + * #RamDiscardSourceClass.replay_discarded to invoke on populated/discarded
603 * parts.
604 *
605 * @section: the #MemoryRegionSection of populated/discarded part
@@ -605,40 +611,17 @@ typedef int (*ReplayRamDiscardState)(MemoryRegionSection *section,
611 void *opaque);
612
613 /*
608 - * RamDiscardManagerClass:
609 - *
610 - * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion
611 - * regions are currently populated to be used/accessed by the VM, notifying
612 - * after parts were discarded (freeing up memory) and before parts will be
613 - * populated (consuming memory), to be used/accessed by the VM.
614 + * RamDiscardSourceClass:
615 *
615 - * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the
616 - * #MemoryRegion isn't mapped into an address space yet (either directly
617 - * or via an alias); it cannot change while the #MemoryRegion is
618 - * mapped into an address space.
616 + * A #RamDiscardSource provides information about which parts of a specific
617 + * RAM #MemoryRegion are currently populated (accessible) vs discarded.
618 *
620 - * The #RamDiscardManager is intended to be used by technologies that are
621 - * incompatible with discarding of RAM (e.g., VFIO, which may pin all
622 - * memory inside a #MemoryRegion), and require proper coordination to only
623 - * map the currently populated parts, to hinder parts that are expected to
624 - * remain discarded from silently getting populated and consuming memory.
625 - * Technologies that support discarding of RAM don't have to bother and can
626 - * simply map the whole #MemoryRegion.
627 - *
628 - * An example #RamDiscardManager is virtio-mem, which logically (un)plugs
629 - * memory within an assigned RAM #MemoryRegion, coordinated with the VM.
630 - * Logically unplugging memory consists of discarding RAM. The VM agreed to not
631 - * access unplugged (discarded) memory - especially via DMA. virtio-mem will
632 - * properly coordinate with listeners before memory is plugged (populated),
633 - * and after memory is unplugged (discarded).
634 - *
635 - * Listeners are called in multiples of the minimum granularity (unless it
636 - * would exceed the registered range) and changes are aligned to the minimum
637 - * granularity within the #MemoryRegion. Listeners have to prepare for memory
638 - * becoming discarded in a different granularity than it was populated and the
639 - * other way around.
619 + * This is an interface that state providers (like virtio-mem or
620 + * RamBlockAttributes) implement to provide discard state information. A
621 + * #RamDiscardManager wraps sources and manages listener registrations and
622 + * notifications.
623 */
641 -struct RamDiscardManagerClass {
624 +struct RamDiscardSourceClass {
625 /* private */
626 InterfaceClass parent_class;
627
@@ -648,47 +631,47 @@ struct RamDiscardManagerClass {
631 * @get_min_granularity:
632 *
633 * Get the minimum granularity in which listeners will get notified
651 - * about changes within the #MemoryRegion via the #RamDiscardManager.
634 + * about changes within the #MemoryRegion via the #RamDiscardSource.
635 *
653 - * @rdm: the #RamDiscardManager
636 + * @rds: the #RamDiscardSource
637 * @mr: the #MemoryRegion
638 *
639 * Returns the minimum granularity.
640 */
658 - uint64_t (*get_min_granularity)(const RamDiscardManager *rdm,
641 + uint64_t (*get_min_granularity)(const RamDiscardSource *rds,
642 const MemoryRegion *mr);
643
644 /**
645 * @is_populated:
646 *
647 * Check whether the given #MemoryRegionSection is completely populated
665 - * (i.e., no parts are currently discarded) via the #RamDiscardManager.
648 + * (i.e., no parts are currently discarded) via the #RamDiscardSource.
649 * There are no alignment requirements.
650 *
668 - * @rdm: the #RamDiscardManager
651 + * @rds: the #RamDiscardSource
652 * @section: the #MemoryRegionSection
653 *
654 * Returns whether the given range is completely populated.
655 */
673 - bool (*is_populated)(const RamDiscardManager *rdm,
656 + bool (*is_populated)(const RamDiscardSource *rds,
657 const MemoryRegionSection *section);
658
659 /**
660 * @replay_populated:
661 *
662 * Call the #ReplayRamDiscardState callback for all populated parts within
680 - * the #MemoryRegionSection via the #RamDiscardManager.
663 + * the #MemoryRegionSection via the #RamDiscardSource.
664 *
665 * In case any call fails, no further calls are made.
666 *
684 - * @rdm: the #RamDiscardManager
667 + * @rds: the #RamDiscardSource
668 * @section: the #MemoryRegionSection
669 * @replay_fn: the #ReplayRamDiscardState callback
670 * @opaque: pointer to forward to the callback
671 *
672 * Returns 0 on success, or a negative error if any notification failed.
673 */
691 - int (*replay_populated)(const RamDiscardManager *rdm,
674 + int (*replay_populated)(const RamDiscardSource *rds,
675 MemoryRegionSection *section,
676 ReplayRamDiscardState replay_fn, void *opaque);
677
@@ -696,50 +679,60 @@ struct RamDiscardManagerClass {
679 * @replay_discarded:
680 *
681 * Call the #ReplayRamDiscardState callback for all discarded parts within
699 - * the #MemoryRegionSection via the #RamDiscardManager.
682 + * the #MemoryRegionSection via the #RamDiscardSource.
683 *
701 - * @rdm: the #RamDiscardManager
684 + * @rds: the #RamDiscardSource
685 * @section: the #MemoryRegionSection
686 * @replay_fn: the #ReplayRamDiscardState callback
687 * @opaque: pointer to forward to the callback
688 *
689 * Returns 0 on success, or a negative error if any notification failed.
690 */
708 - int (*replay_discarded)(const RamDiscardManager *rdm,
691 + int (*replay_discarded)(const RamDiscardSource *rds,
692 MemoryRegionSection *section,
693 ReplayRamDiscardState replay_fn, void *opaque);
694 +};
695
712 - /**
713 - * @register_listener:
714 - *
715 - * Register a #RamDiscardListener for the given #MemoryRegionSection and
716 - * immediately notify the #RamDiscardListener about all populated parts
717 - * within the #MemoryRegionSection via the #RamDiscardManager.
718 - *
719 - * In case any notification fails, no further notifications are triggered
720 - * and an error is logged.
721 - *
722 - * @rdm: the #RamDiscardManager
723 - * @rdl: the #RamDiscardListener
724 - * @section: the #MemoryRegionSection
725 - */
726 - void (*register_listener)(RamDiscardManager *rdm,
727 - RamDiscardListener *rdl,
728 - MemoryRegionSection *section);
696 +/**
697 + * RamDiscardManager:
698 + *
699 + * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion
700 + * regions are currently populated to be used/accessed by the VM, notifying
701 + * after parts were discarded (freeing up memory) and before parts will be
702 + * populated (consuming memory), to be used/accessed by the VM.
703 + *
704 + * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the
705 + * #MemoryRegion isn't mapped into an address space yet (either directly
706 + * or via an alias); it cannot change while the #MemoryRegion is
707 + * mapped into an address space.
708 + *
709 + * The #RamDiscardManager is intended to be used by technologies that are
710 + * incompatible with discarding of RAM (e.g., VFIO, which may pin all
711 + * memory inside a #MemoryRegion), and require proper coordination to only
712 + * map the currently populated parts, to hinder parts that are expected to
713 + * remain discarded from silently getting populated and consuming memory.
714 + * Technologies that support discarding of RAM don't have to bother and can
715 + * simply map the whole #MemoryRegion.
716 + *
717 + * An example #RamDiscardSource is virtio-mem, which logically (un)plugs
718 + * memory within an assigned RAM #MemoryRegion, coordinated with the VM.
719 + * Logically unplugging memory consists of discarding RAM. The VM agreed to not
720 + * access unplugged (discarded) memory - especially via DMA. virtio-mem will
721 + * properly coordinate with listeners before memory is plugged (populated),
722 + * and after memory is unplugged (discarded).
723 + *
724 + * Listeners are called in multiples of the minimum granularity (unless it
725 + * would exceed the registered range) and changes are aligned to the minimum
726 + * granularity within the #MemoryRegion. Listeners have to prepare for memory
727 + * becoming discarded in a different granularity than it was populated and the
728 + * other way around.
729 + */
730 +struct RamDiscardManager {
731 + Object parent;
732
730 - /**
731 - * @unregister_listener:
732 - *
733 - * Unregister a previously registered #RamDiscardListener via the
734 - * #RamDiscardManager after notifying the #RamDiscardListener about all
735 - * populated parts becoming unpopulated within the registered
736 - * #MemoryRegionSection.
737 - *
738 - * @rdm: the #RamDiscardManager
739 - * @rdl: the #RamDiscardListener
740 - */
741 - void (*unregister_listener)(RamDiscardManager *rdm,
742 - RamDiscardListener *rdl);
733 + RamDiscardSource *rds;
734 + MemoryRegion *mr;
735 + QLIST_HEAD(, RamDiscardListener) rdl_list;
736 };
737
738 uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm,
@@ -751,8 +744,8 @@ bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
744 /**
745 * ram_discard_manager_replay_populated:
746 *
754 - * A wrapper to call the #RamDiscardManagerClass.replay_populated callback
755 - * of the #RamDiscardManager.
747 + * A wrapper to call the #RamDiscardSourceClass.replay_populated callback
748 + * of the #RamDiscardSource sources.
749 *
750 * @rdm: the #RamDiscardManager
751 * @section: the #MemoryRegionSection
@@ -769,8 +762,8 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
762 /**
763 * ram_discard_manager_replay_discarded:
764 *
772 - * A wrapper to call the #RamDiscardManagerClass.replay_discarded callback
773 - * of the #RamDiscardManager.
765 + * A wrapper to call the #RamDiscardSourceClass.replay_discarded callback
766 + * of the #RamDiscardSource sources.
767 *
768 * @rdm: the #RamDiscardManager
769 * @section: the #MemoryRegionSection
@@ -791,6 +784,34 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm,
784 void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
785 RamDiscardListener *rdl);
786
787 +/*
788 + * Note: later refactoring should take the source into account and the manager
789 + * should be able to aggregate multiple sources.
790 + */
791 +int ram_discard_manager_notify_populate(RamDiscardManager *rdm,
792 + uint64_t offset, uint64_t size);
793 +
794 + /*
795 + * Note: later refactoring should take the source into account and the manager
796 + * should be able to aggregate multiple sources.
797 + */
798 +void ram_discard_manager_notify_discard(RamDiscardManager *rdm,
799 + uint64_t offset, uint64_t size);
800 +
801 +/*
802 + * Note: later refactoring should take the source into account and the manager
803 + * should be able to aggregate multiple sources.
804 + */
805 +void ram_discard_manager_notify_discard_all(RamDiscardManager *rdm);
806 +
807 +/*
808 + * Replay populated sections to all registered listeners.
809 + *
810 + * Note: later refactoring should take the source into account and the manager
811 + * should be able to aggregate multiple sources.
812 + */
813 +int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm);
814 +
815 /**
816 * memory_translate_iotlb: Extract addresses from a TLB entry.
817 * Called with rcu_read_lock held.
@@ -2504,18 +2525,22 @@ static inline bool memory_region_has_ram_discard_manager(MemoryRegion *mr)
2525 }
2526
2527 /**
2507 - * memory_region_set_ram_discard_manager: set the #RamDiscardManager for a
2528 + * memory_region_add_ram_discard_source: add a #RamDiscardSource for a
2529 * #MemoryRegion
2530 *
2510 - * This function must not be called for a mapped #MemoryRegion, a #MemoryRegion
2511 - * that does not cover RAM, or a #MemoryRegion that already has a
2512 - * #RamDiscardManager assigned. Return 0 if the rdm is set successfully.
2531 + * @mr: the #MemoryRegion
2532 + * @source: #RamDiscardSource to add
2533 + */
2534 +int memory_region_add_ram_discard_source(MemoryRegion *mr, RamDiscardSource *source);
2535 +
2536 +/**
2537 + * memory_region_del_ram_discard_source: remove a #RamDiscardSource for a
2538 + * #MemoryRegion
2539 *
2540 * @mr: the #MemoryRegion
2515 - * @rdm: #RamDiscardManager to set
2541 + * @source: #RamDiscardSource to remove
2542 */
2517 -int memory_region_set_ram_discard_manager(MemoryRegion *mr,
2518 - RamDiscardManager *rdm);
2543 +void memory_region_del_ram_discard_source(MemoryRegion *mr, RamDiscardSource *source);
2544
2545 /**
2546 * memory_region_find: translate an address/size relative to a
include/system/ramblock.h
-2
@@ -99,8 +99,6 @@ struct RamBlockAttributes {
99 /* 1-setting of the bitmap represents ram is populated (shared) */
100 unsigned bitmap_size;
101 unsigned long *bitmap;
102 -
103 - QLIST_HEAD(, RamDiscardListener) rdl_list;
102 };
103
104 /* @offset: the offset within the RAMBlock */
system/memory.c
+191 -27
@@ -2069,34 +2069,88 @@ RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr)
2069 return mr->rdm;
2070 }
2071
2072 -int memory_region_set_ram_discard_manager(MemoryRegion *mr,
2073 - RamDiscardManager *rdm)
2072 +static RamDiscardManager *ram_discard_manager_new(MemoryRegion *mr,
2073 + RamDiscardSource *rds)
2074 +{
2075 + RamDiscardManager *rdm = RAM_DISCARD_MANAGER(object_new(TYPE_RAM_DISCARD_MANAGER));
2076 +
2077 + rdm->rds = rds;
2078 + rdm->mr = mr;
2079 + QLIST_INIT(&rdm->rdl_list);
2080 + return rdm;
2081 +}
2082 +
2083 +int memory_region_add_ram_discard_source(MemoryRegion *mr,
2084 + RamDiscardSource *source)
2085 {
2086 g_assert(memory_region_is_ram(mr));
2076 - if (mr->rdm && rdm) {
2087 + if (mr->rdm) {
2088 return -EBUSY;
2089 }
2090
2080 - mr->rdm = rdm;
2091 + mr->rdm = ram_discard_manager_new(mr, RAM_DISCARD_SOURCE(source));
2092 return 0;
2093 }
2094
2095 +void memory_region_del_ram_discard_source(MemoryRegion *mr,
2096 + RamDiscardSource *source)
2097 +{
2098 + g_assert(mr->rdm->rds == source);
2099 +
2100 + object_unref(mr->rdm);
2101 + mr->rdm = NULL;
2102 +}
2103 +
2104 +static uint64_t ram_discard_source_get_min_granularity(const RamDiscardSource *rds,
2105 + const MemoryRegion *mr)
2106 +{
2107 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
2108 +
2109 + g_assert(rdsc->get_min_granularity);
2110 + return rdsc->get_min_granularity(rds, mr);
2111 +}
2112 +
2113 +static bool ram_discard_source_is_populated(const RamDiscardSource *rds,
2114 + const MemoryRegionSection *section)
2115 +{
2116 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
2117 +
2118 + g_assert(rdsc->is_populated);
2119 + return rdsc->is_populated(rds, section);
2120 +}
2121 +
2122 +static int ram_discard_source_replay_populated(const RamDiscardSource *rds,
2123 + MemoryRegionSection *section,
2124 + ReplayRamDiscardState replay_fn,
2125 + void *opaque)
2126 +{
2127 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
2128 +
2129 + g_assert(rdsc->replay_populated);
2130 + return rdsc->replay_populated(rds, section, replay_fn, opaque);
2131 +}
2132 +
2133 +static int ram_discard_source_replay_discarded(const RamDiscardSource *rds,
2134 + MemoryRegionSection *section,
2135 + ReplayRamDiscardState replay_fn,
2136 + void *opaque)
2137 +{
2138 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
2139 +
2140 + g_assert(rdsc->replay_discarded);
2141 + return rdsc->replay_discarded(rds, section, replay_fn, opaque);
2142 +}
2143 +
2144 uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm,
2145 const MemoryRegion *mr)
2146 {
2087 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
2088 -
2089 - g_assert(rdmc->get_min_granularity);
2090 - return rdmc->get_min_granularity(rdm, mr);
2147 + return ram_discard_source_get_min_granularity(rdm->rds, mr);
2148 }
2149
2150 bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
2151 const MemoryRegionSection *section)
2152 {
2096 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
2097 -
2098 - g_assert(rdmc->is_populated);
2099 - return rdmc->is_populated(rdm, section);
2153 + return ram_discard_source_is_populated(rdm->rds, section);
2154 }
2155
2156 int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
@@ -2104,10 +2158,7 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
2158 ReplayRamDiscardState replay_fn,
2159 void *opaque)
2160 {
2107 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
2108 -
2109 - g_assert(rdmc->replay_populated);
2110 - return rdmc->replay_populated(rdm, section, replay_fn, opaque);
2161 + return ram_discard_source_replay_populated(rdm->rds, section, replay_fn, opaque);
2162 }
2163
2164 int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
@@ -2115,29 +2166,133 @@ int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
2166 ReplayRamDiscardState replay_fn,
2167 void *opaque)
2168 {
2118 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
2169 + return ram_discard_source_replay_discarded(rdm->rds, section, replay_fn, opaque);
2170 +}
2171 +
2172 +static void ram_discard_manager_initfn(Object *obj)
2173 +{
2174 + RamDiscardManager *rdm = RAM_DISCARD_MANAGER(obj);
2175 +
2176 + QLIST_INIT(&rdm->rdl_list);
2177 +}
2178 +
2179 +static void ram_discard_manager_finalize(Object *obj)
2180 +{
2181 + RamDiscardManager *rdm = RAM_DISCARD_MANAGER(obj);
2182
2120 - g_assert(rdmc->replay_discarded);
2121 - return rdmc->replay_discarded(rdm, section, replay_fn, opaque);
2183 + g_assert(QLIST_EMPTY(&rdm->rdl_list));
2184 +}
2185 +
2186 +int ram_discard_manager_notify_populate(RamDiscardManager *rdm,
2187 + uint64_t offset, uint64_t size)
2188 +{
2189 + RamDiscardListener *rdl, *rdl2;
2190 + int ret = 0;
2191 +
2192 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
2193 + MemoryRegionSection tmp = *rdl->section;
2194 +
2195 + if (!memory_region_section_intersect_range(&tmp, offset, size)) {
2196 + continue;
2197 + }
2198 + ret = rdl->notify_populate(rdl, &tmp);
2199 + if (ret) {
2200 + break;
2201 + }
2202 + }
2203 +
2204 + if (ret) {
2205 + /* Notify all already-notified listeners about discard. */
2206 + QLIST_FOREACH(rdl2, &rdm->rdl_list, next) {
2207 + MemoryRegionSection tmp = *rdl2->section;
2208 +
2209 + if (rdl2 == rdl) {
2210 + break;
2211 + }
2212 + if (!memory_region_section_intersect_range(&tmp, offset, size)) {
2213 + continue;
2214 + }
2215 + rdl2->notify_discard(rdl2, &tmp);
2216 + }
2217 + }
2218 + return ret;
2219 +}
2220 +
2221 +void ram_discard_manager_notify_discard(RamDiscardManager *rdm,
2222 + uint64_t offset, uint64_t size)
2223 +{
2224 + RamDiscardListener *rdl;
2225 +
2226 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
2227 + MemoryRegionSection tmp = *rdl->section;
2228 +
2229 + if (!memory_region_section_intersect_range(&tmp, offset, size)) {
2230 + continue;
2231 + }
2232 + rdl->notify_discard(rdl, &tmp);
2233 + }
2234 +}
2235 +
2236 +void ram_discard_manager_notify_discard_all(RamDiscardManager *rdm)
2237 +{
2238 + RamDiscardListener *rdl;
2239 +
2240 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
2241 + rdl->notify_discard(rdl, rdl->section);
2242 + }
2243 +}
2244 +
2245 +static int rdm_populate_cb(MemoryRegionSection *section, void *opaque)
2246 +{
2247 + RamDiscardListener *rdl = opaque;
2248 +
2249 + return rdl->notify_populate(rdl, section);
2250 }
2251
2252 void ram_discard_manager_register_listener(RamDiscardManager *rdm,
2253 RamDiscardListener *rdl,
2254 MemoryRegionSection *section)
2255 {
2128 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
2256 + int ret;
2257 +
2258 + g_assert(section->mr == rdm->mr);
2259 +
2260 + rdl->section = memory_region_section_new_copy(section);
2261 + QLIST_INSERT_HEAD(&rdm->rdl_list, rdl, next);
2262
2130 - g_assert(rdmc->register_listener);
2131 - rdmc->register_listener(rdm, rdl, section);
2263 + ret = ram_discard_source_replay_populated(rdm->rds, rdl->section,
2264 + rdm_populate_cb, rdl);
2265 + if (ret) {
2266 + error_report("%s: Replaying populated ranges failed: %s", __func__,
2267 + strerror(-ret));
2268 + }
2269 }
2270
2271 void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
2272 RamDiscardListener *rdl)
2273 {
2137 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
2274 + g_assert(rdl->section);
2275 + g_assert(rdl->section->mr == rdm->mr);
2276 +
2277 + rdl->notify_discard(rdl, rdl->section);
2278 + memory_region_section_free_copy(rdl->section);
2279 + rdl->section = NULL;
2280 + QLIST_REMOVE(rdl, next);
2281 +}
2282 +
2283 +int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm)
2284 +{
2285 + RamDiscardListener *rdl;
2286 + int ret = 0;
2287
2139 - g_assert(rdmc->unregister_listener);
2140 - rdmc->unregister_listener(rdm, rdl);
2288 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
2289 + ret = ram_discard_source_replay_populated(rdm->rds, rdl->section,
2290 + rdm_populate_cb, rdl);
2291 + if (ret) {
2292 + break;
2293 + }
2294 + }
2295 + return ret;
2296 }
2297
2298 /* Called with rcu_read_lock held. */
@@ -3770,9 +3925,17 @@ static const TypeInfo iommu_memory_region_info = {
3925 };
3926
3927 static const TypeInfo ram_discard_manager_info = {
3773 - .parent = TYPE_INTERFACE,
3928 + .parent = TYPE_OBJECT,
3929 .name = TYPE_RAM_DISCARD_MANAGER,
3775 - .class_size = sizeof(RamDiscardManagerClass),
3930 + .instance_size = sizeof(RamDiscardManager),
3931 + .instance_init = ram_discard_manager_initfn,
3932 + .instance_finalize = ram_discard_manager_finalize,
3933 +};
3934 +
3935 +static const TypeInfo ram_discard_source_info = {
3936 + .parent = TYPE_INTERFACE,
3937 + .name = TYPE_RAM_DISCARD_SOURCE,
3938 + .class_size = sizeof(RamDiscardSourceClass),
3939 };
3940
3941 static void memory_register_types(void)
@@ -3780,6 +3943,7 @@ static void memory_register_types(void)
3943 type_register_static(&memory_region_info);
3944 type_register_static(&iommu_memory_region_info);
3945 type_register_static(&ram_discard_manager_info);
3946 + type_register_static(&ram_discard_source_info);
3947 }
3948
3949 type_init(memory_register_types)
system/ram-block-attributes.c
+51 -120
@@ -18,7 +18,7 @@ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RamBlockAttributes,
18 ram_block_attributes,
19 RAM_BLOCK_ATTRIBUTES,
20 OBJECT,
21 - { TYPE_RAM_DISCARD_MANAGER },
21 + { TYPE_RAM_DISCARD_SOURCE },
22 { })
23
24 static size_t
@@ -32,35 +32,9 @@ ram_block_attributes_get_block_size(void)
32 return qemu_real_host_page_size();
33 }
34
35 -
36 -static bool
37 -ram_block_attributes_rdm_is_populated(const RamDiscardManager *rdm,
38 - const MemoryRegionSection *section)
39 -{
40 - const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
41 - const size_t block_size = ram_block_attributes_get_block_size();
42 - const uint64_t first_bit = section->offset_within_region / block_size;
43 - const uint64_t last_bit =
44 - first_bit + int128_get64(section->size) / block_size - 1;
45 - unsigned long first_discarded_bit;
46 -
47 - first_discarded_bit = find_next_zero_bit(attr->bitmap, last_bit + 1,
48 - first_bit);
49 - return first_discarded_bit > last_bit;
50 -}
51 -
35 typedef int (*ram_block_attributes_section_cb)(MemoryRegionSection *s,
36 void *arg);
37
55 -static int
56 -ram_block_attributes_notify_populate_cb(MemoryRegionSection *section,
57 - void *arg)
58 -{
59 - RamDiscardListener *rdl = arg;
60 -
61 - return rdl->notify_populate(rdl, section);
62 -}
63 -
38 static int
39 ram_block_attributes_for_each_populated_section(const RamBlockAttributes *attr,
40 MemoryRegionSection *section,
@@ -144,93 +118,73 @@ ram_block_attributes_for_each_discarded_section(const RamBlockAttributes *attr,
118 return ret;
119 }
120
147 -static uint64_t
148 -ram_block_attributes_rdm_get_min_granularity(const RamDiscardManager *rdm,
149 - const MemoryRegion *mr)
150 -{
151 - const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
121
153 - g_assert(mr == attr->ram_block->mr);
154 - return ram_block_attributes_get_block_size();
155 -}
122 +typedef struct RamBlockAttributesReplayData {
123 + ReplayRamDiscardState fn;
124 + void *opaque;
125 +} RamBlockAttributesReplayData;
126
157 -static void
158 -ram_block_attributes_rdm_register_listener(RamDiscardManager *rdm,
159 - RamDiscardListener *rdl,
160 - MemoryRegionSection *section)
127 +static int ram_block_attributes_rds_replay_cb(MemoryRegionSection *section,
128 + void *arg)
129 {
162 - RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
163 - int ret;
164 -
165 - g_assert(section->mr == attr->ram_block->mr);
166 - rdl->section = memory_region_section_new_copy(section);
167 -
168 - QLIST_INSERT_HEAD(&attr->rdl_list, rdl, next);
130 + RamBlockAttributesReplayData *data = arg;
131
170 - ret = ram_block_attributes_for_each_populated_section(attr, section, rdl,
171 - ram_block_attributes_notify_populate_cb);
172 - if (ret) {
173 - error_report("%s: Failed to register RAM discard listener: %s",
174 - __func__, strerror(-ret));
175 - exit(1);
176 - }
132 + return data->fn(section, data->opaque);
133 }
134
179 -static void
180 -ram_block_attributes_rdm_unregister_listener(RamDiscardManager *rdm,
181 - RamDiscardListener *rdl)
135 +/* RamDiscardSource interface implementation */
136 +static uint64_t
137 +ram_block_attributes_rds_get_min_granularity(const RamDiscardSource *rds,
138 + const MemoryRegion *mr)
139 {
183 - RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
140 + const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rds);
141
185 - g_assert(rdl->section);
186 - g_assert(rdl->section->mr == attr->ram_block->mr);
187 -
188 - rdl->notify_discard(rdl, rdl->section);
189 -
190 - memory_region_section_free_copy(rdl->section);
191 - rdl->section = NULL;
192 - QLIST_REMOVE(rdl, next);
142 + g_assert(mr == attr->ram_block->mr);
143 + return ram_block_attributes_get_block_size();
144 }
145
195 -typedef struct RamBlockAttributesReplayData {
196 - ReplayRamDiscardState fn;
197 - void *opaque;
198 -} RamBlockAttributesReplayData;
199 -
200 -static int ram_block_attributes_rdm_replay_cb(MemoryRegionSection *section,
201 - void *arg)
146 +static bool
147 +ram_block_attributes_rds_is_populated(const RamDiscardSource *rds,
148 + const MemoryRegionSection *section)
149 {
203 - RamBlockAttributesReplayData *data = arg;
150 + const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rds);
151 + const size_t block_size = ram_block_attributes_get_block_size();
152 + const uint64_t first_bit = section->offset_within_region / block_size;
153 + const uint64_t last_bit =
154 + first_bit + int128_get64(section->size) / block_size - 1;
155 + unsigned long first_discarded_bit;
156
205 - return data->fn(section, data->opaque);
157 + first_discarded_bit = find_next_zero_bit(attr->bitmap, last_bit + 1,
158 + first_bit);
159 + return first_discarded_bit > last_bit;
160 }
161
162 static int
209 -ram_block_attributes_rdm_replay_populated(const RamDiscardManager *rdm,
163 +ram_block_attributes_rds_replay_populated(const RamDiscardSource *rds,
164 MemoryRegionSection *section,
165 ReplayRamDiscardState replay_fn,
166 void *opaque)
167 {
214 - RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
168 + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rds);
169 RamBlockAttributesReplayData data = { .fn = replay_fn, .opaque = opaque };
170
171 g_assert(section->mr == attr->ram_block->mr);
172 return ram_block_attributes_for_each_populated_section(attr, section, &data,
219 - ram_block_attributes_rdm_replay_cb);
173 + ram_block_attributes_rds_replay_cb);
174 }
175
176 static int
223 -ram_block_attributes_rdm_replay_discarded(const RamDiscardManager *rdm,
177 +ram_block_attributes_rds_replay_discarded(const RamDiscardSource *rds,
178 MemoryRegionSection *section,
179 ReplayRamDiscardState replay_fn,
180 void *opaque)
181 {
228 - RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
182 + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rds);
183 RamBlockAttributesReplayData data = { .fn = replay_fn, .opaque = opaque };
184
185 g_assert(section->mr == attr->ram_block->mr);
186 return ram_block_attributes_for_each_discarded_section(attr, section, &data,
233 - ram_block_attributes_rdm_replay_cb);
187 + ram_block_attributes_rds_replay_cb);
188 }
189
190 static bool
@@ -257,42 +211,23 @@ ram_block_attributes_is_valid_range(RamBlockAttributes *attr, uint64_t offset,
211 return true;
212 }
213
260 -static void ram_block_attributes_notify_discard(RamBlockAttributes *attr,
261 - uint64_t offset,
262 - uint64_t size)
214 +static void
215 +ram_block_attributes_notify_discard(RamBlockAttributes *attr,
216 + uint64_t offset,
217 + uint64_t size)
218 {
264 - RamDiscardListener *rdl;
219 + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(attr->ram_block->mr);
220
266 - QLIST_FOREACH(rdl, &attr->rdl_list, next) {
267 - MemoryRegionSection tmp = *rdl->section;
268 -
269 - if (!memory_region_section_intersect_range(&tmp, offset, size)) {
270 - continue;
271 - }
272 - rdl->notify_discard(rdl, &tmp);
273 - }
221 + ram_discard_manager_notify_discard(rdm, offset, size);
222 }
223
224 static int
225 ram_block_attributes_notify_populate(RamBlockAttributes *attr,
226 uint64_t offset, uint64_t size)
227 {
280 - RamDiscardListener *rdl;
281 - int ret = 0;
282 -
283 - QLIST_FOREACH(rdl, &attr->rdl_list, next) {
284 - MemoryRegionSection tmp = *rdl->section;
285 -
286 - if (!memory_region_section_intersect_range(&tmp, offset, size)) {
287 - continue;
288 - }
289 - ret = rdl->notify_populate(rdl, &tmp);
290 - if (ret) {
291 - break;
292 - }
293 - }
228 + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(attr->ram_block->mr);
229
295 - return ret;
230 + return ram_discard_manager_notify_populate(rdm, offset, size);
231 }
232
233 int ram_block_attributes_state_change(RamBlockAttributes *attr,
@@ -376,7 +311,8 @@ RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block)
311 attr = RAM_BLOCK_ATTRIBUTES(object_new(TYPE_RAM_BLOCK_ATTRIBUTES));
312
313 attr->ram_block = ram_block;
379 - if (memory_region_set_ram_discard_manager(mr, RAM_DISCARD_MANAGER(attr))) {
314 +
315 + if (memory_region_add_ram_discard_source(mr, RAM_DISCARD_SOURCE(attr))) {
316 object_unref(OBJECT(attr));
317 return NULL;
318 }
@@ -391,15 +327,12 @@ void ram_block_attributes_destroy(RamBlockAttributes *attr)
327 g_assert(attr);
328
329 g_free(attr->bitmap);
394 - memory_region_set_ram_discard_manager(attr->ram_block->mr, NULL);
330 + memory_region_del_ram_discard_source(attr->ram_block->mr, RAM_DISCARD_SOURCE(attr));
331 object_unref(OBJECT(attr));
332 }
333
334 static void ram_block_attributes_init(Object *obj)
335 {
400 - RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(obj);
401 -
402 - QLIST_INIT(&attr->rdl_list);
336 }
337
338 static void ram_block_attributes_finalize(Object *obj)
@@ -409,12 +342,10 @@ static void ram_block_attributes_finalize(Object *obj)
342 static void ram_block_attributes_class_init(ObjectClass *klass,
343 const void *data)
344 {
412 - RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
413 -
414 - rdmc->get_min_granularity = ram_block_attributes_rdm_get_min_granularity;
415 - rdmc->register_listener = ram_block_attributes_rdm_register_listener;
416 - rdmc->unregister_listener = ram_block_attributes_rdm_unregister_listener;
417 - rdmc->is_populated = ram_block_attributes_rdm_is_populated;
418 - rdmc->replay_populated = ram_block_attributes_rdm_replay_populated;
419 - rdmc->replay_discarded = ram_block_attributes_rdm_replay_discarded;
345 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_CLASS(klass);
346 +
347 + rdsc->get_min_granularity = ram_block_attributes_rds_get_min_granularity;
348 + rdsc->is_populated = ram_block_attributes_rds_is_populated;
349 + rdsc->replay_populated = ram_block_attributes_rds_replay_populated;
350 + rdsc->replay_discarded = ram_block_attributes_rds_replay_discarded;
351 }