From 1632a2017f682a9dc6ce51756b9765af07977873 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:51 -0800 Subject: [PATCH 1/4] migration: cpr_is_incoming Define the cpr_is_incoming helper, to be used in several cpr fix patches. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-2-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- include/migration/cpr.h | 1 + migration/cpr.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 3a6deb7933..7561fc75ad 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -21,6 +21,7 @@ int cpr_find_fd(const char *name, int id); MigMode cpr_get_incoming_mode(void); void cpr_set_incoming_mode(MigMode mode); +bool cpr_is_incoming(void); int cpr_state_save(MigrationChannel *channel, Error **errp); int cpr_state_load(MigrationChannel *channel, Error **errp); diff --git a/migration/cpr.c b/migration/cpr.c index 180faab247..42c46563e5 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -128,6 +128,11 @@ void cpr_set_incoming_mode(MigMode mode) incoming_mode = mode; } +bool cpr_is_incoming(void) +{ + return incoming_mode != MIG_MODE_NONE; +} + int cpr_state_save(MigrationChannel *channel, Error **errp) { int ret; From e56ba1878fefe7babff76ff399311ae5e399c5c5 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:52 -0800 Subject: [PATCH 2/4] pflash: fix cpr During normal migration, new QEMU creates and initializes memory regions, then loads the preserved contents of the region from vmstate. During CPR, memory regions are preserved in place, then the realize method initializes the regions contents, losing the old contents. To fix, skip the re-init during CPR. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-3-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- hw/block/block.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/block/block.c b/hw/block/block.c index 1d405e02bf..2e10611d95 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -12,6 +12,7 @@ #include "system/blockdev.h" #include "system/block-backend.h" #include "hw/block/block.h" +#include "migration/cpr.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" @@ -66,6 +67,10 @@ bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, int ret; g_autofree char *dev_id = NULL; + if (cpr_is_incoming()) { + return true; + } + blk_len = blk_getlength(blk); if (blk_len < 0) { error_setg_errno(errp, -blk_len, From b42f28111e081ab1fd370e92ee78a461027590f0 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:53 -0800 Subject: [PATCH 3/4] hw/loader: fix roms during cpr During normal migration, new QEMU creates and initializes memory regions, then loads the preserved contents of the region from vmstate. During CPR, memory regions are preserved in place, then the realize method initializes the regions contents, losing the old contents. To fix, skip the re-init during CPR. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-4-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- hw/core/loader.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index 332b879a0b..ce6ff1b52e 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -51,6 +51,7 @@ #include "trace.h" #include "hw/hw.h" #include "disas/disas.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "monitor/monitor.h" #include "system/reset.h" @@ -1029,7 +1030,9 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) vmstate_register_ram_global(rom->mr); data = memory_region_get_ram_ptr(rom->mr); - memcpy(data, rom->data, rom->datasize); + if (!cpr_is_incoming()) { + memcpy(data, rom->data, rom->datasize); + } return data; } From 8ffe0623a1f40803feb4280fce13549baa4b0b47 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 7 Mar 2025 12:55:54 -0800 Subject: [PATCH 4/4] hw/qxl: fix cpr During normal migration, new QEMU creates and initializes memory regions, then loads the preserved contents of the region from vmstate. During CPR, memory regions are preserved in place, then the realize method initializes the regions contents, losing the old contents. To fix, skip writes to the qxl memory regions during CPR load. Reported-by: andrey.drobyshev@virtuozzo.com Tested-by: andrey.drobyshev@virtuozzo.com Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-ID: <1741380954-341079-5-git-send-email-steven.sistare@oracle.com> Signed-off-by: Fabiano Rosas --- hw/display/qxl.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 2efdc77e61..da14da5209 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -30,6 +30,7 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "system/runstate.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "trace.h" @@ -333,6 +334,10 @@ static void init_qxl_rom(PCIQXLDevice *d) uint32_t fb; int i, n; + if (cpr_is_incoming()) { + goto skip_init; + } + memset(rom, 0, d->rom_size); rom->magic = cpu_to_le32(QXL_ROM_MAGIC); @@ -390,6 +395,7 @@ static void init_qxl_rom(PCIQXLDevice *d) sizeof(rom->client_monitors_config)); } +skip_init: d->shadow_rom = *rom; d->rom = rom; d->modes = modes; @@ -403,6 +409,9 @@ static void init_qxl_ram(PCIQXLDevice *d) buf = d->vga.vram_ptr; d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); + if (cpr_is_incoming()) { + return; + } d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); d->ram->int_pending = cpu_to_le32(0); d->ram->int_mask = cpu_to_le32(0); @@ -539,6 +548,10 @@ static void interface_set_compression_level(QXLInstance *sin, int level) trace_qxl_interface_set_compression_level(qxl->id, level); qxl->shadow_rom.compression_level = cpu_to_le32(level); + if (cpr_is_incoming()) { + assert(qxl->rom->compression_level == cpu_to_le32(level)); + return; + } qxl->rom->compression_level = cpu_to_le32(level); qxl_rom_set_dirty(qxl); } @@ -997,7 +1010,8 @@ static void interface_set_client_capabilities(QXLInstance *sin, } if (runstate_check(RUN_STATE_INMIGRATE) || - runstate_check(RUN_STATE_POSTMIGRATE)) { + runstate_check(RUN_STATE_POSTMIGRATE) || + cpr_is_incoming()) { return; } @@ -1200,6 +1214,10 @@ static void qxl_reset_state(PCIQXLDevice *d) { QXLRom *rom = d->rom; + if (cpr_is_incoming()) { + return; + } + qxl_check_state(d); d->shadow_rom.update_id = cpu_to_le32(0); *rom = d->shadow_rom; @@ -1370,8 +1388,11 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, memslot.virt_start = virt_start + (guest_start - pci_start); memslot.virt_end = virt_start + (guest_end - pci_start); memslot.addr_delta = memslot.virt_start - delta; - memslot.generation = d->rom->slot_generation = 0; - qxl_rom_set_dirty(d); + if (!cpr_is_incoming()) { + d->rom->slot_generation = 0; + qxl_rom_set_dirty(d); + } + memslot.generation = d->rom->slot_generation; qemu_spice_add_memslot(&d->ssd, &memslot, async); d->guest_slots[slot_id].mr = mr;