@samitouri / QOSamiQemu / commits / d444e348b1

system/memory: move RamDiscardManager to separate compilation unit

Extract RamDiscardManager and RamDiscardSource from system/memory.c into dedicated a unit. This reduces coupling and allows code that only needs the RamDiscardManager interface to avoid pulling in all of memory.h dependencies. rust-sys bindings are no longer generated for RamDiscardSourceClass at this point, thus we drop the unneeded InterfaceClass use. 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-2-5768e6a0943d@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com>

Marc-André Lureau committed Jun 4, 2026 at 17:43 UTC d444e348b11b3f06a2cbbc19281095a9433355bd
7 files changed +542 -501
MAINTAINERS
+2
@@ -3350,6 +3350,7 @@ F: include/system/memory.h
3350 F: include/system/memory_cached.h
3351 F: include/system/memory_ldst*
3352 F: include/system/physmem.h
3353 +F: include/system/ram-discard-manager.h
3354 F: include/system/ramblock.h
3355 F: include/system/memory_mapping.h
3356 F: system/dma-helpers.c
@@ -3360,6 +3361,7 @@ F: system/physmem.c
3361 F: system/memory_ldst*
3362 F: system/memory-internal.h
3363 F: system/ram-block-attributes.c
3364 +F: system/ram-discard-manager.c
3365 F: scripts/coccinelle/memory-region-housekeeping.cocci
3366
3367 Memory devices
include/system/memory.h
+1 -279
@@ -16,6 +16,7 @@
16
17 #include "exec/hwaddr.h"
18 #include "system/ram_addr.h"
19 +#include "system/ram-discard-manager.h"
20 #include "exec/memattrs.h"
21 #include "exec/memop.h"
22 #include "qemu/bswap.h"
@@ -45,18 +46,6 @@ typedef struct IOMMUMemoryRegionClass IOMMUMemoryRegionClass;
46 DECLARE_OBJ_CHECKERS(IOMMUMemoryRegion, IOMMUMemoryRegionClass,
47 IOMMU_MEMORY_REGION, TYPE_IOMMU_MEMORY_REGION)
48
48 -#define TYPE_RAM_DISCARD_MANAGER "ram-discard-manager"
49 -typedef struct RamDiscardManagerClass RamDiscardManagerClass;
50 -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 -
49 #ifdef CONFIG_FUZZ
50 void fuzz_dma_read_cb(size_t addr,
51 size_t len,
@@ -545,273 +534,6 @@ struct IOMMUMemoryRegionClass {
534 int (*num_indexes)(IOMMUMemoryRegion *iommu);
535 };
536
548 -typedef struct RamDiscardListener RamDiscardListener;
549 -typedef int (*NotifyRamPopulate)(RamDiscardListener *rdl,
550 - MemoryRegionSection *section);
551 -typedef void (*NotifyRamDiscard)(RamDiscardListener *rdl,
552 - MemoryRegionSection *section);
553 -
554 -struct RamDiscardListener {
555 - /*
556 - * @notify_populate:
557 - *
558 - * Notification that previously discarded memory is about to get populated.
559 - * Listeners are able to object. If any listener objects, already
560 - * successfully notified listeners are notified about a discard again.
561 - *
562 - * @rdl: the #RamDiscardListener getting notified
563 - * @section: the #MemoryRegionSection to get populated. The section
564 - * is aligned within the memory region to the minimum granularity
565 - * unless it would exceed the registered section.
566 - *
567 - * Returns 0 on success. If the notification is rejected by the listener,
568 - * an error is returned.
569 - */
570 - NotifyRamPopulate notify_populate;
571 -
572 - /*
573 - * @notify_discard:
574 - *
575 - * Notification that previously populated memory was discarded successfully
576 - * and listeners should drop all references to such memory and prevent
577 - * new population (e.g., unmap).
578 - *
579 - * @rdl: the #RamDiscardListener getting notified
580 - * @section: the #MemoryRegionSection to get discarded. The section
581 - * is aligned within the memory region to the minimum granularity
582 - * unless it would exceed the registered section.
583 - */
584 - NotifyRamDiscard notify_discard;
585 -
586 - MemoryRegionSection *section;
587 - QLIST_ENTRY(RamDiscardListener) next;
588 -};
589 -
590 -static inline void ram_discard_listener_init(RamDiscardListener *rdl,
591 - NotifyRamPopulate populate_fn,
592 - NotifyRamDiscard discard_fn)
593 -{
594 - rdl->notify_populate = populate_fn;
595 - rdl->notify_discard = discard_fn;
596 -}
597 -
598 -/**
599 - * typedef ReplayRamDiscardState:
600 - *
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
606 - * @opaque: pointer to forward to the callback
607 - *
608 - * Returns 0 on success, or a negative error if failed.
609 - */
610 -typedef int (*ReplayRamDiscardState)(MemoryRegionSection *section,
611 - void *opaque);
612 -
613 -/*
614 - * RamDiscardSourceClass:
615 - *
616 - * A #RamDiscardSource provides information about which parts of a specific
617 - * RAM #MemoryRegion are currently populated (accessible) vs discarded.
618 - *
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 - */
624 -struct RamDiscardSourceClass {
625 - /* private */
626 - InterfaceClass parent_class;
627 -
628 - /* public */
629 -
630 - /**
631 - * @get_min_granularity:
632 - *
633 - * Get the minimum granularity in which listeners will get notified
634 - * about changes within the #MemoryRegion via the #RamDiscardSource.
635 - *
636 - * @rds: the #RamDiscardSource
637 - * @mr: the #MemoryRegion
638 - *
639 - * Returns the minimum granularity.
640 - */
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
648 - * (i.e., no parts are currently discarded) via the #RamDiscardSource.
649 - * There are no alignment requirements.
650 - *
651 - * @rds: the #RamDiscardSource
652 - * @section: the #MemoryRegionSection
653 - *
654 - * Returns whether the given range is completely populated.
655 - */
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
663 - * the #MemoryRegionSection via the #RamDiscardSource.
664 - *
665 - * In case any call fails, no further calls are made.
666 - *
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 - */
674 - int (*replay_populated)(const RamDiscardSource *rds,
675 - MemoryRegionSection *section,
676 - ReplayRamDiscardState replay_fn, void *opaque);
677 -
678 - /**
679 - * @replay_discarded:
680 - *
681 - * Call the #ReplayRamDiscardState callback for all discarded parts within
682 - * the #MemoryRegionSection via the #RamDiscardSource.
683 - *
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 - */
691 - int (*replay_discarded)(const RamDiscardSource *rds,
692 - MemoryRegionSection *section,
693 - ReplayRamDiscardState replay_fn, void *opaque);
694 -};
695 -
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 -
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,
739 - const MemoryRegion *mr);
740 -
741 -bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
742 - const MemoryRegionSection *section);
743 -
744 -/**
745 - * ram_discard_manager_replay_populated:
746 - *
747 - * A wrapper to call the #RamDiscardSourceClass.replay_populated callback
748 - * of the #RamDiscardSource sources.
749 - *
750 - * @rdm: the #RamDiscardManager
751 - * @section: the #MemoryRegionSection
752 - * @replay_fn: the #ReplayRamDiscardState callback
753 - * @opaque: pointer to forward to the callback
754 - *
755 - * Returns 0 on success, or a negative error if any notification failed.
756 - */
757 -int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
758 - MemoryRegionSection *section,
759 - ReplayRamDiscardState replay_fn,
760 - void *opaque);
761 -
762 -/**
763 - * ram_discard_manager_replay_discarded:
764 - *
765 - * A wrapper to call the #RamDiscardSourceClass.replay_discarded callback
766 - * of the #RamDiscardSource sources.
767 - *
768 - * @rdm: the #RamDiscardManager
769 - * @section: the #MemoryRegionSection
770 - * @replay_fn: the #ReplayRamDiscardState callback
771 - * @opaque: pointer to forward to the callback
772 - *
773 - * Returns 0 on success, or a negative error if any notification failed.
774 - */
775 -int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
776 - MemoryRegionSection *section,
777 - ReplayRamDiscardState replay_fn,
778 - void *opaque);
779 -
780 -void ram_discard_manager_register_listener(RamDiscardManager *rdm,
781 - RamDiscardListener *rdl,
782 - MemoryRegionSection *section);
783 -
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 -
537 /**
538 * memory_translate_iotlb: Extract addresses from a TLB entry.
539 * Called with rcu_read_lock held.
include/system/ram-discard-manager.h new
+297
@@ -0,0 +1,297 @@
1 +/* SPDX-License-Identifier: GPL-2.0-or-later */
2 +/*
3 + * RAM Discard Manager
4 + *
5 + * Copyright Red Hat, Inc. 2026
6 + */
7 +
8 +#ifndef RAM_DISCARD_MANAGER_H
9 +#define RAM_DISCARD_MANAGER_H
10 +
11 +#include "qemu/typedefs.h"
12 +#include "qom/object.h"
13 +#include "qemu/queue.h"
14 +
15 +#define TYPE_RAM_DISCARD_MANAGER "ram-discard-manager"
16 +typedef struct RamDiscardManagerClass RamDiscardManagerClass;
17 +typedef struct RamDiscardManager RamDiscardManager;
18 +DECLARE_OBJ_CHECKERS(RamDiscardManager, RamDiscardManagerClass,
19 + RAM_DISCARD_MANAGER, TYPE_RAM_DISCARD_MANAGER);
20 +
21 +#define TYPE_RAM_DISCARD_SOURCE "ram-discard-source"
22 +typedef struct RamDiscardSourceClass RamDiscardSourceClass;
23 +typedef struct RamDiscardSource RamDiscardSource;
24 +DECLARE_OBJ_CHECKERS(RamDiscardSource, RamDiscardSourceClass,
25 + RAM_DISCARD_SOURCE, TYPE_RAM_DISCARD_SOURCE);
26 +
27 +typedef struct RamDiscardListener RamDiscardListener;
28 +typedef int (*NotifyRamPopulate)(RamDiscardListener *rdl,
29 + MemoryRegionSection *section);
30 +typedef void (*NotifyRamDiscard)(RamDiscardListener *rdl,
31 + MemoryRegionSection *section);
32 +
33 +struct RamDiscardListener {
34 + /*
35 + * @notify_populate:
36 + *
37 + * Notification that previously discarded memory is about to get populated.
38 + * Listeners are able to object. If any listener objects, already
39 + * successfully notified listeners are notified about a discard again.
40 + *
41 + * @rdl: the #RamDiscardListener getting notified
42 + * @section: the #MemoryRegionSection to get populated. The section
43 + * is aligned within the memory region to the minimum granularity
44 + * unless it would exceed the registered section.
45 + *
46 + * Returns 0 on success. If the notification is rejected by the listener,
47 + * an error is returned.
48 + */
49 + NotifyRamPopulate notify_populate;
50 +
51 + /*
52 + * @notify_discard:
53 + *
54 + * Notification that previously populated memory was discarded successfully
55 + * and listeners should drop all references to such memory and prevent
56 + * new population (e.g., unmap).
57 + *
58 + * @rdl: the #RamDiscardListener getting notified
59 + * @section: the #MemoryRegionSection to get discarded. The section
60 + * is aligned within the memory region to the minimum granularity
61 + * unless it would exceed the registered section.
62 + */
63 + NotifyRamDiscard notify_discard;
64 +
65 + MemoryRegionSection *section;
66 + QLIST_ENTRY(RamDiscardListener) next;
67 +};
68 +
69 +static inline void ram_discard_listener_init(RamDiscardListener *rdl,
70 + NotifyRamPopulate populate_fn,
71 + NotifyRamDiscard discard_fn)
72 +{
73 + rdl->notify_populate = populate_fn;
74 + rdl->notify_discard = discard_fn;
75 +}
76 +
77 +/**
78 + * typedef ReplayRamDiscardState:
79 + *
80 + * The callback handler for #RamDiscardSourceClass.replay_populated/
81 + * #RamDiscardSourceClass.replay_discarded to invoke on populated/discarded
82 + * parts.
83 + *
84 + * @section: the #MemoryRegionSection of populated/discarded part
85 + * @opaque: pointer to forward to the callback
86 + *
87 + * Returns 0 on success, or a negative error if failed.
88 + */
89 +typedef int (*ReplayRamDiscardState)(MemoryRegionSection *section,
90 + void *opaque);
91 +
92 +/*
93 + * RamDiscardSourceClass:
94 + *
95 + * A #RamDiscardSource provides information about which parts of a specific
96 + * RAM #MemoryRegion are currently populated (accessible) vs discarded.
97 + *
98 + * This is an interface that state providers (like virtio-mem or
99 + * RamBlockAttributes) implement to provide discard state information. A
100 + * #RamDiscardManager wraps sources and manages listener registrations and
101 + * notifications.
102 + */
103 +struct RamDiscardSourceClass {
104 + /* private */
105 + InterfaceClass parent_class;
106 +
107 + /* public */
108 +
109 + /**
110 + * @get_min_granularity:
111 + *
112 + * Get the minimum granularity in which listeners will get notified
113 + * about changes within the #MemoryRegion via the #RamDiscardSource.
114 + *
115 + * @rds: the #RamDiscardSource
116 + * @mr: the #MemoryRegion
117 + *
118 + * Returns the minimum granularity.
119 + */
120 + uint64_t (*get_min_granularity)(const RamDiscardSource *rds,
121 + const MemoryRegion *mr);
122 +
123 + /**
124 + * @is_populated:
125 + *
126 + * Check whether the given #MemoryRegionSection is completely populated
127 + * (i.e., no parts are currently discarded) via the #RamDiscardSource.
128 + * There are no alignment requirements.
129 + *
130 + * @rds: the #RamDiscardSource
131 + * @section: the #MemoryRegionSection
132 + *
133 + * Returns whether the given range is completely populated.
134 + */
135 + bool (*is_populated)(const RamDiscardSource *rds,
136 + const MemoryRegionSection *section);
137 +
138 + /**
139 + * @replay_populated:
140 + *
141 + * Call the #ReplayRamDiscardState callback for all populated parts within
142 + * the #MemoryRegionSection via the #RamDiscardSource.
143 + *
144 + * In case any call fails, no further calls are made.
145 + *
146 + * @rds: the #RamDiscardSource
147 + * @section: the #MemoryRegionSection
148 + * @replay_fn: the #ReplayRamDiscardState callback
149 + * @opaque: pointer to forward to the callback
150 + *
151 + * Returns 0 on success, or a negative error if any notification failed.
152 + */
153 + int (*replay_populated)(const RamDiscardSource *rds,
154 + MemoryRegionSection *section,
155 + ReplayRamDiscardState replay_fn, void *opaque);
156 +
157 + /**
158 + * @replay_discarded:
159 + *
160 + * Call the #ReplayRamDiscardState callback for all discarded parts within
161 + * the #MemoryRegionSection via the #RamDiscardSource.
162 + *
163 + * @rds: the #RamDiscardSource
164 + * @section: the #MemoryRegionSection
165 + * @replay_fn: the #ReplayRamDiscardState callback
166 + * @opaque: pointer to forward to the callback
167 + *
168 + * Returns 0 on success, or a negative error if any notification failed.
169 + */
170 + int (*replay_discarded)(const RamDiscardSource *rds,
171 + MemoryRegionSection *section,
172 + ReplayRamDiscardState replay_fn, void *opaque);
173 +};
174 +
175 +/**
176 + * RamDiscardManager:
177 + *
178 + * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion
179 + * regions are currently populated to be used/accessed by the VM, notifying
180 + * after parts were discarded (freeing up memory) and before parts will be
181 + * populated (consuming memory), to be used/accessed by the VM.
182 + *
183 + * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the
184 + * #MemoryRegion isn't mapped into an address space yet (either directly
185 + * or via an alias); it cannot change while the #MemoryRegion is
186 + * mapped into an address space.
187 + *
188 + * The #RamDiscardManager is intended to be used by technologies that are
189 + * incompatible with discarding of RAM (e.g., VFIO, which may pin all
190 + * memory inside a #MemoryRegion), and require proper coordination to only
191 + * map the currently populated parts, to hinder parts that are expected to
192 + * remain discarded from silently getting populated and consuming memory.
193 + * Technologies that support discarding of RAM don't have to bother and can
194 + * simply map the whole #MemoryRegion.
195 + *
196 + * An example #RamDiscardSource is virtio-mem, which logically (un)plugs
197 + * memory within an assigned RAM #MemoryRegion, coordinated with the VM.
198 + * Logically unplugging memory consists of discarding RAM. The VM agreed to not
199 + * access unplugged (discarded) memory - especially via DMA. virtio-mem will
200 + * properly coordinate with listeners before memory is plugged (populated),
201 + * and after memory is unplugged (discarded).
202 + *
203 + * Listeners are called in multiples of the minimum granularity (unless it
204 + * would exceed the registered range) and changes are aligned to the minimum
205 + * granularity within the #MemoryRegion. Listeners have to prepare for memory
206 + * becoming discarded in a different granularity than it was populated and the
207 + * other way around.
208 + */
209 +struct RamDiscardManager {
210 + Object parent;
211 +
212 + RamDiscardSource *rds;
213 + MemoryRegion *mr;
214 + QLIST_HEAD(, RamDiscardListener) rdl_list;
215 +};
216 +
217 +RamDiscardManager *ram_discard_manager_new(MemoryRegion *mr,
218 + RamDiscardSource *rds);
219 +
220 +uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm,
221 + const MemoryRegion *mr);
222 +
223 +bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
224 + const MemoryRegionSection *section);
225 +
226 +/**
227 + * ram_discard_manager_replay_populated:
228 + *
229 + * A wrapper to call the #RamDiscardSourceClass.replay_populated callback
230 + * of the #RamDiscardSource sources.
231 + *
232 + * @rdm: the #RamDiscardManager
233 + * @section: the #MemoryRegionSection
234 + * @replay_fn: the #ReplayRamDiscardState callback
235 + * @opaque: pointer to forward to the callback
236 + *
237 + * Returns 0 on success, or a negative error if any notification failed.
238 + */
239 +int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
240 + MemoryRegionSection *section,
241 + ReplayRamDiscardState replay_fn,
242 + void *opaque);
243 +
244 +/**
245 + * ram_discard_manager_replay_discarded:
246 + *
247 + * A wrapper to call the #RamDiscardSourceClass.replay_discarded callback
248 + * of the #RamDiscardSource sources.
249 + *
250 + * @rdm: the #RamDiscardManager
251 + * @section: the #MemoryRegionSection
252 + * @replay_fn: the #ReplayRamDiscardState callback
253 + * @opaque: pointer to forward to the callback
254 + *
255 + * Returns 0 on success, or a negative error if any notification failed.
256 + */
257 +int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
258 + MemoryRegionSection *section,
259 + ReplayRamDiscardState replay_fn,
260 + void *opaque);
261 +
262 +void ram_discard_manager_register_listener(RamDiscardManager *rdm,
263 + RamDiscardListener *rdl,
264 + MemoryRegionSection *section);
265 +
266 +void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
267 + RamDiscardListener *rdl);
268 +
269 +/*
270 + * Note: later refactoring should take the source into account and the manager
271 + * should be able to aggregate multiple sources.
272 + */
273 +int ram_discard_manager_notify_populate(RamDiscardManager *rdm,
274 + uint64_t offset, uint64_t size);
275 +
276 +/*
277 + * Note: later refactoring should take the source into account and the manager
278 + * should be able to aggregate multiple sources.
279 + */
280 +void ram_discard_manager_notify_discard(RamDiscardManager *rdm,
281 + uint64_t offset, uint64_t size);
282 +
283 +/*
284 + * Note: later refactoring should take the source into account and the manager
285 + * should be able to aggregate multiple sources.
286 + */
287 +void ram_discard_manager_notify_discard_all(RamDiscardManager *rdm);
288 +
289 +/*
290 + * Replay populated sections to all registered listeners.
291 + *
292 + * Note: later refactoring should take the source into account and the manager
293 + * should be able to aggregate multiple sources.
294 + */
295 +int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm);
296 +
297 +#endif /* RAM_DISCARD_MANAGER_H */
rust/bindings/system-sys/lib.rs
+1 -1
@@ -20,7 +20,7 @@
20
21 use common::Zeroable;
22 use hwcore_sys::{qemu_irq, DeviceClass, DeviceState};
23 -use qom_sys::{InterfaceClass, Object, ObjectClass};
23 +use qom_sys::{Object, ObjectClass};
24 use util_sys::{Error, EventNotifier, QEMUBH};
25
26 #[cfg(MESON)]
system/memory.c
-221
@@ -2069,17 +2069,6 @@ RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr)
2069 return mr->rdm;
2070 }
2071
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 -
2072 int memory_region_add_ram_discard_source(MemoryRegion *mr,
2073 RamDiscardSource *source)
2074 {
@@ -2101,200 +2090,6 @@ void memory_region_del_ram_discard_source(MemoryRegion *mr,
2090 mr->rdm = NULL;
2091 }
2092
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 -{
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 -{
2153 - return ram_discard_source_is_populated(rdm->rds, section);
2154 -}
2155 -
2156 -int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
2157 - MemoryRegionSection *section,
2158 - ReplayRamDiscardState replay_fn,
2159 - void *opaque)
2160 -{
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,
2165 - MemoryRegionSection *section,
2166 - ReplayRamDiscardState replay_fn,
2167 - void *opaque)
2168 -{
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 -
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 -{
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 -
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 -{
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 -
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 -
2093 /* Called with rcu_read_lock held. */
2094 MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p,
2095 Error **errp)
@@ -3924,26 +3719,10 @@ static const TypeInfo iommu_memory_region_info = {
3719 .abstract = true,
3720 };
3721
3927 -static const TypeInfo ram_discard_manager_info = {
3928 - .parent = TYPE_OBJECT,
3929 - .name = TYPE_RAM_DISCARD_MANAGER,
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 -
3722 static void memory_register_types(void)
3723 {
3724 type_register_static(&memory_region_info);
3725 type_register_static(&iommu_memory_region_info);
3945 - type_register_static(&ram_discard_manager_info);
3946 - type_register_static(&ram_discard_source_info);
3726 }
3727
3728 type_init(memory_register_types)
system/meson.build
+1
@@ -14,6 +14,7 @@ system_ss.add(files(
14 'globals.c',
15 'ioport.c',
16 'ram-block-attributes.c',
17 + 'ram-discard-manager.c',
18 'memory_mapping.c',
19 'memory.c',
20 'physmem.c',
system/ram-discard-manager.c new
+240
@@ -0,0 +1,240 @@
1 +/* SPDX-License-Identifier: GPL-2.0-or-later */
2 +/*
3 + * RAM Discard Manager
4 + *
5 + * Copyright Red Hat, Inc. 2026
6 + */
7 +
8 +#include "qemu/osdep.h"
9 +#include "qemu/error-report.h"
10 +#include "system/memory.h"
11 +
12 +static uint64_t ram_discard_source_get_min_granularity(const RamDiscardSource *rds,
13 + const MemoryRegion *mr)
14 +{
15 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
16 +
17 + g_assert(rdsc->get_min_granularity);
18 + return rdsc->get_min_granularity(rds, mr);
19 +}
20 +
21 +static bool ram_discard_source_is_populated(const RamDiscardSource *rds,
22 + const MemoryRegionSection *section)
23 +{
24 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
25 +
26 + g_assert(rdsc->is_populated);
27 + return rdsc->is_populated(rds, section);
28 +}
29 +
30 +static int ram_discard_source_replay_populated(const RamDiscardSource *rds,
31 + MemoryRegionSection *section,
32 + ReplayRamDiscardState replay_fn,
33 + void *opaque)
34 +{
35 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
36 +
37 + g_assert(rdsc->replay_populated);
38 + return rdsc->replay_populated(rds, section, replay_fn, opaque);
39 +}
40 +
41 +static int ram_discard_source_replay_discarded(const RamDiscardSource *rds,
42 + MemoryRegionSection *section,
43 + ReplayRamDiscardState replay_fn,
44 + void *opaque)
45 +{
46 + RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
47 +
48 + g_assert(rdsc->replay_discarded);
49 + return rdsc->replay_discarded(rds, section, replay_fn, opaque);
50 +}
51 +
52 +RamDiscardManager *ram_discard_manager_new(MemoryRegion *mr,
53 + RamDiscardSource *rds)
54 +{
55 + RamDiscardManager *rdm;
56 +
57 + rdm = RAM_DISCARD_MANAGER(object_new(TYPE_RAM_DISCARD_MANAGER));
58 + rdm->rds = rds;
59 + rdm->mr = mr;
60 + QLIST_INIT(&rdm->rdl_list);
61 + return rdm;
62 +}
63 +
64 +uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm,
65 + const MemoryRegion *mr)
66 +{
67 + return ram_discard_source_get_min_granularity(rdm->rds, mr);
68 +}
69 +
70 +bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
71 + const MemoryRegionSection *section)
72 +{
73 + return ram_discard_source_is_populated(rdm->rds, section);
74 +}
75 +
76 +int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
77 + MemoryRegionSection *section,
78 + ReplayRamDiscardState replay_fn,
79 + void *opaque)
80 +{
81 + return ram_discard_source_replay_populated(rdm->rds, section,
82 + replay_fn, opaque);
83 +}
84 +
85 +int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
86 + MemoryRegionSection *section,
87 + ReplayRamDiscardState replay_fn,
88 + void *opaque)
89 +{
90 + return ram_discard_source_replay_discarded(rdm->rds, section,
91 + replay_fn, opaque);
92 +}
93 +
94 +static void ram_discard_manager_initfn(Object *obj)
95 +{
96 + RamDiscardManager *rdm = RAM_DISCARD_MANAGER(obj);
97 +
98 + QLIST_INIT(&rdm->rdl_list);
99 +}
100 +
101 +static void ram_discard_manager_finalize(Object *obj)
102 +{
103 + RamDiscardManager *rdm = RAM_DISCARD_MANAGER(obj);
104 +
105 + g_assert(QLIST_EMPTY(&rdm->rdl_list));
106 +}
107 +
108 +int ram_discard_manager_notify_populate(RamDiscardManager *rdm,
109 + uint64_t offset, uint64_t size)
110 +{
111 + RamDiscardListener *rdl, *rdl2;
112 + int ret = 0;
113 +
114 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
115 + MemoryRegionSection tmp = *rdl->section;
116 +
117 + if (!memory_region_section_intersect_range(&tmp, offset, size)) {
118 + continue;
119 + }
120 + ret = rdl->notify_populate(rdl, &tmp);
121 + if (ret) {
122 + break;
123 + }
124 + }
125 +
126 + if (ret) {
127 + /* Notify all already-notified listeners about discard. */
128 + QLIST_FOREACH(rdl2, &rdm->rdl_list, next) {
129 + MemoryRegionSection tmp = *rdl2->section;
130 +
131 + if (rdl2 == rdl) {
132 + break;
133 + }
134 + if (!memory_region_section_intersect_range(&tmp, offset, size)) {
135 + continue;
136 + }
137 + rdl2->notify_discard(rdl2, &tmp);
138 + }
139 + }
140 + return ret;
141 +}
142 +
143 +void ram_discard_manager_notify_discard(RamDiscardManager *rdm,
144 + uint64_t offset, uint64_t size)
145 +{
146 + RamDiscardListener *rdl;
147 +
148 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
149 + MemoryRegionSection tmp = *rdl->section;
150 +
151 + if (!memory_region_section_intersect_range(&tmp, offset, size)) {
152 + continue;
153 + }
154 + rdl->notify_discard(rdl, &tmp);
155 + }
156 +}
157 +
158 +void ram_discard_manager_notify_discard_all(RamDiscardManager *rdm)
159 +{
160 + RamDiscardListener *rdl;
161 +
162 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
163 + rdl->notify_discard(rdl, rdl->section);
164 + }
165 +}
166 +
167 +static int rdm_populate_cb(MemoryRegionSection *section, void *opaque)
168 +{
169 + RamDiscardListener *rdl = opaque;
170 +
171 + return rdl->notify_populate(rdl, section);
172 +}
173 +
174 +void ram_discard_manager_register_listener(RamDiscardManager *rdm,
175 + RamDiscardListener *rdl,
176 + MemoryRegionSection *section)
177 +{
178 + int ret;
179 +
180 + g_assert(section->mr == rdm->mr);
181 +
182 + rdl->section = memory_region_section_new_copy(section);
183 + QLIST_INSERT_HEAD(&rdm->rdl_list, rdl, next);
184 +
185 + ret = ram_discard_source_replay_populated(rdm->rds, rdl->section,
186 + rdm_populate_cb, rdl);
187 + if (ret) {
188 + error_report("%s: Replaying populated ranges failed: %s", __func__,
189 + strerror(-ret));
190 + }
191 +}
192 +
193 +void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
194 + RamDiscardListener *rdl)
195 +{
196 + g_assert(rdl->section);
197 + g_assert(rdl->section->mr == rdm->mr);
198 +
199 + rdl->notify_discard(rdl, rdl->section);
200 + memory_region_section_free_copy(rdl->section);
201 + rdl->section = NULL;
202 + QLIST_REMOVE(rdl, next);
203 +}
204 +
205 +int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm)
206 +{
207 + RamDiscardListener *rdl;
208 + int ret = 0;
209 +
210 + QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
211 + ret = ram_discard_source_replay_populated(rdm->rds, rdl->section,
212 + rdm_populate_cb, rdl);
213 + if (ret) {
214 + break;
215 + }
216 + }
217 + return ret;
218 +}
219 +
220 +static const TypeInfo ram_discard_manager_info = {
221 + .parent = TYPE_OBJECT,
222 + .name = TYPE_RAM_DISCARD_MANAGER,
223 + .instance_size = sizeof(RamDiscardManager),
224 + .instance_init = ram_discard_manager_initfn,
225 + .instance_finalize = ram_discard_manager_finalize,
226 +};
227 +
228 +static const TypeInfo ram_discard_source_info = {
229 + .parent = TYPE_INTERFACE,
230 + .name = TYPE_RAM_DISCARD_SOURCE,
231 + .class_size = sizeof(RamDiscardSourceClass),
232 +};
233 +
234 +static void ram_discard_manager_register_types(void)
235 +{
236 + type_register_static(&ram_discard_manager_info);
237 + type_register_static(&ram_discard_source_info);
238 +}
239 +
240 +type_init(ram_discard_manager_register_types)