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)