We want to use local migration to update QEMU for running guests. In this case we don't need to migrate external RAM. So, add a capability to ignore such blocks during live migration.
Signed-off-by: Yury Kotov <yury-ko...@yandex-team.ru> --- exec.c | 5 +++++ include/exec/cpu-common.h | 1 + migration/migration.c | 9 +++++++++ migration/migration.h | 1 + migration/ram.c | 37 ++++++++++++++++++++++++++++++++++--- qapi/migration.json | 6 +++++- 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/exec.c b/exec.c index ef2f29d7cb..3c3e42993f 100644 --- a/exec.c +++ b/exec.c @@ -2000,6 +2000,11 @@ void qemu_ram_unset_migratable(RAMBlock *rb) rb->flags &= ~RAM_MIGRATABLE; } +bool qemu_ram_is_external(RAMBlock *rb) +{ + return rb->flags & RAM_EXTERNAL; +} + /* Called with iothread lock held. */ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) { diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 2ad2d6d86b..57e84e5aa4 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -78,6 +78,7 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb); bool qemu_ram_is_migratable(RAMBlock *rb); void qemu_ram_set_migratable(RAMBlock *rb); void qemu_ram_unset_migratable(RAMBlock *rb); +bool qemu_ram_is_external(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); diff --git a/migration/migration.c b/migration/migration.c index ffc4d9e556..9b789d3535 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1979,6 +1979,15 @@ bool migrate_dirty_bitmaps(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS]; } +bool migrate_ignore_external(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_X_IGNORE_EXTERNAL]; +} + bool migrate_use_events(void) { MigrationState *s; diff --git a/migration/migration.h b/migration/migration.h index e413d4d8b6..06691242f3 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -255,6 +255,7 @@ bool migrate_release_ram(void); bool migrate_postcopy_ram(void); bool migrate_zero_blocks(void); bool migrate_dirty_bitmaps(void); +bool migrate_ignore_external(void); bool migrate_auto_converge(void); bool migrate_use_multifd(void); diff --git a/migration/ram.c b/migration/ram.c index 39629254e1..0091a8ae3a 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -166,6 +166,11 @@ out: #undef RAMBLOCK_FOREACH +static bool is_ignored_block(RAMBlock *block) +{ + return migrate_ignore_external() && qemu_ram_is_external(block); +} + static void ramblock_recv_map_init(void) { RAMBlock *rb; @@ -1537,7 +1542,7 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, unsigned long *bitmap = rb->bmap; unsigned long next; - if (!qemu_ram_is_migratable(rb)) { + if (!qemu_ram_is_migratable(rb) || is_ignored_block(rb)) { return size; } @@ -1651,6 +1656,9 @@ static void migration_bitmap_sync(RAMState *rs) qemu_mutex_lock(&rs->bitmap_mutex); rcu_read_lock(); RAMBLOCK_FOREACH_MIGRATABLE(block) { + if (is_ignored_block(block)) { + continue; + } migration_bitmap_sync_range(rs, block, 0, block->used_length); } ram_counters.remaining = ram_bytes_remaining(); @@ -2472,19 +2480,27 @@ void acct_update_position(QEMUFile *f, size_t size, bool zero) } } -uint64_t ram_bytes_total(void) +static uint64_t ram_bytes_total_common(bool skip_ignored) { RAMBlock *block; uint64_t total = 0; rcu_read_lock(); RAMBLOCK_FOREACH_MIGRATABLE(block) { + if (skip_ignored && is_ignored_block(block)) { + continue; + } total += block->used_length; } rcu_read_unlock(); return total; } +uint64_t ram_bytes_total(void) +{ + return ram_bytes_total_common(true); +} + static void xbzrle_load_setup(void) { XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE); @@ -3162,7 +3178,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) rcu_read_lock(); - qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + qemu_put_be64(f, ram_bytes_total_common(false) | RAM_SAVE_FLAG_MEM_SIZE); RAMBLOCK_FOREACH_MIGRATABLE(block) { qemu_put_byte(f, strlen(block->idstr)); @@ -3172,6 +3188,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) qemu_put_be64(f, block->page_size); } qemu_put_be64(f, block->offset); + qemu_put_byte(f, is_ignored_block(block) ? 1 : 0); } rcu_read_unlock(); @@ -4135,13 +4152,27 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } if (version_id >= 5) { ram_addr_t offset; + bool ignored; offset = qemu_get_be64(f); + ignored = qemu_get_byte(f); if (block->offset != offset) { error_report("Mismatched RAM block offset %s " "%" PRId64 "!= %" PRId64, id, offset, (uint64_t)block->offset); ret = -EINVAL; } + if (ignored) { + if (!migrate_ignore_external()) { + error_report("Unexpected ignored RAM block %s: " + "ignore-external capability is " + "disabled", id); + ret = -EINVAL; + } else if (!qemu_ram_is_external(block)) { + error_report("Only external RAM block %s " + "can be ignored", id); + ret = -EINVAL; + } + } } ram_control_load_hook(f, RAM_CONTROL_BLOCK_REG, block->idstr); diff --git a/qapi/migration.json b/qapi/migration.json index 31b589ec26..10bbac87f8 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -406,13 +406,17 @@ # devices (and thus take locks) immediately at the end of migration. # (since 3.0) # +# @x-ignore-external: If enabled, QEMU will not migrate shared memory +# (since 4.0) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', 'block', 'return-path', 'pause-before-switchover', 'x-multifd', - 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate' ] } + 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', + 'x-ignore-external' ] } ## # @MigrationCapabilityStatus: -- 2.20.1