:p
atchew
Login
The migration stream assumes that the set of migratable RAMBlocks does not change while migration is running. Assert that RAMBlocks are not made migratable or non-migratable during migration, and that migratable RAMBlocks are not freed during migration. Non-migratable RAMBlocks may still be allocated or freed during migration; for example, QMP object-add of memory-backend-ram creates a non-migratable RAMBlock without exposing it to the guest. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> --- system/physmem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/physmem.c b/system/physmem.c index XXXXXXX..XXXXXXX 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -XXX,XX +XXX,XX @@ #include "hw/core/qdev.h" #include "hw/core/qdev-properties.h" #include "hw/core/boards.h" +#include "migration/misc.h" #include "system/xen.h" #include "system/kvm.h" #include "system/tcg.h" @@ -XXX,XX +XXX,XX @@ bool qemu_ram_is_migratable(const RAMBlock *rb) void qemu_ram_set_migratable(RAMBlock *rb) { + assert(!migration_is_running()); rb->flags |= RAM_MIGRATABLE; } void qemu_ram_unset_migratable(RAMBlock *rb) { + assert(!migration_is_running()); rb->flags &= ~RAM_MIGRATABLE; } @@ -XXX,XX +XXX,XX @@ void qemu_ram_free(RAMBlock *block) return; } + assert(!migration_is_running() || !qemu_ram_is_migratable(block)); + if (block->host) { ram_block_notify_remove(block->host, block->used_length, block->max_length); --- base-commit: 2db91528542672cf0db78b3f2cc0e22b36302b38 change-id: 20260602-migration-15128dbe4036 Best regards, -- Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Supersedes: <20260604-migration-v1-1-cef4a5b1bbdd@rsg.ci.i.u-tokyo.ac.jp> ("[PATCH] system/physmem: Assert migration invariants") ram_mig_ram_block_resized() already aborts migration when migratable RAM is resized. Extend the same handling to other unsupported changes to the migratable RAMBlock set, such as removing a migratable RAMBlock or changing a RAMBlock's migratable state. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> --- Akihiko Odaki (3): system/physmem: Pass RAMBlock to RAMBlockNotifier callbacks system/physmem: Notify RAMBlock migratable and idstr changes migration/ram: Abort on unsupported migratable RAM changes include/migration/misc.h | 2 +- include/system/ramlist.h | 24 ++++++--- block/block-ram-registrar.c | 8 +-- hw/core/numa.c | 54 ++++++++++++++++--- hw/xen/xen-mapcache.c | 6 +-- migration/ram.c | 125 ++++++++++++++++++++++++++++++++++++-------- system/physmem.c | 16 ++++-- target/i386/nvmm/nvmm-all.c | 4 +- target/i386/sev.c | 8 +-- util/vfio-helpers.c | 7 +-- 10 files changed, 194 insertions(+), 60 deletions(-) --- base-commit: 2db91528542672cf0db78b3f2cc0e22b36302b38 change-id: 20260606-ram-dcef14f001fb Best regards, -- Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
The RAM save/restore code needs the RAMBlock in RAMBlockNotifier callbacks to decide whether migration must be aborted and to update RAMBlock state for postcopy. Looking up the RAMBlock inside callbacks is hazardous: with Xen mapcache it can deadlock, and it also leaves the callback contract unclear. Pass the RAMBlock explicitly to RAMBlockNotifier callbacks. The ram_block_resized callback takes a non-const RAMBlock because it may modify the RAMBlock, while the other callbacks take const RAMBlock. Xen mapcache passes NULL because mapcache entries do not have a one-to-one relationship with RAMBlocks. ram_block_resized() is never called from Xen mapcache, so its RAMBlock argument is always non-NULL. Drop redundant parameters from ram_block_resized() because they can be derived from the RAMBlock. Keep them for the other callbacks because their RAMBlock argument may be NULL. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> --- include/system/ramlist.h | 18 ++++++++++-------- block/block-ram-registrar.c | 8 ++++---- hw/core/numa.c | 18 ++++++++++-------- hw/xen/xen-mapcache.c | 6 +++--- migration/ram.c | 12 +++--------- system/physmem.c | 7 +++---- target/i386/nvmm/nvmm-all.c | 4 ++-- target/i386/sev.c | 8 ++++---- util/vfio-helpers.c | 7 ++++--- 9 files changed, 43 insertions(+), 45 deletions(-) diff --git a/include/system/ramlist.h b/include/system/ramlist.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/ramlist.h +++ b/include/system/ramlist.h @@ -XXX,XX +XXX,XX @@ void qemu_mutex_lock_ramlist(void); void qemu_mutex_unlock_ramlist(void); struct RAMBlockNotifier { - void (*ram_block_added)(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size); - void (*ram_block_removed)(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size); - void (*ram_block_resized)(RAMBlockNotifier *n, void *host, size_t old_size, + void (*ram_block_added)(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size); + void (*ram_block_removed)(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size); + void (*ram_block_resized)(RAMBlockNotifier *n, RAMBlock *rb, size_t new_size); QLIST_ENTRY(RAMBlockNotifier) next; }; @@ -XXX,XX +XXX,XX @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); void ram_block_notifier_add(RAMBlockNotifier *n); void ram_block_notifier_remove(RAMBlockNotifier *n); -void ram_block_notify_add(void *host, size_t size, size_t max_size); -void ram_block_notify_remove(void *host, size_t size, size_t max_size); -void ram_block_notify_resize(void *host, size_t old_size, size_t new_size); +void ram_block_notify_add(const RAMBlock *rb, + void *host, size_t size, size_t max_size); +void ram_block_notify_remove(const RAMBlock *rb, + void *host, size_t size, size_t max_size); +void ram_block_notify_resize(RAMBlock *rb, size_t new_size); GString *ram_block_format(void); diff --git a/block/block-ram-registrar.c b/block/block-ram-registrar.c index XXXXXXX..XXXXXXX 100644 --- a/block/block-ram-registrar.c +++ b/block/block-ram-registrar.c @@ -XXX,XX +XXX,XX @@ #include "system/block-ram-registrar.h" #include "qapi/error.h" -static void ram_block_added(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size) +static void ram_block_added(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { BlockRAMRegistrar *r = container_of(n, BlockRAMRegistrar, notifier); Error *err = NULL; @@ -XXX,XX +XXX,XX @@ static void ram_block_added(RAMBlockNotifier *n, void *host, size_t size, } } -static void ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size) +static void ram_block_removed(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { BlockRAMRegistrar *r = container_of(n, BlockRAMRegistrar, notifier); blk_unregister_buf(r->blk, host, max_size); diff --git a/hw/core/numa.c b/hw/core/numa.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -XXX,XX +XXX,XX @@ static int ram_block_notify_add_single(RAMBlock *rb, void *opaque) RAMBlockNotifier *notifier = opaque; if (host) { - notifier->ram_block_added(notifier, host, size, max_size); + notifier->ram_block_added(notifier, rb, host, size, max_size); } return 0; } @@ -XXX,XX +XXX,XX @@ static int ram_block_notify_remove_single(RAMBlock *rb, void *opaque) RAMBlockNotifier *notifier = opaque; if (host) { - notifier->ram_block_removed(notifier, host, size, max_size); + notifier->ram_block_removed(notifier, rb, host, size, max_size); } return 0; } @@ -XXX,XX +XXX,XX @@ void ram_block_notifier_remove(RAMBlockNotifier *n) } } -void ram_block_notify_add(void *host, size_t size, size_t max_size) +void ram_block_notify_add(const RAMBlock *rb, + void *host, size_t size, size_t max_size) { RAMBlockNotifier *notifier; RAMBlockNotifier *next; QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { if (notifier->ram_block_added) { - notifier->ram_block_added(notifier, host, size, max_size); + notifier->ram_block_added(notifier, rb, host, size, max_size); } } } -void ram_block_notify_remove(void *host, size_t size, size_t max_size) +void ram_block_notify_remove(const RAMBlock *rb, + void *host, size_t size, size_t max_size) { RAMBlockNotifier *notifier; RAMBlockNotifier *next; QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { if (notifier->ram_block_removed) { - notifier->ram_block_removed(notifier, host, size, max_size); + notifier->ram_block_removed(notifier, rb, host, size, max_size); } } } -void ram_block_notify_resize(void *host, size_t old_size, size_t new_size) +void ram_block_notify_resize(RAMBlock *rb, size_t new_size) { RAMBlockNotifier *notifier; RAMBlockNotifier *next; QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { if (notifier->ram_block_resized) { - notifier->ram_block_resized(notifier, host, old_size, new_size); + notifier->ram_block_resized(notifier, rb, new_size); } } } diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xen/xen-mapcache.c +++ b/hw/xen/xen-mapcache.c @@ -XXX,XX +XXX,XX @@ static void xen_remap_bucket(MapCache *mc, if (entry->vaddr_base != NULL) { if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { - ram_block_notify_remove(entry->vaddr_base, entry->size, + ram_block_notify_remove(NULL, entry->vaddr_base, entry->size, entry->size); } @@ -XXX,XX +XXX,XX @@ static void xen_remap_bucket(MapCache *mc, } if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { - ram_block_notify_add(vaddr_base, size, size); + ram_block_notify_add(NULL, vaddr_base, size, size); } entry->vaddr_base = vaddr_base; @@ -XXX,XX +XXX,XX @@ static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, return; } - ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); + ram_block_notify_remove(NULL, entry->vaddr_base, entry->size, entry->size); if (entry->flags & XEN_MAPCACHE_ENTRY_GRANT) { rc = xengnttab_unmap(xen_region_gnttabdev, entry->vaddr_base, entry->size >> mc->bucket_shift); diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static SaveVMHandlers savevm_ram_handlers = { .save_postcopy_prepare = ram_save_postcopy_prepare, }; -static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, - size_t old_size, size_t new_size) +static void ram_mig_ram_block_resized(RAMBlockNotifier *n, RAMBlock *rb, + size_t new_size) { PostcopyState ps = postcopy_state_get(); - ram_addr_t offset; - RAMBlock *rb = qemu_ram_block_from_host(host, false, &offset); Error *err = NULL; - - if (!rb) { - error_report("RAM block not found"); - return; - } + ram_addr_t old_size = qemu_ram_get_used_length(rb); if (migrate_ram_is_ignored(rb)) { return; diff --git a/system/physmem.c b/system/physmem.c index XXXXXXX..XXXXXXX 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -XXX,XX +XXX,XX @@ static int memory_try_enable_merging(void *addr, size_t len) */ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) { - const ram_addr_t oldsize = block->used_length; const ram_addr_t unaligned_size = newsize; newsize = TARGET_PAGE_ALIGN(newsize); @@ -XXX,XX +XXX,XX @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) /* Notify before modifying the ram block and touching the bitmaps. */ if (block->host) { - ram_block_notify_resize(block->host, oldsize, newsize); + ram_block_notify_resize(block, newsize); } physical_memory_clear_dirty_range(block->offset, block->used_length); @@ -XXX,XX +XXX,XX @@ static void ram_block_add(RAMBlock *new_block, Error **errp) qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK); } - ram_block_notify_add(new_block->host, new_block->used_length, + ram_block_notify_add(new_block, new_block->host, new_block->used_length, new_block->max_length); } return; @@ -XXX,XX +XXX,XX @@ void qemu_ram_free(RAMBlock *block) } if (block->host) { - ram_block_notify_remove(block->host, block->used_length, + ram_block_notify_remove(block, block->host, block->used_length, block->max_length); } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index XXXXXXX..XXXXXXX 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -XXX,XX +XXX,XX @@ static MemoryListener nvmm_memory_listener = { }; static void -nvmm_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size) +nvmm_ram_block_added(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { struct nvmm_machine *mach = get_nvmm_mach(); uintptr_t hva = (uintptr_t)host; diff --git a/target/i386/sev.c b/target/i386/sev.c index XXXXXXX..XXXXXXX 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -XXX,XX +XXX,XX @@ sev_set_guest_state(SevCommonState *sev_common, SevState new_state) } static void -sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size) +sev_ram_block_added(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { int r; struct kvm_enc_region range; @@ -XXX,XX +XXX,XX @@ sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, } static void -sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, - size_t max_size) +sev_ram_block_removed(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { int r; struct kvm_enc_region range; diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index XXXXXXX..XXXXXXX 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -XXX,XX +XXX,XX @@ fail_container: return ret; } -static void qemu_vfio_ram_block_added(RAMBlockNotifier *n, void *host, - size_t size, size_t max_size) +static void qemu_vfio_ram_block_added(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { QEMUVFIOState *s = container_of(n, QEMUVFIOState, ram_notifier); Error *local_err = NULL; @@ -XXX,XX +XXX,XX @@ static void qemu_vfio_ram_block_added(RAMBlockNotifier *n, void *host, } } -static void qemu_vfio_ram_block_removed(RAMBlockNotifier *n, void *host, +static void qemu_vfio_ram_block_removed(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) { QEMUVFIOState *s = container_of(n, QEMUVFIOState, ram_notifier); -- 2.54.0
The RAM migration code assumes that the set of migratable RAMBlocks and their idstr values do not change while migration is running. Add RAMBlockNotifier callbacks for RAM_MIGRATABLE flag and idstr changes so migration can detect attempts to make such changes at runtime and abort with a clear error. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> --- include/system/ramlist.h | 6 ++++++ hw/core/numa.c | 36 ++++++++++++++++++++++++++++++++++++ system/physmem.c | 9 +++++++++ 3 files changed, 51 insertions(+) diff --git a/include/system/ramlist.h b/include/system/ramlist.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/ramlist.h +++ b/include/system/ramlist.h @@ -XXX,XX +XXX,XX @@ struct RAMBlockNotifier { void *host, size_t size, size_t max_size); void (*ram_block_removed)(RAMBlockNotifier *n, const RAMBlock *rb, void *host, size_t size, size_t max_size); + void (*ram_block_set_migratable)(RAMBlockNotifier *n, const RAMBlock *rb); + void (*ram_block_unset_migratable)(RAMBlockNotifier *n, const RAMBlock *rb); + void (*ram_block_set_idstr)(RAMBlockNotifier *n, const RAMBlock *rb); void (*ram_block_resized)(RAMBlockNotifier *n, RAMBlock *rb, size_t new_size); QLIST_ENTRY(RAMBlockNotifier) next; @@ -XXX,XX +XXX,XX @@ void ram_block_notify_add(const RAMBlock *rb, void *host, size_t size, size_t max_size); void ram_block_notify_remove(const RAMBlock *rb, void *host, size_t size, size_t max_size); +void ram_block_notify_set_migratable(const RAMBlock *rb); +void ram_block_notify_unset_migratable(const RAMBlock *rb); +void ram_block_notify_set_idstr(const RAMBlock *rb); void ram_block_notify_resize(RAMBlock *rb, size_t new_size); GString *ram_block_format(void); diff --git a/hw/core/numa.c b/hw/core/numa.c index XXXXXXX..XXXXXXX 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -XXX,XX +XXX,XX @@ void ram_block_notify_remove(const RAMBlock *rb, } } +void ram_block_notify_set_migratable(const RAMBlock *rb) +{ + RAMBlockNotifier *notifier; + RAMBlockNotifier *next; + + QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { + if (notifier->ram_block_set_migratable) { + notifier->ram_block_set_migratable(notifier, rb); + } + } +} + +void ram_block_notify_unset_migratable(const RAMBlock *rb) +{ + RAMBlockNotifier *notifier; + RAMBlockNotifier *next; + + QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { + if (notifier->ram_block_unset_migratable) { + notifier->ram_block_unset_migratable(notifier, rb); + } + } +} + +void ram_block_notify_set_idstr(const RAMBlock *rb) +{ + RAMBlockNotifier *notifier; + RAMBlockNotifier *next; + + QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) { + if (notifier->ram_block_set_idstr) { + notifier->ram_block_set_idstr(notifier, rb); + } + } +} + void ram_block_notify_resize(RAMBlock *rb, size_t new_size) { RAMBlockNotifier *notifier; diff --git a/system/physmem.c b/system/physmem.c index XXXXXXX..XXXXXXX 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -XXX,XX +XXX,XX @@ bool qemu_ram_is_migratable(const RAMBlock *rb) void qemu_ram_set_migratable(RAMBlock *rb) { + /* Notify before modifying the ram block. */ + ram_block_notify_set_migratable(rb); + rb->flags |= RAM_MIGRATABLE; } void qemu_ram_unset_migratable(RAMBlock *rb) { + /* Notify before modifying the ram block. */ + ram_block_notify_unset_migratable(rb); + rb->flags &= ~RAM_MIGRATABLE; } @@ -XXX,XX +XXX,XX @@ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) assert(new_block); assert(!new_block->idstr[0]); + /* Notify before modifying the ram block. */ + ram_block_notify_set_idstr(new_block); + if (dev) { char *id = qdev_get_dev_path(dev); if (id) { -- 2.54.0
ram_mig_ram_block_resized() already aborts migration when migratable RAM is resized. Extend the same handling to other unsupported changes to the migratable RAMBlock set, such as removing a migratable RAMBlock, changing a RAMBlock's migratable state, or setting a RAMBlock's idstr. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> --- include/migration/misc.h | 2 +- migration/ram.c | 117 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/include/migration/misc.h b/include/migration/misc.h index XXXXXXX..XXXXXXX 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -XXX,XX +XXX,XX @@ void precopy_remove_notifier(NotifierWithReturn *n); int precopy_notify(PrecopyNotifyReason reason, Error **errp); void qemu_guest_free_page_hint(void *addr, size_t len); -bool migrate_ram_is_ignored(RAMBlock *block); +bool migrate_ram_is_ignored(const RAMBlock *block); /* migration/block.c */ diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static bool postcopy_preempt_active(void) return migrate_postcopy_preempt() && migration_in_postcopy(); } -bool migrate_ram_is_ignored(RAMBlock *block) +bool migrate_ram_is_ignored(const RAMBlock *block) { MigMode mode = migrate_mode(); return !qemu_ram_is_migratable(block) || @@ -XXX,XX +XXX,XX @@ struct RAMSrcPageRequest { QSIMPLEQ_ENTRY(RAMSrcPageRequest) next_req; }; +typedef enum RAMStateStage { + RAM_STATE_STAGE_ITERATING, + RAM_STATE_STAGE_COMPLETING, + RAM_STATE_STAGE_COMPLETED, +} RAMStateStage; + /* State of RAM for migration */ struct RAMState { /* @@ -XXX,XX +XXX,XX @@ struct RAMState { uint64_t xbzrle_bytes_prev; /* Are we really using XBZRLE (e.g., after the first round). */ bool xbzrle_started; - /* Are we on the last stage of migration */ - bool last_stage; + /* Migration stage */ + RAMStateStage stage; /* total handled target pages at the beginning of period */ uint64_t target_page_count_prev; @@ -XXX,XX +XXX,XX @@ static int save_xbzrle_page(RAMState *rs, PageSearchStatus *pss, if (!cache_is_cached(XBZRLE.cache, current_addr, generation)) { xbzrle_counters.cache_miss++; - if (!rs->last_stage) { + if (rs->stage == RAM_STATE_STAGE_ITERATING) { if (cache_insert(XBZRLE.cache, current_addr, *current_data, generation) == -1) { return -1; @@ -XXX,XX +XXX,XX @@ static int save_xbzrle_page(RAMState *rs, PageSearchStatus *pss, * Update the cache contents, so that it corresponds to the data * sent, in all cases except where we skip the page. */ - if (!rs->last_stage && encoded_len != 0) { + if (rs->stage == RAM_STATE_STAGE_ITERATING && encoded_len != 0) { memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE); /* * In the case where we couldn't compress, ensure that the caller @@ -XXX,XX +XXX,XX @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, * * Do the same for postcopy due to the same reason. */ - if (!rs->last_stage && !migration_in_postcopy()) { + if (rs->stage == RAM_STATE_STAGE_ITERATING && + !migration_in_postcopy()) { /* * Clear dirty bitmap if needed. This _must_ be called before we * send any of the page in the chunk because we need to make sure @@ -XXX,XX +XXX,XX @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss) if (rs->xbzrle_started && !migration_in_postcopy()) { pages = save_xbzrle_page(rs, pss, &p, current_addr, block, offset); - if (!rs->last_stage) { + if (rs->stage == RAM_STATE_STAGE_ITERATING) { /* Can't send this cached data async, since the cache page * might get updated before it gets to the wire */ @@ -XXX,XX +XXX,XX @@ static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) RAMBlock *block; uint64_t pages = 0; + rs->stage = RAM_STATE_STAGE_ITERATING; + /* * Postcopy is not using xbzrle/compression, so no need for that. * Also, since source are already halted, we don't need to care @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) trace_ram_save_complete(rs->migration_dirty_pages, 0); - rs->last_stage = !migration_in_colo_state(); + if (!migration_in_colo_state()) { + rs->stage = RAM_STATE_STAGE_COMPLETING; + } WITH_RCU_READ_LOCK_GUARD() { if (!migration_in_postcopy()) { @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) ret = rdma_registration_start(f, RAM_CONTROL_FINISH); if (ret < 0) { qemu_file_set_error(f, ret); - return ret; + goto err; } /* try transferring iterative blocks of memory */ @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) } if (pages < 0) { qemu_mutex_unlock(&rs->bitmap_mutex); - return pages; + ret = pages; + goto err; } } qemu_mutex_unlock(&rs->bitmap_mutex); @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); if (ret < 0) { qemu_file_set_error(f, ret); - return ret; + goto err; } } @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) */ ret = multifd_ram_flush_and_sync(f); if (ret < 0) { - return ret; + goto err; } } @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) if (qemu_file_get_error(f)) { Error *local_err = NULL; - int err = qemu_file_get_error_obj(f, &local_err); + ret = -qemu_file_get_error_obj(f, &local_err); error_reportf_err(local_err, "Failed to write bitmap to file: "); - return -err; + goto err; } } @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) trace_ram_save_complete(rs->migration_dirty_pages, 1); - return qemu_fflush(f); + ret = qemu_fflush(f); + +err: + if (!migration_in_colo_state()) { + rs->stage = RAM_STATE_STAGE_COMPLETED; + } + + return ret; } static void ram_state_pending(void *opaque, MigPendingData *pending, @@ -XXX,XX +XXX,XX @@ static SaveVMHandlers savevm_ram_handlers = { .save_postcopy_prepare = ram_save_postcopy_prepare, }; +static bool ram_migration_is_running(void) +{ + return ram_state && ram_state->stage != RAM_STATE_STAGE_COMPLETED; +} + +static void ram_mig_ram_block_set_migratable(RAMBlockNotifier *n, + const RAMBlock *rb) +{ + Error *err = NULL; + + if (!ram_migration_is_running()) { + return; + } + + error_setg(&err, "RAM block '%s' set migratable during precopy.", + rb->idstr); + migrate_error_propagate(migrate_get_current(), err); + migration_cancel(); +} + +static void ram_mig_ram_block_unset_migratable(RAMBlockNotifier *n, + const RAMBlock *rb) +{ + Error *err = NULL; + + if (!ram_migration_is_running()) { + return; + } + + error_setg(&err, "RAM block '%s' unset migratable during precopy.", + rb->idstr); + migrate_error_propagate(migrate_get_current(), err); + migration_cancel(); +} + +static void ram_mig_ram_block_set_idstr(RAMBlockNotifier *n, const RAMBlock *rb) +{ + Error *err = NULL; + + if (!ram_migration_is_running() || migrate_ram_is_ignored(rb)) { + return; + } + + error_setg(&err, "RAM block idstr set during precopy."); + migrate_error_propagate(migrate_get_current(), err); + migration_cancel(); +} + +static void ram_mig_ram_block_removed(RAMBlockNotifier *n, const RAMBlock *rb, + void *host, size_t size, size_t max_size) +{ + Error *err = NULL; + + if (!rb || !ram_migration_is_running() || migrate_ram_is_ignored(rb)) { + return; + } + + error_setg(&err, "RAM block '%s' removed during precopy.", rb->idstr); + migrate_error_propagate(migrate_get_current(), err); + migration_cancel(); +} + static void ram_mig_ram_block_resized(RAMBlockNotifier *n, RAMBlock *rb, size_t new_size) { @@ -XXX,XX +XXX,XX @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, RAMBlock *rb, return; } - if (migration_is_running()) { + if (ram_migration_is_running()) { /* * Precopy code on the source cannot deal with the size of RAM blocks * changing at random points in time - especially after sending the @@ -XXX,XX +XXX,XX @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, RAMBlock *rb, } static RAMBlockNotifier ram_mig_ram_notifier = { + .ram_block_removed = ram_mig_ram_block_removed, + .ram_block_set_migratable = ram_mig_ram_block_set_migratable, + .ram_block_unset_migratable = ram_mig_ram_block_unset_migratable, + .ram_block_set_idstr = ram_mig_ram_block_set_idstr, .ram_block_resized = ram_mig_ram_block_resized, }; -- 2.54.0