VFIOContainerBase *bcontainer = vrdl->bcontainer;
@@ -381,7 +383,7 @@ static int
vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
vaddr, section->readonly);
if (ret) {
/* Rollback */
- vfio_ram_discard_notify_discard(rdl, section);
+ vfio_ram_discard_notify_discard(scl, section);
return ret;
}
}
@@ -391,8 +393,9 @@ static int
vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
static void vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer,
MemoryRegionSection *section)
{
- RamDiscardManager *rdm =
memory_region_get_ram_discard_manager(section->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(section->mr);
VFIORamDiscardListener *vrdl;
+ RamDiscardListener *rdl;
/* Ignore some corner cases not relevant in practice. */
g_assert(QEMU_IS_ALIGNED(section->offset_within_region,
TARGET_PAGE_SIZE));
@@ -405,17 +408,18 @@ static void
vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer,
vrdl->mr = section->mr;
vrdl->offset_within_address_space = section->offset_within_address_space;
vrdl->size = int128_get64(section->size);
- vrdl->granularity = ram_discard_manager_get_min_granularity(rdm,
- section->mr);
+ vrdl->granularity = generic_state_manager_get_min_granularity(gsm,
+ section->mr);
g_assert(vrdl->granularity && is_power_of_2(vrdl->granularity));
g_assert(bcontainer->pgsizes &&
vrdl->granularity >= 1ULL << ctz64(bcontainer->pgsizes));
- ram_discard_listener_init(&vrdl->listener,
+ rdl = &vrdl->listener;
+ ram_discard_listener_init(rdl,
vfio_ram_discard_notify_populate,
vfio_ram_discard_notify_discard, true);
- ram_discard_manager_register_listener(rdm, &vrdl->listener, section);
+ generic_state_manager_register_listener(gsm, &rdl->scl, section);
QLIST_INSERT_HEAD(&bcontainer->vrdl_list, vrdl, next);
/*
@@ -465,8 +469,9 @@ static void
vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer,
static void vfio_unregister_ram_discard_listener(VFIOContainerBase
*bcontainer,
MemoryRegionSection *section)
{
- RamDiscardManager *rdm =
memory_region_get_ram_discard_manager(section->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(section->mr);
VFIORamDiscardListener *vrdl = NULL;
+ RamDiscardListener *rdl;
QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) {
if (vrdl->mr == section->mr &&
@@ -480,7 +485,8 @@ static void
vfio_unregister_ram_discard_listener(VFIOContainerBase *bcontainer,
hw_error("vfio: Trying to unregister missing RAM discard listener");
}
- ram_discard_manager_unregister_listener(rdm, &vrdl->listener);
+ rdl = &vrdl->listener;
+ generic_state_manager_unregister_listener(gsm, &rdl->scl);
QLIST_REMOVE(vrdl, next);
g_free(vrdl);
}
@@ -1265,7 +1271,7 @@ static int
vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer,
MemoryRegionSection *section)
{
- RamDiscardManager *rdm =
memory_region_get_ram_discard_manager(section->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(section->mr);
VFIORamDiscardListener *vrdl = NULL;
QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) {
@@ -1284,7 +1290,7 @@
vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer,
* We only want/can synchronize the bitmap for actually mapped parts -
* which correspond to populated parts. Replay all populated parts.
*/
- return ram_discard_manager_replay_populated(rdm, section,
+ return generic_state_manager_replay_on_state_set(gsm, section,
vfio_ram_discard_get_dirty_bitmap,
&vrdl);
}
diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c
index 1a88d649cb..40e8267254 100644
--- a/hw/virtio/virtio-mem.c
+++ b/hw/virtio/virtio-mem.c
@@ -312,16 +312,16 @@ static int virtio_mem_for_each_unplugged_section(const
VirtIOMEM *vmem,
static int virtio_mem_notify_populate_cb(MemoryRegionSection *s, void *arg)
{
- RamDiscardListener *rdl = arg;
+ StateChangeListener *scl = arg;
- return rdl->notify_populate(rdl, s);
+ return scl->notify_to_state_set(scl, s);
}
static int virtio_mem_notify_discard_cb(MemoryRegionSection *s, void *arg)
{
- RamDiscardListener *rdl = arg;
+ StateChangeListener *scl = arg;
- rdl->notify_discard(rdl, s);
+ scl->notify_to_state_clear(scl, s);
return 0;
}
@@ -331,12 +331,13 @@ static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset,
RamDiscardListener *rdl;
QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
- MemoryRegionSection tmp = *rdl->section;
+ StateChangeListener *scl = &rdl->scl;
+ MemoryRegionSection tmp = *scl->section;
if (!memory_region_section_intersect_range(&tmp, offset, size)) {
continue;
}
- rdl->notify_discard(rdl, &tmp);
+ scl->notify_to_state_clear(scl, &tmp);
}
}
@@ -347,12 +348,13 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset,
int ret = 0;
QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
- MemoryRegionSection tmp = *rdl->section;
+ StateChangeListener *scl = &rdl->scl;
+ MemoryRegionSection tmp = *scl->section;
if (!memory_region_section_intersect_range(&tmp, offset, size)) {
continue;
}
- ret = rdl->notify_populate(rdl, &tmp);
+ ret = scl->notify_to_state_set(scl, &tmp);
if (ret) {
break;
}
@@ -361,7 +363,8 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t
offset,
if (ret) {
/* Notify all already-notified listeners. */
QLIST_FOREACH(rdl2, &vmem->rdl_list, next) {
- MemoryRegionSection tmp = *rdl2->section;
+ StateChangeListener *scl2 = &rdl2->scl;
+ MemoryRegionSection tmp = *scl2->section;
if (rdl2 == rdl) {
break;
@@ -369,7 +372,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t
offset,
if (!memory_region_section_intersect_range(&tmp, offset, size)) {
continue;
}
- rdl2->notify_discard(rdl2, &tmp);
+ scl2->notify_to_state_clear(scl2, &tmp);
}
}
return ret;
@@ -384,10 +387,11 @@ static void virtio_mem_notify_unplug_all(VirtIOMEM *vmem)
}
QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
+ StateChangeListener *scl = &rdl->scl;
if (rdl->double_discard_supported) {
- rdl->notify_discard(rdl, rdl->section);
+ scl->notify_to_state_clear(scl, scl->section);
} else {
- virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
+ virtio_mem_for_each_plugged_section(vmem, scl->section, scl,
virtio_mem_notify_discard_cb);
}
}
@@ -1053,8 +1057,8 @@ static void virtio_mem_device_realize(DeviceState *dev,
Error **errp)
* Set ourselves as RamDiscardManager before the plug handler maps the
* memory region and exposes it via an address space.
*/
- if (memory_region_set_ram_discard_manager(&vmem->memdev->mr,
- RAM_DISCARD_MANAGER(vmem))) {
+ if (memory_region_set_generic_state_manager(&vmem->memdev->mr,
+ GENERIC_STATE_MANAGER(vmem))) {
error_setg(errp, "Failed to set RamDiscardManager");
ram_block_coordinated_discard_require(false);
return;
@@ -1158,7 +1162,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
* The unplug handler unmapped the memory region, it cannot be
* found via an address space anymore. Unset ourselves.
*/
- memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
+ memory_region_set_generic_state_manager(&vmem->memdev->mr, NULL);
ram_block_coordinated_discard_require(false);
}
@@ -1207,7 +1211,8 @@ static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem)
* into an address space. Replay, now that we updated the bitmap.
*/
QLIST_FOREACH(rdl, &vmem->rdl_list, next) {
- ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
+ StateChangeListener *scl = &rdl->scl;
+ ret = virtio_mem_for_each_plugged_section(vmem, scl->section, scl,
virtio_mem_notify_populate_cb);
if (ret) {
return ret;
@@ -1704,19 +1709,19 @@ static const Property virtio_mem_properties[] = {
dynamic_memslots, false),
};
-static uint64_t virtio_mem_rdm_get_min_granularity(const RamDiscardManager *rdm,
+static uint64_t virtio_mem_rdm_get_min_granularity(const GenericStateManager
*gsm,
const MemoryRegion *mr)
{
- const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
+ const VirtIOMEM *vmem = VIRTIO_MEM(gsm);
g_assert(mr == &vmem->memdev->mr);
return vmem->block_size;
}
-static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm,
+static bool virtio_mem_rdm_is_populated(const GenericStateManager *gsm,
const MemoryRegionSection *s)
{
- const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
+ const VirtIOMEM *vmem = VIRTIO_MEM(gsm);
uint64_t start_gpa = vmem->addr + s->offset_within_region;
uint64_t end_gpa = start_gpa + int128_get64(s->size);
@@ -1744,12 +1749,12 @@ static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void *arg)
return data->fn(s, data->opaque);
}
-static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm,
+static int virtio_mem_rdm_replay_populated(const GenericStateManager *gsm,
MemoryRegionSection *s,
ReplayStateChange replay_fn,
void *opaque)
{
- const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
+ const VirtIOMEM *vmem = VIRTIO_MEM(gsm);
struct VirtIOMEMReplayData data = {
.fn = replay_fn,
.opaque = opaque,
@@ -1769,12 +1774,12 @@ static int
virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s,
return 0;
}
-static int virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm,
+static int virtio_mem_rdm_replay_discarded(const GenericStateManager *gsm,
MemoryRegionSection *s,
ReplayStateChange replay_fn,
void *opaque)
{
- const VirtIOMEM *vmem = VIRTIO_MEM(rdm);
+ const VirtIOMEM *vmem = VIRTIO_MEM(gsm);
struct VirtIOMEMReplayData data = {
.fn = replay_fn,
.opaque = opaque,
@@ -1785,18 +1790,19 @@ static int virtio_mem_rdm_replay_discarded(const
RamDiscardManager *rdm,
virtio_mem_rdm_replay_discarded_cb);
}
-static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm,
- RamDiscardListener *rdl,
+static void virtio_mem_rdm_register_listener(GenericStateManager *gsm,
+ StateChangeListener *scl,
MemoryRegionSection *s)
{
- VirtIOMEM *vmem = VIRTIO_MEM(rdm);
+ VirtIOMEM *vmem = VIRTIO_MEM(gsm);
+ RamDiscardListener *rdl = container_of(scl, RamDiscardListener, scl);
int ret;
g_assert(s->mr == &vmem->memdev->mr);
- rdl->section = memory_region_section_new_copy(s);
+ scl->section = memory_region_section_new_copy(s);
QLIST_INSERT_HEAD(&vmem->rdl_list, rdl, next);
- ret = virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
+ ret = virtio_mem_for_each_plugged_section(vmem, scl->section, scl,
virtio_mem_notify_populate_cb);
if (ret) {
error_report("%s: Replaying plugged ranges failed: %s", __func__,
@@ -1804,23 +1810,24 @@ static void
virtio_mem_rdm_register_listener(RamDiscardManager *rdm,
}
}
-static void virtio_mem_rdm_unregister_listener(RamDiscardManager *rdm,
- RamDiscardListener *rdl)
+static void virtio_mem_rdm_unregister_listener(GenericStateManager *gsm,
+ StateChangeListener *scl)
{
- VirtIOMEM *vmem = VIRTIO_MEM(rdm);
+ VirtIOMEM *vmem = VIRTIO_MEM(gsm);
+ RamDiscardListener *rdl = container_of(scl, RamDiscardListener, scl);
- g_assert(rdl->section->mr == &vmem->memdev->mr);
+ g_assert(scl->section->mr == &vmem->memdev->mr);
if (vmem->size) {
if (rdl->double_discard_supported) {
- rdl->notify_discard(rdl, rdl->section);
+ scl->notify_to_state_clear(scl, scl->section);
} else {
- virtio_mem_for_each_plugged_section(vmem, rdl->section, rdl,
+ virtio_mem_for_each_plugged_section(vmem, scl->section, scl,
virtio_mem_notify_discard_cb);
}
}
- memory_region_section_free_copy(rdl->section);
- rdl->section = NULL;
+ memory_region_section_free_copy(scl->section);
+ scl->section = NULL;
QLIST_REMOVE(rdl, next);
}
@@ -1853,7 +1860,7 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass);
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass);
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_CLASS(klass);
device_class_set_props(dc, virtio_mem_properties);
dc->vmsd = &vmstate_virtio_mem;
@@ -1874,12 +1881,12 @@ static void virtio_mem_class_init(ObjectClass *klass,
void *data)
vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier;
vmc->unplug_request_check = virtio_mem_unplug_request_check;
- rdmc->get_min_granularity = virtio_mem_rdm_get_min_granularity;
- rdmc->is_populated = virtio_mem_rdm_is_populated;
- rdmc->replay_populated = virtio_mem_rdm_replay_populated;
- rdmc->replay_discarded = virtio_mem_rdm_replay_discarded;
- rdmc->register_listener = virtio_mem_rdm_register_listener;
- rdmc->unregister_listener = virtio_mem_rdm_unregister_listener;
+ gsmc->get_min_granularity = virtio_mem_rdm_get_min_granularity;
+ gsmc->is_state_set = virtio_mem_rdm_is_populated;
+ gsmc->replay_on_state_set = virtio_mem_rdm_replay_populated;
+ gsmc->replay_on_state_clear = virtio_mem_rdm_replay_discarded;
+ gsmc->register_listener = virtio_mem_rdm_register_listener;
+ gsmc->unregister_listener = virtio_mem_rdm_unregister_listener;
}
static const TypeInfo virtio_mem_info = {
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 3b1d25a403..30e5838d02 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -43,6 +43,12 @@ typedef struct IOMMUMemoryRegionClass IOMMUMemoryRegionClass;
DECLARE_OBJ_CHECKERS(IOMMUMemoryRegion, IOMMUMemoryRegionClass,
IOMMU_MEMORY_REGION, TYPE_IOMMU_MEMORY_REGION)
+#define TYPE_GENERIC_STATE_MANAGER "generic-state-manager"
+typedef struct GenericStateManagerClass GenericStateManagerClass;
+typedef struct GenericStateManager GenericStateManager;
+DECLARE_OBJ_CHECKERS(GenericStateManager, GenericStateManagerClass,
+ GENERIC_STATE_MANAGER, TYPE_GENERIC_STATE_MANAGER)
+
#define TYPE_RAM_DISCARD_MANAGER "ram-discard-manager"
typedef struct RamDiscardManagerClass RamDiscardManagerClass;
typedef struct RamDiscardManager RamDiscardManager;
@@ -506,103 +512,59 @@ struct IOMMUMemoryRegionClass {
int (*num_indexes)(IOMMUMemoryRegion *iommu);
};
-typedef struct RamDiscardListener RamDiscardListener;
-typedef int (*NotifyRamPopulate)(RamDiscardListener *rdl,
- MemoryRegionSection *section);
-typedef void (*NotifyRamDiscard)(RamDiscardListener *rdl,
+typedef int (*ReplayStateChange)(MemoryRegionSection *section, void *opaque);
+
+typedef struct StateChangeListener StateChangeListener;
+typedef int (*NotifyStateSet)(StateChangeListener *scl,
+ MemoryRegionSection *section);
+typedef void (*NotifyStateClear)(StateChangeListener *scl,
MemoryRegionSection *section);
-struct RamDiscardListener {
+struct StateChangeListener {
/*
- * @notify_populate:
+ * @notify_to_state_set:
*
- * Notification that previously discarded memory is about to get populated.
- * Listeners are able to object. If any listener objects, already
- * successfully notified listeners are notified about a discard again.
+ * Notification that previously state clear part is about to be set.
*
- * @rdl: the #RamDiscardListener getting notified
- * @section: the #MemoryRegionSection to get populated. The section
+ * @scl: the #StateChangeListener getting notified
+ * @section: the #MemoryRegionSection to be state-set. The section
* is aligned within the memory region to the minimum
granularity
* unless it would exceed the registered section.
*
* Returns 0 on success. If the notification is rejected by the listener,
* an error is returned.
*/
- NotifyRamPopulate notify_populate;
+ NotifyStateSet notify_to_state_set;
/*
- * @notify_discard:
+ * @notify_to_state_clear:
*
- * Notification that previously populated memory was discarded successfully
- * and listeners should drop all references to such memory and prevent
- * new population (e.g., unmap).
+ * Notification that previously state set part is about to be cleared
*
- * @rdl: the #RamDiscardListener getting notified
- * @section: the #MemoryRegionSection to get populated. The section
+ * @scl: the #StateChangeListener getting notified
+ * @section: the #MemoryRegionSection to be state-cleared. The section
* is aligned within the memory region to the minimum
granularity
* unless it would exceed the registered section.
- */
- NotifyRamDiscard notify_discard;
-
- /*
- * @double_discard_supported:
*
- * The listener suppors getting @notify_discard notifications that span
- * already discarded parts.
+ * Returns 0 on success. If the notification is rejected by the listener,
+ * an error is returned.
*/
- bool double_discard_supported;
+ NotifyStateClear notify_to_state_clear;
MemoryRegionSection *section;
- QLIST_ENTRY(RamDiscardListener) next;
};
-static inline void ram_discard_listener_init(RamDiscardListener *rdl,
- NotifyRamPopulate populate_fn,
- NotifyRamDiscard discard_fn,
- bool double_discard_supported)
-{
- rdl->notify_populate = populate_fn;
- rdl->notify_discard = discard_fn;
- rdl->double_discard_supported = double_discard_supported;
-}
-
-typedef int (*ReplayStateChange)(MemoryRegionSection *section, void *opaque);
-
/*
- * RamDiscardManagerClass:
- *
- * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion
- * regions are currently populated to be used/accessed by the VM, notifying
- * after parts were discarded (freeing up memory) and before parts will be
- * populated (consuming memory), to be used/accessed by the VM.
- *
- * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the
- * #MemoryRegion isn't mapped into an address space yet (either directly
- * or via an alias); it cannot change while the #MemoryRegion is
- * mapped into an address space.
+ * GenericStateManagerClass:
*
- * The #RamDiscardManager is intended to be used by technologies that are
- * incompatible with discarding of RAM (e.g., VFIO, which may pin all
- * memory inside a #MemoryRegion), and require proper coordination to only
- * map the currently populated parts, to hinder parts that are expected to
- * remain discarded from silently getting populated and consuming memory.
- * Technologies that support discarding of RAM don't have to bother and can
- * simply map the whole #MemoryRegion.
- *
- * An example #RamDiscardManager is virtio-mem, which logically (un)plugs
- * memory within an assigned RAM #MemoryRegion, coordinated with the VM.
- * Logically unplugging memory consists of discarding RAM. The VM agreed to not
- * access unplugged (discarded) memory - especially via DMA. virtio-mem will
- * properly coordinate with listeners before memory is plugged (populated),
- * and after memory is unplugged (discarded).
+ * A #GenericStateManager is a common interface used to manage the state of
+ * a #MemoryRegion. The managed states is a pair of opposite states, such as
+ * populated and discarded, or private and shared. It is abstract as set and
+ * clear in below callbacks, and the actual state is managed by the
+ * implementation.
*
- * Listeners are called in multiples of the minimum granularity (unless it
- * would exceed the registered range) and changes are aligned to the minimum
- * granularity within the #MemoryRegion. Listeners have to prepare for memory
- * becoming discarded in a different granularity than it was populated and the
- * other way around.
*/
-struct RamDiscardManagerClass {
+struct GenericStateManagerClass {
/* private */
InterfaceClass parent_class;
@@ -612,122 +574,188 @@ struct RamDiscardManagerClass {
* @get_min_granularity:
*
* Get the minimum granularity in which listeners will get notified
- * about changes within the #MemoryRegion via the #RamDiscardManager.
+ * about changes within the #MemoryRegion via the #GenericStateManager.
*
- * @rdm: the #RamDiscardManager
+ * @gsm: the #GenericStateManager
* @mr: the #MemoryRegion
*
* Returns the minimum granularity.
*/
- uint64_t (*get_min_granularity)(const RamDiscardManager *rdm,
+ uint64_t (*get_min_granularity)(const GenericStateManager *gsm,
const MemoryRegion *mr);
/**
- * @is_populated:
+ * @is_state_set:
*
- * Check whether the given #MemoryRegionSection is completely populated
- * (i.e., no parts are currently discarded) via the #RamDiscardManager.
- * There are no alignment requirements.
+ * Check whether the given #MemoryRegionSection state is set.
+ * via the #GenericStateManager.
*
- * @rdm: the #RamDiscardManager
+ * @gsm: the #GenericStateManager
* @section: the #MemoryRegionSection
*
- * Returns whether the given range is completely populated.
+ * Returns whether the given range is completely set.
*/
- bool (*is_populated)(const RamDiscardManager *rdm,
+ bool (*is_state_set)(const GenericStateManager *gsm,
const MemoryRegionSection *section);
/**
- * @replay_populated:
+ * @replay_on_state_set:
*
- * Call the #ReplayStateChange callback for all populated parts within the
- * #MemoryRegionSection via the #RamDiscardManager.
+ * Call the #ReplayStateChange callback for all state set parts within the
+ * #MemoryRegionSection via the #GenericStateManager.
*
* In case any call fails, no further calls are made.
*
- * @rdm: the #RamDiscardManager
+ * @gsm: the #GenericStateManager
* @section: the #MemoryRegionSection
* @replay_fn: the #ReplayStateChange callback
* @opaque: pointer to forward to the callback
*
* Returns 0 on success, or a negative error if any notification failed.
*/
- int (*replay_populated)(const RamDiscardManager *rdm,
- MemoryRegionSection *section,
- ReplayStateChange replay_fn, void *opaque);
+ int (*replay_on_state_set)(const GenericStateManager *gsm,
+ MemoryRegionSection *section,
+ ReplayStateChange replay_fn, void *opaque);
/**
- * @replay_discarded:
+ * @replay_on_state_clear:
*
- * Call the #ReplayStateChange callback for all discarded parts within the
- * #MemoryRegionSection via the #RamDiscardManager.
+ * Call the #ReplayStateChange callback for all state clear parts within
the
+ * #MemoryRegionSection via the #GenericStateManager.
+ *
+ * In case any call fails, no further calls are made.
*
- * @rdm: the #RamDiscardManager
+ * @gsm: the #GenericStateManager
* @section: the #MemoryRegionSection
* @replay_fn: the #ReplayStateChange callback
* @opaque: pointer to forward to the callback
*
* Returns 0 on success, or a negative error if any notification failed.
*/
- int (*replay_discarded)(const RamDiscardManager *rdm,
- MemoryRegionSection *section,
- ReplayStateChange replay_fn, void *opaque);
+ int (*replay_on_state_clear)(const GenericStateManager *gsm,
+ MemoryRegionSection *section,
+ ReplayStateChange replay_fn, void *opaque);
/**
* @register_listener:
*
- * Register a #RamDiscardListener for the given #MemoryRegionSection and
- * immediately notify the #RamDiscardListener about all populated parts
- * within the #MemoryRegionSection via the #RamDiscardManager.
+ * Register a #StateChangeListener for the given #MemoryRegionSection and
+ * immediately notify the #StateChangeListener about all state-set parts
+ * within the #MemoryRegionSection via the #GenericStateManager.
*
* In case any notification fails, no further notifications are triggered
* and an error is logged.
*
- * @rdm: the #RamDiscardManager
- * @rdl: the #RamDiscardListener
+ * @rdm: the #GenericStateManager
+ * @rdl: the #StateChangeListener
* @section: the #MemoryRegionSection
*/
- void (*register_listener)(RamDiscardManager *rdm,
- RamDiscardListener *rdl,
+ void (*register_listener)(GenericStateManager *gsm,
+ StateChangeListener *scl,
MemoryRegionSection *section);
/**
* @unregister_listener:
*
- * Unregister a previously registered #RamDiscardListener via the
- * #RamDiscardManager after notifying the #RamDiscardListener about all
- * populated parts becoming unpopulated within the registered
+ * Unregister a previously registered #StateChangeListener via the
+ * #GenericStateManager after notifying the #StateChangeListener about all
+ * state-set parts becoming state-cleared within the registered
* #MemoryRegionSection.
*
- * @rdm: the #RamDiscardManager
- * @rdl: the #RamDiscardListener
+ * @rdm: the #GenericStateManager
+ * @rdl: the #StateChangeListener
*/
- void (*unregister_listener)(RamDiscardManager *rdm,
- RamDiscardListener *rdl);
+ void (*unregister_listener)(GenericStateManager *gsm,
+ StateChangeListener *scl);
};
-uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm,
- const MemoryRegion *mr);
+uint64_t generic_state_manager_get_min_granularity(const GenericStateManager
*gsm,
+ const MemoryRegion *mr);
-bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
- const MemoryRegionSection *section);
+bool generic_state_manager_is_state_set(const GenericStateManager *gsm,
+ const MemoryRegionSection *section);
-int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
- MemoryRegionSection *section,
- ReplayStateChange replay_fn,
- void *opaque);
+int generic_state_manager_replay_on_state_set(const GenericStateManager *gsm,
+ MemoryRegionSection *section,
+ ReplayStateChange replay_fn,
+ void *opaque);
-int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
- MemoryRegionSection *section,
- ReplayStateChange replay_fn,
- void *opaque);
+int generic_state_manager_replay_on_state_clear(const GenericStateManager *gsm,
+ MemoryRegionSection *section,
+ ReplayStateChange replay_fn,
+ void *opaque);
-void ram_discard_manager_register_listener(RamDiscardManager *rdm,
- RamDiscardListener *rdl,
- MemoryRegionSection *section);
+void generic_state_manager_register_listener(GenericStateManager *gsm,
+ StateChangeListener *scl,
+ MemoryRegionSection *section);
-void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
- RamDiscardListener *rdl);
+void generic_state_manager_unregister_listener(GenericStateManager *gsm,
+ StateChangeListener *scl);
+
+typedef struct RamDiscardListener RamDiscardListener;
+
+struct RamDiscardListener {
+ struct StateChangeListener scl;
+
+ /*
+ * @double_discard_supported:
+ *
+ * The listener suppors getting @notify_discard notifications that span
+ * already discarded parts.
+ */
+ bool double_discard_supported;
+
+ QLIST_ENTRY(RamDiscardListener) next;
+};
+
+static inline void ram_discard_listener_init(RamDiscardListener *rdl,
+ NotifyStateSet populate_fn,
+ NotifyStateClear discard_fn,
+ bool double_discard_supported)
+{
+ rdl->scl.notify_to_state_set = populate_fn;
+ rdl->scl.notify_to_state_clear = discard_fn;
+ rdl->double_discard_supported = double_discard_supported;
+}
+
+/*
+ * RamDiscardManagerClass:
+ *
+ * A #RamDiscardManager coordinates which parts of specific RAM #MemoryRegion
+ * regions are currently populated to be used/accessed by the VM, notifying
+ * after parts were discarded (freeing up memory) and before parts will be
+ * populated (consuming memory), to be used/accessed by the VM.
+ *
+ * A #RamDiscardManager can only be set for a RAM #MemoryRegion while the
+ * #MemoryRegion isn't mapped into an address space yet (either directly
+ * or via an alias); it cannot change while the #MemoryRegion is
+ * mapped into an address space.
+ *
+ * The #RamDiscardManager is intended to be used by technologies that are
+ * incompatible with discarding of RAM (e.g., VFIO, which may pin all
+ * memory inside a #MemoryRegion), and require proper coordination to only
+ * map the currently populated parts, to hinder parts that are expected to
+ * remain discarded from silently getting populated and consuming memory.
+ * Technologies that support discarding of RAM don't have to bother and can
+ * simply map the whole #MemoryRegion.
+ *
+ * An example #RamDiscardManager is virtio-mem, which logically (un)plugs
+ * memory within an assigned RAM #MemoryRegion, coordinated with the VM.
+ * Logically unplugging memory consists of discarding RAM. The VM agreed to not
+ * access unplugged (discarded) memory - especially via DMA. virtio-mem will
+ * properly coordinate with listeners before memory is plugged (populated),
+ * and after memory is unplugged (discarded).
+ *
+ * Listeners are called in multiples of the minimum granularity (unless it
+ * would exceed the registered range) and changes are aligned to the minimum
+ * granularity within the #MemoryRegion. Listeners have to prepare for memory
+ * becoming discarded in a different granularity than it was populated and the
+ * other way around.
+ */
+struct RamDiscardManagerClass {
+ /* private */
+ GenericStateManagerClass parent_class;
+};
/**
* memory_get_xlat_addr: Extract addresses from a TLB entry
@@ -795,7 +823,7 @@ struct MemoryRegion {
const char *name;
unsigned ioeventfd_nb;
MemoryRegionIoeventfd *ioeventfds;
- RamDiscardManager *rdm; /* Only for RAM */
+ GenericStateManager *gsm; /* Only for RAM */
/* For devices designed to perform re-entrant IO into their own IO MRs */
bool disable_reentrancy_guard;
@@ -2462,39 +2490,36 @@ bool memory_region_present(MemoryRegion *container,
hwaddr addr);
bool memory_region_is_mapped(MemoryRegion *mr);
/**
- * memory_region_get_ram_discard_manager: get the #RamDiscardManager for a
+ * memory_region_get_generic_state_manager: get the #GenericStateManager for a
* #MemoryRegion
*
- * The #RamDiscardManager cannot change while a memory region is mapped.
+ * The #GenericStateManager cannot change while a memory region is mapped.
*
* @mr: the #MemoryRegion
*/
-RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr);
+GenericStateManager *memory_region_get_generic_state_manager(MemoryRegion *mr);
/**
- * memory_region_has_ram_discard_manager: check whether a #MemoryRegion has a
- * #RamDiscardManager assigned
+ * memory_region_set_generic_state_manager: set the #GenericStateManager for a
+ * #MemoryRegion
+ *
+ * This function must not be called for a mapped #MemoryRegion, a #MemoryRegion
+ * that does not cover RAM, or a #MemoryRegion that already has a
+ * #GenericStateManager assigned. Return 0 if the gsm is set successfully.
*
* @mr: the #MemoryRegion
+ * @gsm: #GenericStateManager to set
*/
-static inline bool memory_region_has_ram_discard_manager(MemoryRegion *mr)
-{
- return !!memory_region_get_ram_discard_manager(mr);
-}
+int memory_region_set_generic_state_manager(MemoryRegion *mr,
+ GenericStateManager *gsm);
/**
- * memory_region_set_ram_discard_manager: set the #RamDiscardManager for a
- * #MemoryRegion
- *
- * This function must not be called for a mapped #MemoryRegion, a #MemoryRegion
- * that does not cover RAM, or a #MemoryRegion that already has a
- * #RamDiscardManager assigned. Return 0 if the rdm is set successfully.
+ * memory_region_has_ram_discard_manager: check whether a #MemoryRegion has a
+ * #RamDiscardManager assigned
*
* @mr: the #MemoryRegion
- * @rdm: #RamDiscardManager to set
*/
-int memory_region_set_ram_discard_manager(MemoryRegion *mr,
- RamDiscardManager *rdm);
+bool memory_region_has_ram_discard_manager(MemoryRegion *mr);
/**
* memory_region_find: translate an address/size relative to a
diff --git a/migration/ram.c b/migration/ram.c
index 053730367b..c881523e64 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -857,14 +857,14 @@ static uint64_t
ramblock_dirty_bitmap_clear_discarded_pages(RAMBlock *rb)
uint64_t cleared_bits = 0;
if (rb->mr && rb->bmap && memory_region_has_ram_discard_manager(rb->mr)) {
- RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(rb->mr);
MemoryRegionSection section = {
.mr = rb->mr,
.offset_within_region = 0,
.size = int128_make64(qemu_ram_get_used_length(rb)),
};
- ram_discard_manager_replay_discarded(rdm, §ion,
+ generic_state_manager_replay_on_state_clear(gsm, §ion,
dirty_bitmap_clear_section,
&cleared_bits);
}
@@ -880,14 +880,14 @@ static uint64_t
ramblock_dirty_bitmap_clear_discarded_pages(RAMBlock *rb)
bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start)
{
if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) {
- RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(rb->mr);
MemoryRegionSection section = {
.mr = rb->mr,
.offset_within_region = start,
.size = int128_make64(qemu_ram_pagesize(rb)),
};
- return !ram_discard_manager_is_populated(rdm, §ion);
+ return !generic_state_manager_is_state_set(gsm, §ion);
}
return false;
}
@@ -1545,14 +1545,14 @@ static void ram_block_populate_read(RAMBlock *rb)
* Note: The result is only stable while migrating (precopy/postcopy).
*/
if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) {
- RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(rb->mr);
MemoryRegionSection section = {
.mr = rb->mr,
.offset_within_region = 0,
.size = rb->mr->size,
};
- ram_discard_manager_replay_populated(rdm, §ion,
+ generic_state_manager_replay_on_state_set(gsm, §ion,
populate_read_section, NULL);
} else {
populate_read_range(rb, 0, rb->used_length);
@@ -1604,14 +1604,14 @@ static int ram_block_uffd_protect(RAMBlock *rb, int
uffd_fd)
/* See ram_block_populate_read() */
if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) {
- RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr);
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(rb->mr);
MemoryRegionSection section = {
.mr = rb->mr,
.offset_within_region = 0,
.size = rb->mr->size,
};
- return ram_discard_manager_replay_populated(rdm, §ion,
+ return generic_state_manager_replay_on_state_set(gsm, §ion,
uffd_protect_section,
(void
*)(uintptr_t)uffd_fd);
}
diff --git a/system/memory.c b/system/memory.c
index b5ab729e13..7b921c66a6 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -2107,83 +2107,93 @@ int memory_region_iommu_num_indexes(IOMMUMemoryRegion
*iommu_mr)
return imrc->num_indexes(iommu_mr);
}
-RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr)
+GenericStateManager *memory_region_get_generic_state_manager(MemoryRegion *mr)
{
if (!memory_region_is_ram(mr)) {
return NULL;
}
- return mr->rdm;
+ return mr->gsm;
}
-int memory_region_set_ram_discard_manager(MemoryRegion *mr,
- RamDiscardManager *rdm)
+int memory_region_set_generic_state_manager(MemoryRegion *mr,
+ GenericStateManager *gsm)
{
g_assert(memory_region_is_ram(mr));
- if (mr->rdm && rdm) {
+ if (mr->gsm && gsm) {
return -EBUSY;
}
- mr->rdm = rdm;
+ mr->gsm = gsm;
return 0;
}
-uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm,
- const MemoryRegion *mr)
+bool memory_region_has_ram_discard_manager(MemoryRegion *mr)
{
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
+ if (!memory_region_is_ram(mr) ||
+ !object_dynamic_cast(OBJECT(mr->gsm), TYPE_RAM_DISCARD_MANAGER)) {
+ return false;
+ }
+
+ return true;
+}
+
+uint64_t generic_state_manager_get_min_granularity(const GenericStateManager
*gsm,
+ const MemoryRegion *mr)
+{
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_GET_CLASS(gsm);
- g_assert(rdmc->get_min_granularity);
- return rdmc->get_min_granularity(rdm, mr);
+ g_assert(gsmc->get_min_granularity);
+ return gsmc->get_min_granularity(gsm, mr);
}
-bool ram_discard_manager_is_populated(const RamDiscardManager *rdm,
- const MemoryRegionSection *section)
+bool generic_state_manager_is_state_set(const GenericStateManager *gsm,
+ const MemoryRegionSection *section)
{
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_GET_CLASS(gsm);
- g_assert(rdmc->is_populated);
- return rdmc->is_populated(rdm, section);
+ g_assert(gsmc->is_state_set);
+ return gsmc->is_state_set(gsm, section);
}
-int ram_discard_manager_replay_populated(const RamDiscardManager *rdm,
- MemoryRegionSection *section,
- ReplayStateChange replay_fn,
- void *opaque)
+int generic_state_manager_replay_on_state_set(const GenericStateManager *gsm,
+ MemoryRegionSection *section,
+ ReplayStateChange replay_fn,
+ void *opaque)
{
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_GET_CLASS(gsm);
- g_assert(rdmc->replay_populated);
- return rdmc->replay_populated(rdm, section, replay_fn, opaque);
+ g_assert(gsmc->replay_on_state_set);
+ return gsmc->replay_on_state_set(gsm, section, replay_fn, opaque);
}
-int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm,
- MemoryRegionSection *section,
- ReplayStateChange replay_fn,
- void *opaque)
+int generic_state_manager_replay_on_state_clear(const GenericStateManager *gsm,
+ MemoryRegionSection *section,
+ ReplayStateChange replay_fn,
+ void *opaque)
{
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_GET_CLASS(gsm);
- g_assert(rdmc->replay_discarded);
- return rdmc->replay_discarded(rdm, section, replay_fn, opaque);
+ g_assert(gsmc->replay_on_state_clear);
+ return gsmc->replay_on_state_clear(gsm, section, replay_fn, opaque);
}
-void ram_discard_manager_register_listener(RamDiscardManager *rdm,
- RamDiscardListener *rdl,
- MemoryRegionSection *section)
+void generic_state_manager_register_listener(GenericStateManager *gsm,
+ StateChangeListener *scl,
+ MemoryRegionSection *section)
{
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_GET_CLASS(gsm);
- g_assert(rdmc->register_listener);
- rdmc->register_listener(rdm, rdl, section);
+ g_assert(gsmc->register_listener);
+ gsmc->register_listener(gsm, scl, section);
}
-void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
- RamDiscardListener *rdl)
+void generic_state_manager_unregister_listener(GenericStateManager *gsm,
+ StateChangeListener *scl)
{
- RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm);
+ GenericStateManagerClass *gsmc = GENERIC_STATE_MANAGER_GET_CLASS(gsm);
- g_assert(rdmc->unregister_listener);
- rdmc->unregister_listener(rdm, rdl);
+ g_assert(gsmc->unregister_listener);
+ gsmc->unregister_listener(gsm, scl);
}
/* Called with rcu_read_lock held. */
@@ -2210,7 +2220,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void
**vaddr,
error_setg(errp, "iommu map to non memory area %" HWADDR_PRIx "",
xlat);
return false;
} else if (memory_region_has_ram_discard_manager(mr)) {
- RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
+ GenericStateManager *gsm = memory_region_get_generic_state_manager(mr);
MemoryRegionSection tmp = {
.mr = mr,
.offset_within_region = xlat,
@@ -2225,7 +2235,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void
**vaddr,
* Disallow that. vmstate priorities make sure any RamDiscardManager
* were already restored before IOMMUs are restored.
*/
- if (!ram_discard_manager_is_populated(rdm, &tmp)) {
+ if (!generic_state_manager_is_state_set(gsm, &tmp)) {
error_setg(errp, "iommu map to discarded memory (e.g., unplugged"
" via virtio-mem): %" HWADDR_PRIx "",
iotlb->translated_addr);
@@ -3814,8 +3824,15 @@ static const TypeInfo iommu_memory_region_info = {
.abstract = true,
};
-static const TypeInfo ram_discard_manager_info = {
+static const TypeInfo generic_state_manager_info = {
.parent = TYPE_INTERFACE,
+ .name = TYPE_GENERIC_STATE_MANAGER,
+ .class_size = sizeof(GenericStateManagerClass),
+ .abstract = true,
+};
+
+static const TypeInfo ram_discard_manager_info = {
+ .parent = TYPE_GENERIC_STATE_MANAGER,
.name = TYPE_RAM_DISCARD_MANAGER,
.class_size = sizeof(RamDiscardManagerClass),
};
@@ -3824,6 +3841,7 @@ static void memory_register_types(void)
{
type_register_static(&memory_region_info);
type_register_static(&iommu_memory_region_info);
+ type_register_static(&generic_state_manager_info);
type_register_static(&ram_discard_manager_info);
}
diff --git a/system/memory_mapping.c b/system/memory_mapping.c
index 37d3325f77..e9d15c737d 100644
--- a/system/memory_mapping.c
+++ b/system/memory_mapping.c
@@ -271,10 +271,8 @@ static void guest_phys_blocks_region_add(MemoryListener
*listener,
/* for special sparse regions, only add populated parts */
if (memory_region_has_ram_discard_manager(section->mr)) {
- RamDiscardManager *rdm;
-
- rdm = memory_region_get_ram_discard_manager(section->mr);
- ram_discard_manager_replay_populated(rdm, section,
+ GenericStateManager *gsm =
memory_region_get_generic_state_manager(section->mr);
+ generic_state_manager_replay_on_state_set(gsm, section,
guest_phys_ram_populate_cb, g);
return;
}