382 lines
13 KiB
Diff
382 lines
13 KiB
Diff
From c38aaf5a09a4f06096f9a66ca3a7c22c7e657a4f Mon Sep 17 00:00:00 2001
|
|
From: "plai@redhat.com" <plai@redhat.com>
|
|
Date: Mon, 7 Jan 2019 17:02:14 +0000
|
|
Subject: [PATCH 13/22] migration: discard non-migratable RAMBlocks
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
RH-Author: plai@redhat.com
|
|
Message-id: <1546880543-24860-2-git-send-email-plai@redhat.com>
|
|
Patchwork-id: 83886
|
|
O-Subject: [RHEL8.0 qemu-kvm PATCH v7 01/10] migration: discard non-migratable RAMBlocks
|
|
Bugzilla: 1539285
|
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
|
|
|
|
From: Cédric Le Goater <clg@kaod.org>
|
|
|
|
On the POWER9 processor, the XIVE interrupt controller can control
|
|
interrupt sources using MMIO to trigger events, to EOI or to turn off
|
|
the sources. Priority management and interrupt acknowledgment is also
|
|
controlled by MMIO in the presenter sub-engine.
|
|
|
|
These MMIO regions are exposed to guests in QEMU with a set of 'ram
|
|
device' memory mappings, similarly to VFIO, and the VMAs are populated
|
|
dynamically with the appropriate pages using a fault handler.
|
|
|
|
But, these regions are an issue for migration. We need to discard the
|
|
associated RAMBlocks from the RAM state on the source VM and let the
|
|
destination VM rebuild the memory mappings on the new host in the
|
|
post_load() operation just before resuming the system.
|
|
|
|
To achieve this goal, the following introduces a new RAMBlock flag
|
|
RAM_MIGRATABLE which is updated in the vmstate_register_ram() and
|
|
vmstate_unregister_ram() routines. This flag is then used by the
|
|
migration to identify RAMBlocks to discard on the source. Some checks
|
|
are also performed on the destination to make sure nothing invalid was
|
|
sent.
|
|
|
|
This change impacts the boston, malta and jazz mips boards for which
|
|
migration compatibility is broken.
|
|
|
|
Signed-off-by: Cédric Le Goater <clg@kaod.org>
|
|
Reviewed-by: Juan Quintela <quintela@redhat.com>
|
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
|
(cherry picked from commit b895de502717b83b4e5f089df617cb23530c4d2d)
|
|
Signed-off-by: Paul Lai <plai@redhat.com>
|
|
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
|
---
|
|
exec.c | 38 ++++++++++++++++++++++++++++++++++++++
|
|
include/exec/cpu-common.h | 4 ++++
|
|
migration/postcopy-ram.c | 12 ++++++------
|
|
migration/ram.c | 46 ++++++++++++++++++++++++++++++++++------------
|
|
migration/savevm.c | 2 ++
|
|
5 files changed, 84 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/exec.c b/exec.c
|
|
index 22cc7ef..fff49ba 100644
|
|
--- a/exec.c
|
|
+++ b/exec.c
|
|
@@ -104,6 +104,9 @@ static MemoryRegion io_mem_unassigned;
|
|
* (Set during postcopy)
|
|
*/
|
|
#define RAM_UF_ZEROPAGE (1 << 3)
|
|
+
|
|
+/* RAM can be migrated */
|
|
+#define RAM_MIGRATABLE (1 << 4)
|
|
#endif
|
|
|
|
#ifdef TARGET_PAGE_BITS_VARY
|
|
@@ -1811,6 +1814,21 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb)
|
|
rb->flags |= RAM_UF_ZEROPAGE;
|
|
}
|
|
|
|
+bool qemu_ram_is_migratable(RAMBlock *rb)
|
|
+{
|
|
+ return rb->flags & RAM_MIGRATABLE;
|
|
+}
|
|
+
|
|
+void qemu_ram_set_migratable(RAMBlock *rb)
|
|
+{
|
|
+ rb->flags |= RAM_MIGRATABLE;
|
|
+}
|
|
+
|
|
+void qemu_ram_unset_migratable(RAMBlock *rb)
|
|
+{
|
|
+ rb->flags &= ~RAM_MIGRATABLE;
|
|
+}
|
|
+
|
|
/* Called with iothread lock held. */
|
|
void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev)
|
|
{
|
|
@@ -3754,6 +3772,26 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
|
|
return ret;
|
|
}
|
|
|
|
+int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque)
|
|
+{
|
|
+ RAMBlock *block;
|
|
+ int ret = 0;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ RAMBLOCK_FOREACH(block) {
|
|
+ if (!qemu_ram_is_migratable(block)) {
|
|
+ continue;
|
|
+ }
|
|
+ ret = func(block->idstr, block->host, block->offset,
|
|
+ block->used_length, opaque);
|
|
+ if (ret) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ rcu_read_unlock();
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/*
|
|
* Unmap pages of memory from start to start+length such that
|
|
* they a) read as 0, b) Trigger whatever fault mechanism
|
|
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
|
|
index 24d335f..0b58e26 100644
|
|
--- a/include/exec/cpu-common.h
|
|
+++ b/include/exec/cpu-common.h
|
|
@@ -75,6 +75,9 @@ const char *qemu_ram_get_idstr(RAMBlock *rb);
|
|
bool qemu_ram_is_shared(RAMBlock *rb);
|
|
bool qemu_ram_is_uf_zeroable(RAMBlock *rb);
|
|
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);
|
|
|
|
size_t qemu_ram_pagesize(RAMBlock *block);
|
|
size_t qemu_ram_pagesize_largest(void);
|
|
@@ -119,6 +122,7 @@ typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr,
|
|
ram_addr_t offset, ram_addr_t length, void *opaque);
|
|
|
|
int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque);
|
|
+int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque);
|
|
int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length);
|
|
|
|
#endif
|
|
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
|
|
index b04e903..4b65ff9 100644
|
|
--- a/migration/postcopy-ram.c
|
|
+++ b/migration/postcopy-ram.c
|
|
@@ -264,7 +264,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
|
|
}
|
|
|
|
/* We don't support postcopy with shared RAM yet */
|
|
- if (qemu_ram_foreach_block(test_ramblock_postcopiable, NULL)) {
|
|
+ if (qemu_ram_foreach_migratable_block(test_ramblock_postcopiable, NULL)) {
|
|
goto out;
|
|
}
|
|
|
|
@@ -392,7 +392,7 @@ static int cleanup_range(const char *block_name, void *host_addr,
|
|
*/
|
|
int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages)
|
|
{
|
|
- if (qemu_ram_foreach_block(init_range, NULL)) {
|
|
+ if (qemu_ram_foreach_migratable_block(init_range, NULL)) {
|
|
return -1;
|
|
}
|
|
|
|
@@ -428,7 +428,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis)
|
|
return -1;
|
|
}
|
|
|
|
- if (qemu_ram_foreach_block(cleanup_range, mis)) {
|
|
+ if (qemu_ram_foreach_migratable_block(cleanup_range, mis)) {
|
|
return -1;
|
|
}
|
|
/* Let the fault thread quit */
|
|
@@ -494,7 +494,7 @@ static int nhp_range(const char *block_name, void *host_addr,
|
|
*/
|
|
int postcopy_ram_prepare_discard(MigrationIncomingState *mis)
|
|
{
|
|
- if (qemu_ram_foreach_block(nhp_range, mis)) {
|
|
+ if (qemu_ram_foreach_migratable_block(nhp_range, mis)) {
|
|
return -1;
|
|
}
|
|
|
|
@@ -505,7 +505,7 @@ int postcopy_ram_prepare_discard(MigrationIncomingState *mis)
|
|
|
|
/*
|
|
* Mark the given area of RAM as requiring notification to unwritten areas
|
|
- * Used as a callback on qemu_ram_foreach_block.
|
|
+ * Used as a callback on qemu_ram_foreach_migratable_block.
|
|
* host_addr: Base of area to mark
|
|
* offset: Offset in the whole ram arena
|
|
* length: Length of the section
|
|
@@ -807,7 +807,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
|
|
mis->have_fault_thread = true;
|
|
|
|
/* Mark so that we get notified of accesses to unwritten areas */
|
|
- if (qemu_ram_foreach_block(ram_block_enable_notify, mis)) {
|
|
+ if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) {
|
|
return -1;
|
|
}
|
|
|
|
diff --git a/migration/ram.c b/migration/ram.c
|
|
index bd563b5..04b5df5 100644
|
|
--- a/migration/ram.c
|
|
+++ b/migration/ram.c
|
|
@@ -153,11 +153,16 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
+/* Should be holding either ram_list.mutex, or the RCU lock. */
|
|
+#define RAMBLOCK_FOREACH_MIGRATABLE(block) \
|
|
+ RAMBLOCK_FOREACH(block) \
|
|
+ if (!qemu_ram_is_migratable(block)) {} else
|
|
+
|
|
static void ramblock_recv_map_init(void)
|
|
{
|
|
RAMBlock *rb;
|
|
|
|
- RAMBLOCK_FOREACH(rb) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(rb) {
|
|
assert(!rb->receivedmap);
|
|
rb->receivedmap = bitmap_new(rb->max_length >> qemu_target_page_bits());
|
|
}
|
|
@@ -813,6 +818,10 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb,
|
|
unsigned long *bitmap = rb->bmap;
|
|
unsigned long next;
|
|
|
|
+ if (!qemu_ram_is_migratable(rb)) {
|
|
+ return size;
|
|
+ }
|
|
+
|
|
if (rs->ram_bulk_stage && start > 0) {
|
|
next = start + 1;
|
|
} else {
|
|
@@ -858,7 +867,7 @@ uint64_t ram_pagesize_summary(void)
|
|
RAMBlock *block;
|
|
uint64_t summary = 0;
|
|
|
|
- RAMBLOCK_FOREACH(block) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
summary |= block->page_size;
|
|
}
|
|
|
|
@@ -882,7 +891,7 @@ static void migration_bitmap_sync(RAMState *rs)
|
|
|
|
qemu_mutex_lock(&rs->bitmap_mutex);
|
|
rcu_read_lock();
|
|
- RAMBLOCK_FOREACH(block) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
migration_bitmap_sync_range(rs, block, 0, block->used_length);
|
|
}
|
|
ram_counters.remaining = ram_bytes_remaining();
|
|
@@ -1522,6 +1531,11 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss,
|
|
size_t pagesize_bits =
|
|
qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS;
|
|
|
|
+ if (!qemu_ram_is_migratable(pss->block)) {
|
|
+ error_report("block %s should not be migrated !", pss->block->idstr);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
do {
|
|
/* Check the pages is dirty and if it is send it */
|
|
if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) {
|
|
@@ -1620,7 +1634,7 @@ uint64_t ram_bytes_total(void)
|
|
uint64_t total = 0;
|
|
|
|
rcu_read_lock();
|
|
- RAMBLOCK_FOREACH(block) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
total += block->used_length;
|
|
}
|
|
rcu_read_unlock();
|
|
@@ -1675,7 +1689,7 @@ static void ram_save_cleanup(void *opaque)
|
|
*/
|
|
memory_global_dirty_log_stop();
|
|
|
|
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
g_free(block->bmap);
|
|
block->bmap = NULL;
|
|
g_free(block->unsentmap);
|
|
@@ -1738,7 +1752,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms)
|
|
{
|
|
struct RAMBlock *block;
|
|
|
|
- RAMBLOCK_FOREACH(block) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
unsigned long *bitmap = block->bmap;
|
|
unsigned long range = block->used_length >> TARGET_PAGE_BITS;
|
|
unsigned long run_start = find_next_zero_bit(bitmap, range, 0);
|
|
@@ -1816,7 +1830,7 @@ static int postcopy_each_ram_send_discard(MigrationState *ms)
|
|
struct RAMBlock *block;
|
|
int ret;
|
|
|
|
- RAMBLOCK_FOREACH(block) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
PostcopyDiscardState *pds =
|
|
postcopy_discard_send_init(ms, block->idstr);
|
|
|
|
@@ -2024,7 +2038,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms)
|
|
rs->last_sent_block = NULL;
|
|
rs->last_page = 0;
|
|
|
|
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
unsigned long pages = block->used_length >> TARGET_PAGE_BITS;
|
|
unsigned long *bitmap = block->bmap;
|
|
unsigned long *unsentmap = block->unsentmap;
|
|
@@ -2183,7 +2197,7 @@ static void ram_list_init_bitmaps(void)
|
|
|
|
/* Skip setting bitmap if there is no RAM */
|
|
if (ram_bytes_total()) {
|
|
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
pages = block->max_length >> TARGET_PAGE_BITS;
|
|
block->bmap = bitmap_new(pages);
|
|
bitmap_set(block->bmap, 0, pages);
|
|
@@ -2264,7 +2278,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
|
|
|
|
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
|
|
|
|
- RAMBLOCK_FOREACH(block) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(block) {
|
|
qemu_put_byte(f, strlen(block->idstr));
|
|
qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
|
|
qemu_put_be64(f, block->used_length);
|
|
@@ -2508,6 +2522,11 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags)
|
|
return NULL;
|
|
}
|
|
|
|
+ if (!qemu_ram_is_migratable(block)) {
|
|
+ error_report("block %s should not be migrated !", id);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
return block;
|
|
}
|
|
|
|
@@ -2750,7 +2769,7 @@ static int ram_load_cleanup(void *opaque)
|
|
xbzrle_load_cleanup();
|
|
compress_threads_load_cleanup();
|
|
|
|
- RAMBLOCK_FOREACH(rb) {
|
|
+ RAMBLOCK_FOREACH_MIGRATABLE(rb) {
|
|
g_free(rb->receivedmap);
|
|
rb->receivedmap = NULL;
|
|
}
|
|
@@ -3012,7 +3031,10 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
|
length = qemu_get_be64(f);
|
|
|
|
block = qemu_ram_block_by_name(id);
|
|
- if (block) {
|
|
+ if (block && !qemu_ram_is_migratable(block)) {
|
|
+ error_report("block %s should not be migrated !", id);
|
|
+ ret = -EINVAL;
|
|
+ } else if (block) {
|
|
if (length != block->used_length) {
|
|
Error *local_err = NULL;
|
|
|
|
diff --git a/migration/savevm.c b/migration/savevm.c
|
|
index edb3b94..0bb9446 100644
|
|
--- a/migration/savevm.c
|
|
+++ b/migration/savevm.c
|
|
@@ -2506,11 +2506,13 @@ void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev)
|
|
{
|
|
qemu_ram_set_idstr(mr->ram_block,
|
|
memory_region_name(mr), dev);
|
|
+ qemu_ram_set_migratable(mr->ram_block);
|
|
}
|
|
|
|
void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev)
|
|
{
|
|
qemu_ram_unset_idstr(mr->ram_block);
|
|
+ qemu_ram_unset_migratable(mr->ram_block);
|
|
}
|
|
|
|
void vmstate_register_ram_global(MemoryRegion *mr)
|
|
--
|
|
1.8.3.1
|
|
|