From: Marc-André Lureau <marcandre.lureau@redhat.com>
Replace the source-level replay wrappers with a new
replay_by_populated_state() helper that iterates the section at
min-granularity, calls is_populated() for each chunk, and aggregates
consecutive chunks of the same state before invoking the callback.
This moves the iteration logic from individual sources into the manager,
preparing for multi-source aggregation where the manager must combine
state from multiple sources anyway.
The replay_populated/replay_discarded vtable entries in
RamDiscardSourceClass are no longer called but remain in the interface
for now; they will be removed in follow-up commits along with the
now-dead source implementations.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
system/ram-discard-manager.c | 85 ++++++++++++++++++++++++++----------
1 file changed, 61 insertions(+), 24 deletions(-)
diff --git a/system/ram-discard-manager.c b/system/ram-discard-manager.c
index 1c9ff7fda58..25beb052a1e 100644
--- a/system/ram-discard-manager.c
+++ b/system/ram-discard-manager.c
@@ -27,26 +27,65 @@ static bool ram_discard_source_is_populated(const RamDiscardSource *rds,
return rdsc->is_populated(rds, section);
}
-static int ram_discard_source_replay_populated(const RamDiscardSource *rds,
- const MemoryRegionSection *section,
- ReplayRamDiscardState replay_fn,
- void *opaque)
+/*
+ * Iterate the section at source granularity, aggregating consecutive chunks
+ * with matching populated state, and call replay_fn for each run.
+ */
+static int replay_by_populated_state(const RamDiscardManager *rdm,
+ const MemoryRegionSection *section,
+ bool replay_populated,
+ ReplayRamDiscardState replay_fn,
+ void *opaque)
{
- RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
+ uint64_t granularity, offset, size, end, pos, run_start;
+ bool in_run = false;
+ int ret = 0;
- g_assert(rdsc->replay_populated);
- return rdsc->replay_populated(rds, section, replay_fn, opaque);
-}
+ granularity = ram_discard_source_get_min_granularity(rdm->rds, rdm->mr);
+ offset = section->offset_within_region;
+ size = int128_get64(section->size);
+ end = offset + size;
+
+ /* Align iteration to granularity boundaries */
+ pos = QEMU_ALIGN_DOWN(offset, granularity);
+
+ for (; pos < end; pos += granularity) {
+ MemoryRegionSection chunk = {
+ .mr = section->mr,
+ .offset_within_region = pos,
+ .size = int128_make64(granularity),
+ };
+ bool populated = ram_discard_source_is_populated(rdm->rds, &chunk);
+
+ if (populated == replay_populated) {
+ if (!in_run) {
+ run_start = pos;
+ in_run = true;
+ }
+ } else if (in_run) {
+ MemoryRegionSection tmp = *section;
+
+ if (memory_region_section_intersect_range(&tmp, run_start,
+ pos - run_start)) {
+ ret = replay_fn(&tmp, opaque);
+ if (ret) {
+ return ret;
+ }
+ }
+ in_run = false;
+ }
+ }
-static int ram_discard_source_replay_discarded(const RamDiscardSource *rds,
- const MemoryRegionSection *section,
- ReplayRamDiscardState replay_fn,
- void *opaque)
-{
- RamDiscardSourceClass *rdsc = RAM_DISCARD_SOURCE_GET_CLASS(rds);
+ if (in_run) {
+ MemoryRegionSection tmp = *section;
- g_assert(rdsc->replay_discarded);
- return rdsc->replay_discarded(rds, section, replay_fn, opaque);
+ if (memory_region_section_intersect_range(&tmp, run_start,
+ pos - run_start)) {
+ ret = replay_fn(&tmp, opaque);
+ }
+ }
+
+ return ret;
}
RamDiscardManager *ram_discard_manager_new(MemoryRegion *mr,
@@ -78,8 +117,7 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
ReplayRamDiscardState replay_fn,
void *opaque)
{
- return ram_discard_source_replay_populated(rdm->rds, section,
- replay_fn, opaque);
+ return replay_by_populated_state(rdm, section, true, replay_fn, opaque);
}
int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
@@ -87,8 +125,7 @@ int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
ReplayRamDiscardState replay_fn,
void *opaque)
{
- return ram_discard_source_replay_discarded(rdm->rds, section,
- replay_fn, opaque);
+ return replay_by_populated_state(rdm, section, false, replay_fn, opaque);
}
static void ram_discard_manager_initfn(Object *obj)
@@ -182,8 +219,8 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm,
rdl->section = memory_region_section_new_copy(section);
QLIST_INSERT_HEAD(&rdm->rdl_list, rdl, next);
- ret = ram_discard_source_replay_populated(rdm->rds, rdl->section,
- rdm_populate_cb, rdl);
+ ret = ram_discard_manager_replay_populated(rdm, rdl->section,
+ rdm_populate_cb, rdl);
if (ret) {
error_report("%s: Replaying populated ranges failed: %s", __func__,
strerror(-ret));
@@ -208,8 +245,8 @@ int ram_discard_manager_replay_populated_to_listeners(RamDiscardManager *rdm)
int ret = 0;
QLIST_FOREACH(rdl, &rdm->rdl_list, next) {
- ret = ram_discard_source_replay_populated(rdm->rds, rdl->section,
- rdm_populate_cb, rdl);
+ ret = ram_discard_manager_replay_populated(rdm, rdl->section,
+ rdm_populate_cb, rdl);
if (ret) {
break;
}
--
2.53.0