mem + migration pull for 10.2

- Fabiano's patch to fix snapshot crash by rejecting some caps
 - Marco's mapped-ram support on snapshot save/load
 - Steve's cpr maintainers entry update on retirement
 - Peter's coverity fixes
 - Chenyi's tdx fix on hugetlbfs regression
 - Peter's doc update on migrate resume flag
 - Peter's doc update on HMP set parameter for cpr-exec-command's char** parsing
 - Xiaoyao's guest-memfd fix for enabling shmem
 - Arun's fix on error_fatal regression for migration errors
 - Bin's fix on redundant error free for add block failures
 - Markus's cleanup around MigMode sets
 - Peter's two patches (out of loadvm threadify) to cleanup qio read peek process
 - Thomas's vmstate-static-checker update for possible deprecation of argparse use
 - Stefan's fix on windows deadlock by making unassigned MMIOs lockless
 -----BEGIN PGP SIGNATURE-----
 
 iIgEABYKADAWIQS5GE3CDMRX2s990ak7X8zN86vXBgUCaQkZPBIccGV0ZXJ4QHJl
 ZGhhdC5jb20ACgkQO1/MzfOr1wZhTgEA8eCBMpM7PusNSdzzeIygKnIp2A8I70ca
 eIJz3ZM+FiUBAPVDrIZ59EhZA6NPcJb8Ya9OY4lT63F4BxrvN+f+uG4N
 =GUBi
 -----END PGP SIGNATURE-----

Merge tag 'staging-pull-request' of https://gitlab.com/peterx/qemu into staging

mem + migration pull for 10.2

- Fabiano's patch to fix snapshot crash by rejecting some caps
- Marco's mapped-ram support on snapshot save/load
- Steve's cpr maintainers entry update on retirement
- Peter's coverity fixes
- Chenyi's tdx fix on hugetlbfs regression
- Peter's doc update on migrate resume flag
- Peter's doc update on HMP set parameter for cpr-exec-command's char** parsing
- Xiaoyao's guest-memfd fix for enabling shmem
- Arun's fix on error_fatal regression for migration errors
- Bin's fix on redundant error free for add block failures
- Markus's cleanup around MigMode sets
- Peter's two patches (out of loadvm threadify) to cleanup qio read peek process
- Thomas's vmstate-static-checker update for possible deprecation of argparse use
- Stefan's fix on windows deadlock by making unassigned MMIOs lockless

# -----BEGIN PGP SIGNATURE-----
#
# iIgEABYKADAWIQS5GE3CDMRX2s990ak7X8zN86vXBgUCaQkZPBIccGV0ZXJ4QHJl
# ZGhhdC5jb20ACgkQO1/MzfOr1wZhTgEA8eCBMpM7PusNSdzzeIygKnIp2A8I70ca
# eIJz3ZM+FiUBAPVDrIZ59EhZA6NPcJb8Ya9OY4lT63F4BxrvN+f+uG4N
# =GUBi
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 03 Nov 2025 10:06:04 PM CET
# gpg:                using EDDSA key B9184DC20CC457DACF7DD1A93B5FCCCDF3ABD706
# gpg:                issuer "peterx@redhat.com"
# gpg: Good signature from "Peter Xu <xzpeter@gmail.com>" [unknown]
# gpg:                 aka "Peter Xu <peterx@redhat.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: B918 4DC2 0CC4 57DA CF7D  D1A9 3B5F CCCD F3AB D706

* tag 'staging-pull-request' of https://gitlab.com/peterx/qemu: (36 commits)
  migration: Introduce POSTCOPY_DEVICE state
  migration: Make postcopy listen thread joinable
  migration: Respect exit-on-error when migration fails before resuming
  migration: Refactor all incoming cleanup info migration_incoming_destroy()
  migration: Introduce postcopy incoming setup and cleanup functions
  migration: Move postcopy_ram_listen_thread() to postcopy-ram.c
  migration: Do not try to start VM if disk activation fails
  migration: Flush migration channel after sending data of CMD_PACKAGED
  system/physmem: mark io_mem_unassigned lockless
  scripts/vmstate-static-checker: Fix deprecation warnings with latest argparse
  migration: vmsd errp handlers: return bool
  migration/vmstate: stop reporting error number for new _errp APIs
  tmp_emulator: improve and fix use of errp
  migration: vmstate_save_state_v(): fix error path
  migration: Properly wait on G_IO_IN when peeking messages
  io: Add qio_channel_wait_cond() helper
  migration: Put Error **errp parameter last
  migration: Use bitset of MigMode instead of variable arguments
  migration: Use unsigned instead of int for bit set of MigMode
  migration: Don't free the reason after calling migrate_add_blocker
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2025-11-04 16:19:35 +01:00
commit abe80c8ae2
49 changed files with 684 additions and 380 deletions

View file

@ -3114,7 +3114,8 @@ T: git https://gitlab.com/jsnow/qemu.git jobs
T: git https://gitlab.com/vsementsov/qemu.git block
CheckPoint and Restart (CPR)
R: Steve Sistare <steven.sistare@oracle.com>
R: Peter Xu <peterx@redhat.com>
R: Fabiano Rosas <farosas@suse.de>
S: Supported
F: hw/vfio/cpr*
F: include/hw/vfio/vfio-cpr.h

View file

@ -54,6 +54,7 @@ have_fd:
/* Let's do the same as memory-backend-ram,share=on would do. */
ram_flags = RAM_SHARED;
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0;
return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
backend_name, backend->size,

View file

@ -308,22 +308,22 @@ static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
return 0;
}
static int tpm_emulator_stop_tpm(TPMBackend *tb)
static int tpm_emulator_stop_tpm(TPMBackend *tb, Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_res res;
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0,
sizeof(ptm_res), sizeof(res)) < 0) {
error_report("tpm-emulator: Could not stop TPM: %s",
strerror(errno));
error_setg(errp, "tpm-emulator: Could not stop TPM: %s",
strerror(errno));
return -1;
}
res = be32_to_cpu(res);
if (res) {
error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res,
tpm_emulator_strerror(res));
error_setg(errp, "tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res,
tpm_emulator_strerror(res));
return -1;
}
@ -362,12 +362,13 @@ static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu)
static int tpm_emulator_set_buffer_size(TPMBackend *tb,
size_t wanted_size,
size_t *actual_size)
size_t *actual_size,
Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_setbuffersize psbs;
if (tpm_emulator_stop_tpm(tb) < 0) {
if (tpm_emulator_stop_tpm(tb, errp) < 0) {
return -1;
}
@ -376,16 +377,17 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result),
sizeof(psbs.u.resp)) < 0) {
error_report("tpm-emulator: Could not set buffer size: %s",
strerror(errno));
error_setg(errp, "tpm-emulator: Could not set buffer size: %s",
strerror(errno));
return -1;
}
psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
if (psbs.u.resp.tpm_result != 0) {
error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s",
psbs.u.resp.tpm_result,
tpm_emulator_strerror(psbs.u.resp.tpm_result));
error_setg(errp,
"tpm-emulator: TPM result for set buffer size : 0x%x %s",
psbs.u.resp.tpm_result,
tpm_emulator_strerror(psbs.u.resp.tpm_result));
return -1;
}
@ -402,7 +404,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
}
static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
bool is_resume)
bool is_resume, Error **errp)
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_init init = {
@ -413,7 +415,7 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize);
if (buffersize != 0 &&
tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) {
tpm_emulator_set_buffer_size(tb, buffersize, NULL, errp) < 0) {
goto err_exit;
}
@ -424,15 +426,15 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
sizeof(init.u.resp.tpm_result),
sizeof(init)) < 0) {
error_report("tpm-emulator: could not send INIT: %s",
strerror(errno));
error_setg(errp, "tpm-emulator: could not send INIT: %s",
strerror(errno));
goto err_exit;
}
res = be32_to_cpu(init.u.resp.tpm_result);
if (res) {
error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res,
tpm_emulator_strerror(res));
error_setg(errp, "tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res,
tpm_emulator_strerror(res));
goto err_exit;
}
return 0;
@ -441,18 +443,31 @@ err_exit:
return -1;
}
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
static int do_tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize,
Error **errp)
{
/* TPM startup will be done from post_load hook */
if (runstate_check(RUN_STATE_INMIGRATE)) {
if (buffersize != 0) {
return tpm_emulator_set_buffer_size(tb, buffersize, NULL);
return tpm_emulator_set_buffer_size(tb, buffersize, NULL, errp);
}
return 0;
}
return tpm_emulator_startup_tpm_resume(tb, buffersize, false);
return tpm_emulator_startup_tpm_resume(tb, buffersize, false, errp);
}
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
{
Error *local_err = NULL;
int ret = do_tpm_emulator_startup_tpm(tb, buffersize, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
return ret;
}
static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
@ -546,7 +561,7 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
{
size_t actual_size;
if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) {
if (tpm_emulator_set_buffer_size(tb, 0, &actual_size, NULL) < 0) {
return 4096;
}
@ -889,7 +904,7 @@ static int tpm_emulator_set_state_blobs(TPMBackend *tb, Error **errp)
trace_tpm_emulator_set_state_blobs();
if (tpm_emulator_stop_tpm(tb) < 0) {
if (tpm_emulator_stop_tpm(tb, errp) < 0) {
trace_tpm_emulator_set_state_blobs_error("Could not stop TPM");
return -EIO;
}
@ -947,24 +962,22 @@ static void tpm_emulator_vm_state_change(void *opaque, bool running,
/*
* Load the TPM state blobs into the TPM.
*
* Returns negative errno codes in case of error.
*/
static int tpm_emulator_post_load(void *opaque, int version_id, Error **errp)
static bool tpm_emulator_post_load(void *opaque, int version_id, Error **errp)
{
TPMBackend *tb = opaque;
int ret;
ret = tpm_emulator_set_state_blobs(tb, errp);
if (ret < 0) {
return ret;
return false;
}
if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) {
return -EIO;
if (tpm_emulator_startup_tpm_resume(tb, 0, true, errp) < 0) {
return false;
}
return 0;
return true;
}
static const VMStateDescription vmstate_tpm_emulator = {

View file

@ -446,15 +446,15 @@ The functions to do that are inside a vmstate definition, and are called:
Following are the errp variants of these functions.
- ``int (*pre_load_errp)(void *opaque, Error **errp);``
- ``bool (*pre_load_errp)(void *opaque, Error **errp);``
This function is called before we load the state of one device.
- ``int (*post_load_errp)(void *opaque, int version_id, Error **errp);``
- ``bool (*post_load_errp)(void *opaque, int version_id, Error **errp);``
This function is called after we load the state of one device.
- ``int (*pre_save_errp)(void *opaque, Error **errp);``
- ``bool (*pre_save_errp)(void *opaque, Error **errp);``
This function is called before we save the state of one device.

View file

@ -1225,7 +1225,8 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size,
{
VirtIOGPU *g = opaque;
struct virtio_gpu_simple_resource *res;
int i;
Error *err = NULL;
int i, ret;
/* in 2d mode we should never find unprocessed commands here */
assert(QTAILQ_EMPTY(&g->cmdq));
@ -1248,8 +1249,12 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size,
}
qemu_put_be32(f, 0); /* end of list */
return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
&error_fatal);
ret = vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
&err);
if (ret < 0) {
error_report_err(err);
}
return ret;
}
static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
@ -1288,7 +1293,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
Error *err = NULL;
struct virtio_gpu_simple_resource *res;
uint32_t resource_id, pformat;
int i;
int i, ret;
g->hostmem = 0;
@ -1348,9 +1353,11 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
}
/* load & apply scanout state */
vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &error_fatal);
return 0;
ret = vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &err);
if (ret < 0) {
error_report_err(err);
}
return ret;
}
static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size,

View file

@ -841,7 +841,6 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
error_setg(&kvm_nv_migration_blocker,
"Live migration disabled because KVM nested virt is enabled");
if (migrate_add_blocker(&kvm_nv_migration_blocker, errp)) {
error_free(kvm_nv_migration_blocker);
return;
}

View file

@ -921,21 +921,32 @@ const VMStateDescription vmstate_pci_device = {
void pci_device_save(PCIDevice *s, QEMUFile *f)
{
Error *local_err = NULL;
int ret;
/* Clear interrupt status bit: it is implicit
* in irq_state which we are saving.
* This makes us compatible with old devices
* which never set or clear this bit. */
s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_fatal);
ret = vmstate_save_state(f, &vmstate_pci_device, s, NULL, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
/* Restore the interrupt status bit. */
pci_update_irq_status(s);
}
int pci_device_load(PCIDevice *s, QEMUFile *f)
{
Error *local_err = NULL;
int ret;
ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id,
&error_fatal);
&local_err);
if (ret < 0) {
error_report_err(local_err);
}
/* Restore the interrupt status bit. */
pci_update_irq_status(s);
return ret;

View file

@ -1130,13 +1130,26 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f)
static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL, &error_fatal);
Error *local_err = NULL;
int ret;
ret = vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
}
static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &error_fatal);
Error *local_err = NULL;
int ret;
ret = vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
return ret;
}
static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp)

View file

@ -628,10 +628,16 @@ static const VMStateDescription vmstate_spapr_vscsi_req = {
static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq)
{
vscsi_req *req = sreq->hba_private;
Error *local_err = NULL;
int rc;
assert(req->active);
vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL, &error_fatal);
rc = vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL, &local_err);
if (rc < 0) {
error_report_err(local_err);
return;
}
trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
req->cur_desc_offset);
}

View file

@ -977,9 +977,9 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
if (vbasedev->mdev) {
error_setg(&vbasedev->cpr.mdev_blocker,
"CPR does not support vfio mdev %s", vbasedev->name);
if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp,
MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
-1) < 0) {
if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker,
BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_EXEC),
errp) < 0) {
goto hiod_unref_exit;
}
}

View file

@ -158,9 +158,9 @@ bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp)
Error **cpr_blocker = &be->cpr_blocker;
if (!vfio_cpr_supported(be, cpr_blocker)) {
return migrate_add_blocker_modes(cpr_blocker, errp,
MIG_MODE_CPR_TRANSFER,
MIG_MODE_CPR_EXEC, -1) == 0;
return migrate_add_blocker_modes(cpr_blocker,
BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_TRANSFER),
errp);
}
vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be);

View file

@ -176,9 +176,9 @@ bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container,
MIG_MODE_CPR_REBOOT);
if (!vfio_cpr_supported(container, cpr_blocker)) {
return migrate_add_blocker_modes(cpr_blocker, errp,
MIG_MODE_CPR_TRANSFER,
MIG_MODE_CPR_EXEC, -1) == 0;
return migrate_add_blocker_modes(cpr_blocker,
BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_EXEC),
errp) == 0;
}
vfio_cpr_add_kvm_notifier();
@ -187,7 +187,7 @@ bool vfio_legacy_cpr_register_container(VFIOLegacyContainer *container,
migration_add_notifier_modes(&container->cpr.transfer_notifier,
vfio_cpr_fail_notifier,
MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1);
BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_EXEC));
return true;
}

View file

@ -197,8 +197,7 @@ void vfio_cpr_add_kvm_notifier(void)
if (!kvm_close_notifier.notify) {
migration_add_notifier_modes(&kvm_close_notifier,
vfio_cpr_kvm_close_notifier,
MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
-1);
BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_EXEC));
}
}
@ -285,7 +284,7 @@ void vfio_cpr_pci_register_device(VFIOPCIDevice *vdev)
{
migration_add_notifier_modes(&vdev->cpr.transfer_notifier,
vfio_cpr_pci_notifier,
MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC, -1);
BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_EXEC));
}
void vfio_cpr_pci_unregister_device(VFIOPCIDevice *vdev)

View file

@ -345,8 +345,8 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp)
"vfio device with fd=%d needs an id property",
vbasedev->fd);
return migrate_add_blocker_modes(&vbasedev->cpr.id_blocker,
errp, MIG_MODE_CPR_TRANSFER,
-1) == 0;
BIT(MIG_MODE_CPR_TRANSFER),
errp) == 0;
}
}
}

View file

@ -612,15 +612,26 @@ static const VMStateDescription vmstate_virtio_mmio = {
static void virtio_mmio_save_extra_state(DeviceState *opaque, QEMUFile *f)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
Error *local_err = NULL;
int ret;
vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_fatal);
ret = vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
}
static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
Error *local_err = NULL;
int ret;
return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &error_fatal);
ret = vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
return ret;
}
static bool virtio_mmio_has_extra_state(DeviceState *opaque)

View file

@ -187,15 +187,26 @@ static bool virtio_pci_has_extra_state(DeviceState *d)
static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
Error *local_err = NULL;
int ret;
vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_fatal);
ret = vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
}
static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
Error *local_err = NULL;
int ret;
return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &error_fatal);
ret = vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
return ret;
}
static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)

View file

@ -3030,7 +3030,7 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
uint32_t guest_features_lo = (vdev->guest_features & 0xffffffff);
int i;
int i, ret;
Error *local_err = NULL;
if (k->save_config) {
@ -3075,7 +3075,7 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
}
if (vdc->vmsd) {
int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL, &local_err);
ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL, &local_err);
if (ret) {
error_report_err(local_err);
return ret;
@ -3083,7 +3083,11 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
}
/* Subsections */
return vmstate_save_state(f, &vmstate_virtio, vdev, NULL, &error_fatal);
ret = vmstate_save_state(f, &vmstate_virtio, vdev, NULL, &local_err);
if (ret < 0) {
error_report_err(local_err);
}
return ret;
}
/* A wrapper for use as a VMState .put function */

View file

@ -871,6 +871,21 @@ void qio_channel_wake_read(QIOChannel *ioc);
void qio_channel_wait(QIOChannel *ioc,
GIOCondition condition);
/**
* qio_channel_wait_cond:
* @ioc: the channel object
* @condition: the I/O condition to wait for
*
* Block execution from the current thread until
* the condition indicated by @condition becomes
* available.
*
* This will work with/without a coroutine context, by automatically select
* the proper API to wait.
*/
void qio_channel_wait_cond(QIOChannel *ioc,
GIOCondition condition);
/**
* qio_channel_set_aio_fd_handler:
* @ioc: the channel object

View file

@ -16,8 +16,6 @@
#include "qapi/qapi-types-migration.h"
#define MIG_MODE_ALL MIG_MODE__MAX
/**
* @migrate_add_blocker - prevent all modes of migration from proceeding
*
@ -82,16 +80,15 @@ int migrate_add_blocker_normal(Error **reasonp, Error **errp);
*
* @reasonp - address of an error to be returned whenever migration is attempted
*
* @errp - [out] The reason (if any) we cannot block migration right now.
* @modes - the migration modes to be blocked, a bit set of MigMode
*
* @mode - one or more migration modes to be blocked. The list is terminated
* by -1 or MIG_MODE_ALL. For the latter, all modes are blocked.
* @errp - [out] The reason (if any) we cannot block migration right now.
*
* @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set.
*
* *@reasonp is freed and set to NULL if failure is returned.
* On success, the caller must not free *@reasonp before the blocker is removed.
*/
int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...);
int migrate_add_blocker_modes(Error **reasonp, unsigned modes, Error **errp);
#endif

View file

@ -41,7 +41,7 @@ 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);
bool cpr_state_save(MigrationChannel *channel, Error **errp);
int cpr_state_load(MigrationChannel *channel, Error **errp);
void cpr_state_close(void);
struct QIOChannel *cpr_state_ioc(void);
@ -56,7 +56,7 @@ QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
void cpr_exec_init(void);
QEMUFile *cpr_exec_output(Error **errp);
QEMUFile *cpr_exec_input(Error **errp);
void cpr_exec_persist_state(QEMUFile *f);
bool cpr_exec_persist_state(QEMUFile *f, Error **errp);
bool cpr_exec_has_state(void);
void cpr_exec_unpersist_state(void);
void cpr_exec_unpreserve_fds(void);

View file

@ -19,8 +19,6 @@
#define VMSTATE_UINTTL_V(_f, _s, _v) \
VMSTATE_UINT64_V(_f, _s, _v)
#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \
VMSTATE_UINT64_EQUAL_V(_f, _s, _v)
#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v)
#define VMSTATE_UINTTL_2DARRAY_V(_f, _s, _n1, _n2, _v) \
@ -40,8 +38,6 @@
#define VMSTATE_UINTTL_V(_f, _s, _v) \
VMSTATE_UINT32_V(_f, _s, _v)
#define VMSTATE_UINTTL_EQUAL_V(_f, _s, _v) \
VMSTATE_UINT32_EQUAL_V(_f, _s, _v)
#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v)
#define VMSTATE_UINTTL_2DARRAY_V(_f, _s, _n1, _n2, _v) \
@ -53,8 +49,6 @@
#define VMSTATE_UINTTL(_f, _s) \
VMSTATE_UINTTL_V(_f, _s, 0)
#define VMSTATE_UINTTL_EQUAL(_f, _s) \
VMSTATE_UINTTL_EQUAL_V(_f, _s, 0)
#define VMSTATE_UINTTL_ARRAY(_f, _s, _n) \
VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, 0)
#define VMSTATE_UINTTL_2DARRAY(_f, _s, _n1, _n2) \

View file

@ -90,18 +90,18 @@ void migration_add_notifier(NotifierWithReturn *notify,
MigrationNotifyFunc func);
/*
* Same as migration_add_notifier, but applies to be specified @mode.
* Same as migration_add_notifier, but applies to the specified @mode
* instead of MIG_MODE_NORMAL.
*/
void migration_add_notifier_mode(NotifierWithReturn *notify,
MigrationNotifyFunc func, MigMode mode);
/*
* Same as migration_add_notifier, but applies to all @mode in the argument
* list. The list is terminated by -1 or MIG_MODE_ALL. For the latter,
* the notifier is added for all modes.
* Same as migration_add_notifier, but applies to the specified @modes
* (a bitset of MigMode).
*/
void migration_add_notifier_modes(NotifierWithReturn *notify,
MigrationNotifyFunc func, MigMode mode, ...);
MigrationNotifyFunc func, unsigned modes);
/*
* Remove a notifier from all modes.

View file

@ -218,11 +218,11 @@ struct VMStateDescription {
int minimum_version_id;
MigrationPriority priority;
int (*pre_load)(void *opaque);
int (*pre_load_errp)(void *opaque, Error **errp);
bool (*pre_load_errp)(void *opaque, Error **errp);
int (*post_load)(void *opaque, int version_id);
int (*post_load_errp)(void *opaque, int version_id, Error **errp);
bool (*post_load_errp)(void *opaque, int version_id, Error **errp);
int (*pre_save)(void *opaque);
int (*pre_save_errp)(void *opaque, Error **errp);
bool (*pre_save_errp)(void *opaque, Error **errp);
int (*post_save)(void *opaque);
bool (*needed)(void *opaque);
bool (*dev_unplug_pending)(void *opaque);

View file

@ -159,11 +159,7 @@ int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc,
len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
local_nfds, flags, errp);
if (len == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) {
qio_channel_yield(ioc, G_IO_IN);
} else {
qio_channel_wait(ioc, G_IO_IN);
}
qio_channel_wait_cond(ioc, G_IO_IN);
continue;
}
@ -268,11 +264,7 @@ int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc,
nfds, flags, errp);
if (len == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) {
qio_channel_yield(ioc, G_IO_OUT);
} else {
qio_channel_wait(ioc, G_IO_OUT);
}
qio_channel_wait_cond(ioc, G_IO_OUT);
continue;
}
if (len < 0) {
@ -774,6 +766,15 @@ void qio_channel_wait(QIOChannel *ioc,
g_main_context_unref(ctxt);
}
void qio_channel_wait_cond(QIOChannel *ioc,
GIOCondition condition)
{
if (qemu_in_coroutine()) {
qio_channel_yield(ioc, condition);
} else {
qio_channel_wait(ioc, condition);
}
}
static void qio_channel_finalize(Object *obj)
{

View file

@ -30,6 +30,7 @@ qio_channel_block_new(BlockDriverState *bs)
QIOChannelBlock *ioc;
ioc = QIO_CHANNEL_BLOCK(object_new(TYPE_QIO_CHANNEL_BLOCK));
qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE);
bdrv_ref(bs);
ioc->bs = bs;
@ -96,6 +97,47 @@ qio_channel_block_writev(QIOChannel *ioc,
return qiov.size;
}
static ssize_t
qio_channel_block_preadv(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
off_t offset,
Error **errp)
{
QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc);
QEMUIOVector qiov;
int ret;
qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov);
ret = bdrv_readv_vmstate(bioc->bs, &qiov, offset);
if (ret < 0) {
error_setg_errno(errp, -ret, "bdrv_readv_vmstate failed");
return -1;
}
return qiov.size;
}
static ssize_t
qio_channel_block_pwritev(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
off_t offset,
Error **errp)
{
QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc);
QEMUIOVector qiov;
int ret;
qemu_iovec_init_external(&qiov, (struct iovec *)iov, niov);
ret = bdrv_writev_vmstate(bioc->bs, &qiov, offset);
if (ret < 0) {
error_setg_errno(errp, -ret, "bdrv_writev_vmstate failed");
return -1;
}
return qiov.size;
}
static int
qio_channel_block_set_blocking(QIOChannel *ioc,
@ -177,6 +219,8 @@ qio_channel_block_class_init(ObjectClass *klass,
ioc_klass->io_writev = qio_channel_block_writev;
ioc_klass->io_readv = qio_channel_block_readv;
ioc_klass->io_set_blocking = qio_channel_block_set_blocking;
ioc_klass->io_preadv = qio_channel_block_preadv;
ioc_klass->io_pwritev = qio_channel_block_pwritev;
ioc_klass->io_seek = qio_channel_block_seek;
ioc_klass->io_close = qio_channel_block_close;
ioc_klass->io_set_aio_fd_handler = qio_channel_block_set_aio_fd_handler;

View file

@ -135,12 +135,7 @@ int migration_channel_read_peek(QIOChannel *ioc,
break;
}
/* 1ms sleep. */
if (qemu_in_coroutine()) {
qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000);
} else {
g_usleep(1000);
}
qio_channel_wait_cond(ioc, G_IO_IN);
}
return 0;

View file

@ -40,16 +40,22 @@ static QEMUFile *qemu_file_new_fd_output(int fd, const char *name)
return qemu_file_new_output(ioc);
}
void cpr_exec_persist_state(QEMUFile *f)
bool cpr_exec_persist_state(QEMUFile *f, Error **errp)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(qemu_file_get_ioc(f));
/* coverity[leaked_storage] - mfd intentionally kept open across exec() */
int mfd = dup(fioc->fd);
char val[16];
/* Remember mfd in environment for post-exec load */
qemu_clear_cloexec(mfd);
snprintf(val, sizeof(val), "%d", mfd);
g_setenv(CPR_EXEC_STATE_NAME, val, 1);
if (!g_setenv(CPR_EXEC_STATE_NAME, val, 1)) {
error_setg(errp, "Setting env %s = %s failed", CPR_EXEC_STATE_NAME, val);
return false;
}
return true;
}
static int cpr_exec_find_state(void)
@ -146,10 +152,10 @@ static void cpr_exec_cb(void *opaque)
* exec should only fail if argv[0] is bogus, or has a permissions problem,
* or the system is very short on resources.
*/
g_strfreev(argv);
error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
g_clear_pointer(&argv, g_strfreev);
cpr_exec_unpreserve_fds();
error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
error_report_err(error_copy(err));
migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
migrate_set_error(s, err);

View file

@ -176,7 +176,7 @@ bool cpr_is_incoming(void)
return incoming_mode != MIG_MODE_NONE;
}
int cpr_state_save(MigrationChannel *channel, Error **errp)
bool cpr_state_save(MigrationChannel *channel, Error **errp)
{
int ret;
QEMUFile *f;
@ -190,10 +190,10 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
} else if (mode == MIG_MODE_CPR_EXEC) {
f = cpr_exec_output(errp);
} else {
return 0;
return true;
}
if (!f) {
return -1;
return false;
}
qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
@ -202,11 +202,14 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
if (ret) {
qemu_fclose(f);
return ret;
return false;
}
if (migrate_mode() == MIG_MODE_CPR_EXEC) {
cpr_exec_persist_state(f);
if (!cpr_exec_persist_state(f, errp)) {
qemu_fclose(f);
return false;
}
}
/*
@ -217,7 +220,7 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
NULL);
cpr_state_file = f;
return 0;
return true;
}
int cpr_state_load(MigrationChannel *channel, Error **errp)

View file

@ -734,6 +734,12 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
visit_type_bool(v, param, &p->direct_io, &err);
break;
case MIGRATION_PARAMETER_CPR_EXEC_COMMAND: {
/*
* NOTE: g_autofree will only auto g_free() the strv array when
* needed, it will not free the strings within the array. It's
* intentional: when strv is set, the ownership of the strings will
* always be passed to p->cpr_exec_command via QAPI_LIST_APPEND().
*/
g_autofree char **strv = NULL;
g_autoptr(GError) gerr = NULL;
strList **tail = &p->cpr_exec_command;

View file

@ -438,10 +438,15 @@ void migration_incoming_transport_cleanup(MigrationIncomingState *mis)
void migration_incoming_state_destroy(void)
{
struct MigrationIncomingState *mis = migration_incoming_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
PostcopyState ps = postcopy_state_get();
multifd_recv_cleanup();
if (ps != POSTCOPY_INCOMING_NONE) {
postcopy_incoming_cleanup(mis);
}
/*
* RAM state cleanup needs to happen after multifd cleanup, because
* multifd threads can use some of its states (receivedmap).
@ -866,7 +871,6 @@ process_incoming_migration_co(void *opaque)
{
MigrationState *s = migrate_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
PostcopyState ps;
int ret;
Error *local_err = NULL;
@ -883,25 +887,14 @@ process_incoming_migration_co(void *opaque)
trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
ps = postcopy_state_get();
trace_process_incoming_migration_co_end(ret, ps);
if (ps != POSTCOPY_INCOMING_NONE) {
if (ps == POSTCOPY_INCOMING_ADVISE) {
/*
* Where a migration had postcopy enabled (and thus went to advise)
* but managed to complete within the precopy period, we can use
* the normal exit.
*/
postcopy_ram_incoming_cleanup(mis);
} else if (ret >= 0) {
/*
* Postcopy was started, cleanup should happen at the end of the
* postcopy thread.
*/
trace_process_incoming_migration_co_postcopy_end_main();
goto out;
}
/* Else if something went wrong then just fall out of the normal exit */
trace_process_incoming_migration_co_end(ret);
if (mis->have_listen_thread) {
/*
* Postcopy was started, cleanup should happen at the end of the
* postcopy listen thread.
*/
trace_process_incoming_migration_co_postcopy_end_main();
goto out;
}
if (ret < 0) {
@ -933,15 +926,6 @@ fail:
}
exit(EXIT_FAILURE);
} else {
/*
* Report the error here in case that QEMU abruptly exits
* when postcopy is enabled.
*/
WITH_QEMU_LOCK_GUARD(&s->error_mutex) {
error_report_err(s->error);
s->error = NULL;
}
}
out:
/* Pairs with the refcount taken in qmp_migrate_incoming() */
@ -1222,6 +1206,7 @@ bool migration_is_running(void)
switch (s->state) {
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
@ -1243,6 +1228,7 @@ static bool migration_is_active(void)
MigrationState *s = current_migration;
return (s->state == MIGRATION_STATUS_ACTIVE ||
s->state == MIGRATION_STATUS_POSTCOPY_DEVICE ||
s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
}
@ -1365,6 +1351,7 @@ static void fill_source_migration_info(MigrationInfo *info)
break;
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_CANCELLING:
case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_PRE_SWITCHOVER:
case MIGRATION_STATUS_DEVICE:
@ -1418,6 +1405,7 @@ static void fill_destination_migration_info(MigrationInfo *info)
case MIGRATION_STATUS_CANCELLING:
case MIGRATION_STATUS_CANCELLED:
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER:
@ -1672,9 +1660,7 @@ void migration_cancel(void)
}
}
static int get_modes(MigMode mode, va_list ap);
static void add_notifiers(NotifierWithReturn *notify, int modes)
static void add_notifiers(NotifierWithReturn *notify, unsigned modes)
{
for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
if (modes & BIT(mode)) {
@ -1685,15 +1671,8 @@ static void add_notifiers(NotifierWithReturn *notify, int modes)
}
void migration_add_notifier_modes(NotifierWithReturn *notify,
MigrationNotifyFunc func, MigMode mode, ...)
MigrationNotifyFunc func, unsigned modes)
{
int modes;
va_list ap;
va_start(ap, mode);
modes = get_modes(mode, ap);
va_end(ap);
notify->notify = (NotifierWithReturnFunc)func;
add_notifiers(notify, modes);
}
@ -1701,13 +1680,13 @@ void migration_add_notifier_modes(NotifierWithReturn *notify,
void migration_add_notifier_mode(NotifierWithReturn *notify,
MigrationNotifyFunc func, MigMode mode)
{
migration_add_notifier_modes(notify, func, mode, -1);
migration_add_notifier_modes(notify, func, BIT(mode));
}
void migration_add_notifier(NotifierWithReturn *notify,
MigrationNotifyFunc func)
{
migration_add_notifier_modes(notify, func, MIG_MODE_NORMAL, -1);
migration_add_notifier_mode(notify, func, MIG_MODE_NORMAL);
}
void migration_remove_notifier(NotifierWithReturn *notify)
@ -1757,6 +1736,7 @@ bool migration_in_postcopy(void)
MigrationState *s = migrate_get_current();
switch (s->state) {
case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
@ -1858,6 +1838,9 @@ int migrate_init(MigrationState *s, Error **errp)
memset(&mig_stats, 0, sizeof(mig_stats));
migration_reset_vfio_bytes_transferred();
s->postcopy_package_loaded = false;
qemu_event_reset(&s->postcopy_package_loaded_event);
return 0;
}
@ -1876,7 +1859,7 @@ static bool is_busy(Error **reasonp, Error **errp)
return false;
}
static bool is_only_migratable(Error **reasonp, Error **errp, int modes)
static bool is_only_migratable(Error **reasonp, unsigned modes, Error **errp)
{
ERRP_GUARD();
@ -1890,22 +1873,7 @@ static bool is_only_migratable(Error **reasonp, Error **errp, int modes)
return false;
}
static int get_modes(MigMode mode, va_list ap)
{
int modes = 0;
while (mode != -1 && mode != MIG_MODE_ALL) {
assert(mode >= MIG_MODE_NORMAL && mode < MIG_MODE__MAX);
modes |= BIT(mode);
mode = va_arg(ap, MigMode);
}
if (mode == MIG_MODE_ALL) {
modes = BIT(MIG_MODE__MAX) - 1;
}
return modes;
}
static int add_blockers(Error **reasonp, Error **errp, int modes)
static int add_blockers(Error **reasonp, unsigned modes, Error **errp)
{
for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
if (modes & BIT(mode)) {
@ -1918,39 +1886,32 @@ static int add_blockers(Error **reasonp, Error **errp, int modes)
int migrate_add_blocker(Error **reasonp, Error **errp)
{
return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_ALL);
return migrate_add_blocker_modes(reasonp, -1u, errp);
}
int migrate_add_blocker_normal(Error **reasonp, Error **errp)
{
return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_NORMAL, -1);
return migrate_add_blocker_modes(reasonp, BIT(MIG_MODE_NORMAL), errp);
}
int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...)
int migrate_add_blocker_modes(Error **reasonp, unsigned modes, Error **errp)
{
int modes;
va_list ap;
va_start(ap, mode);
modes = get_modes(mode, ap);
va_end(ap);
if (is_only_migratable(reasonp, errp, modes)) {
if (is_only_migratable(reasonp, modes, errp)) {
return -EACCES;
} else if (is_busy(reasonp, errp)) {
return -EBUSY;
}
return add_blockers(reasonp, errp, modes);
return add_blockers(reasonp, modes, errp);
}
int migrate_add_blocker_internal(Error **reasonp, Error **errp)
{
int modes = BIT(MIG_MODE__MAX) - 1;
unsigned modes = BIT(MIG_MODE__MAX) - 1;
if (is_busy(reasonp, errp)) {
return -EBUSY;
}
return add_blockers(reasonp, errp, modes);
return add_blockers(reasonp, modes, errp);
}
void migrate_del_blocker(Error **reasonp)
@ -2195,6 +2156,12 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp)
error_setg(errp, "Cannot use %s with CPR", conflict);
return false;
}
if (s->parameters.mode == MIG_MODE_CPR_EXEC &&
!s->parameters.cpr_exec_command) {
error_setg(errp, "Parameter 'cpr-exec-command' required for cpr-exec");
return false;
}
}
if (migrate_init(s, errp)) {
@ -2301,7 +2268,7 @@ void qmp_migrate(const char *uri, bool has_channels,
return;
}
if (cpr_state_save(cpr_channel, &local_err)) {
if (!cpr_state_save(cpr_channel, &local_err)) {
goto out;
}
@ -2609,6 +2576,11 @@ static void *source_return_path_thread(void *opaque)
tmp32 = ldl_be_p(buf);
trace_source_return_path_thread_pong(tmp32);
qemu_sem_post(&ms->rp_state.rp_pong_acks);
if (tmp32 == QEMU_VM_PING_PACKAGED_LOADED) {
trace_source_return_path_thread_postcopy_package_loaded();
ms->postcopy_package_loaded = true;
qemu_event_set(&ms->postcopy_package_loaded_event);
}
break;
case MIG_RP_MSG_REQ_PAGES:
@ -2854,6 +2826,15 @@ static int postcopy_start(MigrationState *ms, Error **errp)
if (migrate_postcopy_ram()) {
qemu_savevm_send_ping(fb, 3);
}
if (ms->rp_state.rp_thread_created) {
/*
* This ping will tell us that all non-postcopiable device state has been
* successfully loaded and the destination is about to start. When
* response is received, it will trigger transition from POSTCOPY_DEVICE
* to POSTCOPY_ACTIVE state.
*/
qemu_savevm_send_ping(fb, QEMU_VM_PING_PACKAGED_LOADED);
}
qemu_savevm_send_postcopy_run(fb);
@ -2909,8 +2890,13 @@ static int postcopy_start(MigrationState *ms, Error **errp)
*/
migration_rate_set(migrate_max_postcopy_bandwidth());
/* Now, switchover looks all fine, switching to postcopy-active */
/*
* Now, switchover looks all fine, switching to POSTCOPY_DEVICE, or
* directly to POSTCOPY_ACTIVE if there is no return path.
*/
migrate_set_state(&ms->state, MIGRATION_STATUS_DEVICE,
ms->rp_state.rp_thread_created ?
MIGRATION_STATUS_POSTCOPY_DEVICE :
MIGRATION_STATUS_POSTCOPY_ACTIVE);
bql_unlock();
@ -3352,8 +3338,8 @@ static MigThrError migration_detect_error(MigrationState *s)
return postcopy_pause(s);
} else {
/*
* For precopy (or postcopy with error outside IO), we fail
* with no time.
* For precopy (or postcopy with error outside IO, or before dest
* starts), we fail with no time.
*/
migrate_set_state(&s->state, state, MIGRATION_STATUS_FAILED);
trace_migration_thread_file_err();
@ -3488,7 +3474,8 @@ static MigIterateState migration_iteration_run(MigrationState *s)
{
uint64_t must_precopy, can_postcopy, pending_size;
Error *local_err = NULL;
bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE;
bool in_postcopy = (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE ||
s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
bool can_switchover = migration_can_switchover(s);
bool complete_ready;
@ -3504,6 +3491,18 @@ static MigIterateState migration_iteration_run(MigrationState *s)
* POSTCOPY_ACTIVE it means switchover already happened.
*/
complete_ready = !pending_size;
if (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE &&
(s->postcopy_package_loaded || complete_ready)) {
/*
* If package has been loaded, the event is set and we will
* immediatelly transition to POSTCOPY_ACTIVE. If we are ready for
* completion, we need to wait for destination to load the postcopy
* package before actually completing.
*/
qemu_event_wait(&s->postcopy_package_loaded_event);
migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_DEVICE,
MIGRATION_STATUS_POSTCOPY_ACTIVE);
}
} else {
/*
* Exact pending reporting is only needed for precopy. Taking RAM
@ -3551,6 +3550,8 @@ static MigIterateState migration_iteration_run(MigrationState *s)
static void migration_iteration_finish(MigrationState *s)
{
Error *local_err = NULL;
bql_lock();
/*
@ -3574,11 +3575,28 @@ static void migration_iteration_finish(MigrationState *s)
case MIGRATION_STATUS_FAILED:
case MIGRATION_STATUS_CANCELLED:
case MIGRATION_STATUS_CANCELLING:
/*
* Re-activate the block drives if they're inactivated. Note, COLO
* shouldn't use block_active at all, so it should be no-op there.
*/
migration_block_activate(NULL);
if (!migration_block_activate(&local_err)) {
/*
* Re-activate the block drives if they're inactivated.
*
* If it fails (e.g. in case of a split brain, where dest QEMU
* might have taken some of the drive locks and running!), do
* not start VM, instead wait for mgmt to decide the next step.
*
* If dest already started, it means dest QEMU should contain
* all the data it needs and it properly owns all the drive
* locks. Then even if src QEMU got a FAILED in migration, it
* normally should mean we should treat the migration as
* COMPLETED.
*
* NOTE: it's not safe anymore to start VM on src now even if
* dest would release the drive locks. It's because as long as
* dest started running then only dest QEMU's RAM is consistent
* with the shared storage.
*/
error_free(local_err);
break;
}
if (runstate_is_live(s->vm_old_state)) {
if (!runstate_check(RUN_STATE_SHUTDOWN)) {
vm_start();
@ -4139,6 +4157,7 @@ static void migration_instance_finalize(Object *obj)
qemu_sem_destroy(&ms->rp_state.rp_pong_acks);
qemu_sem_destroy(&ms->postcopy_qemufile_src_sem);
error_free(ms->error);
qemu_event_destroy(&ms->postcopy_package_loaded_event);
}
static void migration_instance_init(Object *obj)
@ -4160,6 +4179,7 @@ static void migration_instance_init(Object *obj)
qemu_sem_init(&ms->wait_unplug_sem, 0);
qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0);
qemu_mutex_init(&ms->qemu_file_lock);
qemu_event_init(&ms->postcopy_package_loaded_event, 0);
}
/*

View file

@ -254,6 +254,7 @@ struct MigrationIncomingState {
MigrationIncomingState *migration_incoming_get_current(void);
void migration_incoming_state_destroy(void);
void migration_incoming_transport_cleanup(MigrationIncomingState *mis);
void migration_incoming_qemu_exit(void);
/*
* Functions to work with blocktime context
*/
@ -509,6 +510,9 @@ struct MigrationState {
/* Is this a rdma migration */
bool rdma_migration;
bool postcopy_package_loaded;
QemuEvent postcopy_package_loaded_event;
GSource *hup_source;
};

View file

@ -445,11 +445,37 @@ INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot,
MIGRATION_CAPABILITY_VALIDATE_UUID,
MIGRATION_CAPABILITY_ZERO_COPY_SEND);
/* Snapshot compatibility check list */
static const
INITIALIZE_MIGRATE_CAPS_SET(check_caps_savevm,
MIGRATION_CAPABILITY_MULTIFD,
);
static bool migrate_incoming_started(void)
{
return !!migration_incoming_get_current()->transport_data;
}
bool migrate_can_snapshot(Error **errp)
{
MigrationState *s = migrate_get_current();
int i;
for (i = 0; i < check_caps_savevm.size; i++) {
int incomp_cap = check_caps_savevm.caps[i];
if (s->capabilities[incomp_cap]) {
error_setg(errp,
"Snapshots are not compatible with %s",
MigrationCapability_str(incomp_cap));
return false;
}
}
return true;
}
bool migrate_rdma_caps_check(bool *caps, Error **errp)
{
if (caps[MIGRATION_CAPABILITY_XBZRLE]) {

View file

@ -59,6 +59,7 @@ bool migrate_tls(void);
bool migrate_rdma_caps_check(bool *caps, Error **errp);
bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp);
bool migrate_can_snapshot(Error **errp);
/* parameters */

View file

@ -2077,3 +2077,164 @@ bool postcopy_is_paused(MigrationStatus status)
return status == MIGRATION_STATUS_POSTCOPY_PAUSED ||
status == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP;
}
static void postcopy_listen_thread_bh(void *opaque)
{
MigrationState *s = migrate_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
migration_incoming_state_destroy();
if (mis->state == MIGRATION_STATUS_FAILED && mis->exit_on_error) {
WITH_QEMU_LOCK_GUARD(&s->error_mutex) {
error_report_err(s->error);
s->error = NULL;
}
/*
* If something went wrong then we have a bad state so exit;
* we only could have gotten here if something failed before
* POSTCOPY_INCOMING_RUNNING (for example device load), otherwise
* postcopy migration would pause inside qemu_loadvm_state_main().
* Failing dirty-bitmaps won't fail the whole migration.
*/
exit(1);
}
}
/*
* Triggered by a postcopy_listen command; this thread takes over reading
* the input stream, leaving the main thread free to carry on loading the rest
* of the device state (from RAM).
*/
static void *postcopy_listen_thread(void *opaque)
{
MigrationIncomingState *mis = migration_incoming_get_current();
QEMUFile *f = mis->from_src_file;
int load_res;
MigrationState *migr = migrate_get_current();
Error *local_err = NULL;
object_ref(OBJECT(migr));
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
mis->to_src_file ? MIGRATION_STATUS_POSTCOPY_DEVICE :
MIGRATION_STATUS_POSTCOPY_ACTIVE);
qemu_event_set(&mis->thread_sync_event);
trace_postcopy_ram_listen_thread_start();
rcu_register_thread();
/*
* Because we're a thread and not a coroutine we can't yield
* in qemu_file, and thus we must be blocking now.
*/
qemu_file_set_blocking(f, true, &error_fatal);
/* TODO: sanity check that only postcopiable data will be loaded here */
load_res = qemu_loadvm_state_main(f, mis, &local_err);
/*
* This is tricky, but, mis->from_src_file can change after it
* returns, when postcopy recovery happened. In the future, we may
* want a wrapper for the QEMUFile handle.
*/
f = mis->from_src_file;
/* And non-blocking again so we don't block in any cleanup */
qemu_file_set_blocking(f, false, &error_fatal);
trace_postcopy_ram_listen_thread_exit();
if (load_res < 0) {
qemu_file_set_error(f, load_res);
dirty_bitmap_mig_cancel_incoming();
if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
!migrate_postcopy_ram() && migrate_dirty_bitmaps())
{
error_report("%s: loadvm failed during postcopy: %d: %s. All states "
"are migrated except dirty bitmaps. Some dirty "
"bitmaps may be lost, and present migrated dirty "
"bitmaps are correctly migrated and valid.",
__func__, load_res, error_get_pretty(local_err));
g_clear_pointer(&local_err, error_free);
} else {
/*
* Something went fatally wrong and we have a bad state, QEMU will
* exit depending on if postcopy-exit-on-error is true, but the
* migration cannot be recovered.
*/
error_prepend(&local_err,
"loadvm failed during postcopy: %d: ", load_res);
migrate_set_error(migr, local_err);
g_clear_pointer(&local_err, error_report_err);
migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED);
goto out;
}
}
/*
* This looks good, but it's possible that the device loading in the
* main thread hasn't finished yet, and so we might not be in 'RUN'
* state yet; wait for the end of the main thread.
*/
qemu_event_wait(&mis->main_thread_load_event);
/*
* Device load in the main thread has finished, we should be in
* POSTCOPY_ACTIVE now.
*/
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
MIGRATION_STATUS_COMPLETED);
out:
rcu_unregister_thread();
postcopy_state_set(POSTCOPY_INCOMING_END);
migration_bh_schedule(postcopy_listen_thread_bh, NULL);
object_unref(OBJECT(migr));
return NULL;
}
int postcopy_incoming_setup(MigrationIncomingState *mis, Error **errp)
{
/*
* Sensitise RAM - can now generate requests for blocks that don't exist
* However, at this point the CPU shouldn't be running, and the IO
* shouldn't be doing anything yet so don't actually expect requests
*/
if (migrate_postcopy_ram()) {
if (postcopy_ram_incoming_setup(mis)) {
postcopy_ram_incoming_cleanup(mis);
error_setg(errp, "Failed to setup incoming postcopy RAM blocks");
return -1;
}
}
trace_loadvm_postcopy_handle_listen("after uffd");
if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, errp)) {
return -1;
}
mis->have_listen_thread = true;
postcopy_thread_create(mis, &mis->listen_thread,
MIGRATION_THREAD_DST_LISTEN,
postcopy_listen_thread, QEMU_THREAD_JOINABLE);
return 0;
}
int postcopy_incoming_cleanup(MigrationIncomingState *mis)
{
int rc = 0;
if (mis->have_listen_thread) {
qemu_thread_join(&mis->listen_thread);
mis->have_listen_thread = false;
}
if (migrate_postcopy_ram()) {
rc = postcopy_ram_incoming_cleanup(mis);
}
return rc;
}

View file

@ -199,4 +199,7 @@ bool postcopy_is_paused(MigrationStatus status);
void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid,
RAMBlock *rb);
int postcopy_incoming_setup(MigrationIncomingState *mis, Error **errp);
int postcopy_incoming_cleanup(MigrationIncomingState *mis);
#endif

View file

@ -343,11 +343,7 @@ static ssize_t coroutine_mixed_fn qemu_fill_buffer(QEMUFile *f)
QIO_CHANNEL_READ_FLAG_FD_PRESERVE_BLOCKING,
&local_error);
if (len == QIO_CHANNEL_ERR_BLOCK) {
if (qemu_in_coroutine()) {
qio_channel_yield(f->ioc, G_IO_IN);
} else {
qio_channel_wait(f->ioc, G_IO_IN);
}
qio_channel_wait_cond(f->ioc, G_IO_IN);
}
} while (len == QIO_CHANNEL_ERR_BLOCK);

View file

@ -3630,7 +3630,6 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block,
* determined to be zero, then zap it.
*
* @host: host address for the zero page
* @ch: what the page is filled from. We only support zero
* @size: size of the zero page
*/
void ram_handle_zero(void *host, uint64_t size)
@ -4039,12 +4038,58 @@ static size_t ram_load_multifd_pages(void *host_addr, size_t size,
return size;
}
/**
* handle_zero_mapped_ram: Zero out a range of RAM pages if required during
* mapped-ram load
*
* Zeroing is only performed when restoring from a snapshot (HMP loadvm).
* During incoming migration or -loadvm cli snapshot load, the function is a
* no-op and returns true as in those cases the pages are already guaranteed to
* be zeroed.
*
* Returns: true on success, false on error (with @errp set).
* @from_bit_idx: Starting index relative to the map of the page (inclusive)
* @to_bit_idx: Ending index relative to the map of the page (exclusive)
*/
static bool handle_zero_mapped_ram(RAMBlock *block, unsigned long from_bit_idx,
unsigned long to_bit_idx, Error **errp)
{
ERRP_GUARD();
ram_addr_t offset;
size_t size;
void *host;
/*
* Zeroing is not needed for either -loadvm (RUN_STATE_PRELAUNCH), or
* -incoming (RUN_STATE_INMIGRATE).
*/
if (!runstate_check(RUN_STATE_RESTORE_VM)) {
return true;
}
if (from_bit_idx >= to_bit_idx) {
return true;
}
size = TARGET_PAGE_SIZE * (to_bit_idx - from_bit_idx);
offset = from_bit_idx << TARGET_PAGE_BITS;
host = host_from_ram_block_offset(block, offset);
if (!host) {
error_setg(errp, "zero page outside of ramblock %s range",
block->idstr);
return false;
}
ram_handle_zero(host, size);
return true;
}
static bool read_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block,
long num_pages, unsigned long *bitmap,
Error **errp)
{
ERRP_GUARD();
unsigned long set_bit_idx, clear_bit_idx;
unsigned long set_bit_idx, clear_bit_idx = 0;
ram_addr_t offset;
void *host;
size_t read, unread, size;
@ -4053,6 +4098,12 @@ static bool read_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block,
set_bit_idx < num_pages;
set_bit_idx = find_next_bit(bitmap, num_pages, clear_bit_idx + 1)) {
/* Zero pages */
if (!handle_zero_mapped_ram(block, clear_bit_idx, set_bit_idx, errp)) {
return false;
}
/* Non-zero pages */
clear_bit_idx = find_next_zero_bit(bitmap, num_pages, set_bit_idx + 1);
unread = TARGET_PAGE_SIZE * (clear_bit_idx - set_bit_idx);
@ -4084,6 +4135,11 @@ static bool read_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block,
}
}
/* Handle trailing 0 pages */
if (!handle_zero_mapped_ram(block, clear_bit_idx, num_pages, errp)) {
return false;
}
return true;
err:

View file

@ -1142,6 +1142,7 @@ int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len)
qemu_savevm_command_send(f, MIG_CMD_PACKAGED, 4, (uint8_t *)&tmp);
qemu_put_buffer(f, buf, len);
qemu_fflush(f);
return 0;
}
@ -2087,112 +2088,6 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
return 0;
}
/*
* Triggered by a postcopy_listen command; this thread takes over reading
* the input stream, leaving the main thread free to carry on loading the rest
* of the device state (from RAM).
* (TODO:This could do with being in a postcopy file - but there again it's
* just another input loop, not that postcopy specific)
*/
static void *postcopy_ram_listen_thread(void *opaque)
{
MigrationIncomingState *mis = migration_incoming_get_current();
QEMUFile *f = mis->from_src_file;
int load_res;
MigrationState *migr = migrate_get_current();
Error *local_err = NULL;
object_ref(OBJECT(migr));
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_POSTCOPY_ACTIVE);
qemu_event_set(&mis->thread_sync_event);
trace_postcopy_ram_listen_thread_start();
rcu_register_thread();
/*
* Because we're a thread and not a coroutine we can't yield
* in qemu_file, and thus we must be blocking now.
*/
qemu_file_set_blocking(f, true, &error_fatal);
/* TODO: sanity check that only postcopiable data will be loaded here */
load_res = qemu_loadvm_state_main(f, mis, &local_err);
/*
* This is tricky, but, mis->from_src_file can change after it
* returns, when postcopy recovery happened. In the future, we may
* want a wrapper for the QEMUFile handle.
*/
f = mis->from_src_file;
/* And non-blocking again so we don't block in any cleanup */
qemu_file_set_blocking(f, false, &error_fatal);
trace_postcopy_ram_listen_thread_exit();
if (load_res < 0) {
qemu_file_set_error(f, load_res);
dirty_bitmap_mig_cancel_incoming();
if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
!migrate_postcopy_ram() && migrate_dirty_bitmaps())
{
error_report("%s: loadvm failed during postcopy: %d. All states "
"are migrated except dirty bitmaps. Some dirty "
"bitmaps may be lost, and present migrated dirty "
"bitmaps are correctly migrated and valid.",
__func__, load_res);
load_res = 0; /* prevent further exit() */
} else {
error_prepend(&local_err,
"loadvm failed during postcopy: %d: ", load_res);
migrate_set_error(migr, local_err);
error_report_err(local_err);
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
MIGRATION_STATUS_FAILED);
}
}
if (load_res >= 0) {
/*
* This looks good, but it's possible that the device loading in the
* main thread hasn't finished yet, and so we might not be in 'RUN'
* state yet; wait for the end of the main thread.
*/
qemu_event_wait(&mis->main_thread_load_event);
}
postcopy_ram_incoming_cleanup(mis);
if (load_res < 0) {
/*
* If something went wrong then we have a bad state so exit;
* depending how far we got it might be possible at this point
* to leave the guest running and fire MCEs for pages that never
* arrived as a desperate recovery step.
*/
rcu_unregister_thread();
exit(EXIT_FAILURE);
}
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
MIGRATION_STATUS_COMPLETED);
/*
* If everything has worked fine, then the main thread has waited
* for us to start, and we're the last use of the mis.
* (If something broke then qemu will have to exit anyway since it's
* got a bad migration state).
*/
bql_lock();
migration_incoming_state_destroy();
bql_unlock();
rcu_unregister_thread();
mis->have_listen_thread = false;
postcopy_state_set(POSTCOPY_INCOMING_END);
object_unref(OBJECT(migr));
return NULL;
}
/* After this message we must be able to immediately receive postcopy data */
static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis,
Error **errp)
@ -2218,32 +2113,11 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis,
trace_loadvm_postcopy_handle_listen("after discard");
/*
* Sensitise RAM - can now generate requests for blocks that don't exist
* However, at this point the CPU shouldn't be running, and the IO
* shouldn't be doing anything yet so don't actually expect requests
*/
if (migrate_postcopy_ram()) {
if (postcopy_ram_incoming_setup(mis)) {
postcopy_ram_incoming_cleanup(mis);
error_setg(errp, "Failed to setup incoming postcopy RAM blocks");
return -1;
}
}
int rc = postcopy_incoming_setup(mis, errp);
trace_loadvm_postcopy_handle_listen("after uffd");
if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, errp)) {
return -1;
}
mis->have_listen_thread = true;
postcopy_thread_create(mis, &mis->listen_thread,
MIGRATION_THREAD_DST_LISTEN,
postcopy_ram_listen_thread, QEMU_THREAD_DETACHED);
trace_loadvm_postcopy_handle_listen("return");
return 0;
return rc;
}
static void loadvm_postcopy_handle_run_bh(void *opaque)
@ -2296,6 +2170,11 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis, Error **errp)
return -1;
}
/* We might be already in POSTCOPY_ACTIVE if there is no return path */
if (mis->state == MIGRATION_STATUS_POSTCOPY_DEVICE) {
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_DEVICE,
MIGRATION_STATUS_POSTCOPY_ACTIVE);
}
postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
migration_bh_schedule(loadvm_postcopy_handle_run_bh, mis);
@ -3322,6 +3201,10 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate,
GLOBAL_STATE_CODE();
if (!migrate_can_snapshot(errp)) {
return false;
}
if (migration_is_blocked(errp)) {
return false;
}
@ -3507,6 +3390,10 @@ bool load_snapshot(const char *name, const char *vmstate,
int ret;
MigrationIncomingState *mis = migration_incoming_get_current();
if (!migrate_can_snapshot(errp)) {
return false;
}
if (!bdrv_all_can_snapshot(has_devices, devices, errp)) {
return false;
}

View file

@ -29,6 +29,8 @@
#define QEMU_VM_COMMAND 0x08
#define QEMU_VM_SECTION_FOOTER 0x7e
#define QEMU_VM_PING_PACKAGED_LOADED 0x42
bool qemu_savevm_state_blocked(Error **errp);
void qemu_savevm_non_migratable_list(strList **reasons);
int qemu_savevm_state_prepare(Error **errp);

View file

@ -191,9 +191,10 @@ source_return_path_thread_pong(uint32_t val) "0x%x"
source_return_path_thread_shut(uint32_t val) "0x%x"
source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32
source_return_path_thread_switchover_acked(void) ""
source_return_path_thread_postcopy_package_loaded(void) ""
migration_thread_low_pending(uint64_t pending) "%" PRIu64
migrate_transferred(uint64_t transferred, uint64_t time_spent, uint64_t bandwidth, uint64_t avail_bw, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " switchover_bw %" PRIu64 " max_size %" PRId64
process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d"
process_incoming_migration_co_end(int ret) "ret=%d"
process_incoming_migration_co_postcopy_end_main(void) ""
postcopy_preempt_enabled(bool value) "%d"
migration_precopy_complete(void) ""

View file

@ -154,13 +154,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
return -EINVAL;
}
if (vmsd->pre_load_errp) {
ret = vmsd->pre_load_errp(opaque, errp);
if (ret < 0) {
if (!vmsd->pre_load_errp(opaque, errp)) {
error_prepend(errp, "pre load hook failed for: '%s', "
"version_id: %d, minimum version_id: %d, "
"ret: %d: ", vmsd->name, vmsd->version_id,
vmsd->minimum_version_id, ret);
return ret;
"version_id: %d, minimum version_id: %d: ",
vmsd->name, vmsd->version_id,
vmsd->minimum_version_id);
return -EINVAL;
}
} else if (vmsd->pre_load) {
ret = vmsd->pre_load(opaque);
@ -256,11 +255,11 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
return ret;
}
if (vmsd->post_load_errp) {
ret = vmsd->post_load_errp(opaque, version_id, errp);
if (ret < 0) {
if (!vmsd->post_load_errp(opaque, version_id, errp)) {
error_prepend(errp, "post load hook failed for: %s, version_id: "
"%d, minimum_version: %d, ret: %d: ", vmsd->name,
vmsd->version_id, vmsd->minimum_version_id, ret);
"%d, minimum_version: %d: ", vmsd->name,
vmsd->version_id, vmsd->minimum_version_id);
ret = -EINVAL;
}
} else if (vmsd->post_load) {
ret = vmsd->post_load(opaque, version_id);
@ -438,11 +437,11 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
trace_vmstate_save_state_top(vmsd->name);
if (vmsd->pre_save_errp) {
ret = vmsd->pre_save_errp(opaque, errp);
ret = vmsd->pre_save_errp(opaque, errp) ? 0 : -EINVAL;
trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
if (ret < 0) {
error_prepend(errp, "pre-save for %s failed, ret: %d: ",
vmsd->name, ret);
error_prepend(errp, "pre-save for %s failed: ", vmsd->name);
return ret;
}
} else if (vmsd->pre_save) {
ret = vmsd->pre_save(opaque);

View file

@ -142,6 +142,12 @@
# @postcopy-active: like active, but now in postcopy mode.
# (since 2.5)
#
# @postcopy-device: like postcopy-active, but the destination is still
# loading device state and is not running yet. If migration fails
# during this state, the source side will resume. If there is no
# return-path from destination to source, this state is skipped.
# (since 10.2)
#
# @postcopy-paused: during postcopy but paused. (since 3.0)
#
# @postcopy-recover-setup: setup phase for a postcopy recovery
@ -173,8 +179,8 @@
##
{ 'enum': 'MigrationStatus',
'data': [ 'none', 'setup', 'cancelling', 'cancelled',
'active', 'postcopy-active', 'postcopy-paused',
'postcopy-recover-setup',
'active', 'postcopy-device', 'postcopy-active',
'postcopy-paused', 'postcopy-recover-setup',
'postcopy-recover', 'completed', 'failed', 'colo',
'pre-switchover', 'device', 'wait-unplug' ] }
##
@ -1732,7 +1738,10 @@
# @detach: this argument exists only for compatibility reasons and is
# ignored by QEMU
#
# @resume: resume one paused migration, default "off". (since 3.0)
# @resume: when set, use the new uri/channels specified to resume paused
# postcopy migration. This flag should only be used if the previous
# postcopy migration was interrupted. The command will fail unless
# migration is in "postcopy-paused" state. (default: false, since 3.0)
#
# Features:
#

View file

@ -21,6 +21,7 @@
import argparse
import json
import pathlib
import sys
# Count the number of errors found
@ -382,10 +383,10 @@ def main():
help_text = "Parse JSON-formatted vmstate dumps from QEMU in files SRC and DEST. Checks whether migration from SRC to DEST QEMU versions would break based on the VMSTATE information contained within the JSON outputs. The JSON output is created from a QEMU invocation with the -dump-vmstate parameter and a filename argument to it. Other parameters to QEMU do not matter, except the -M (machine type) parameter."
parser = argparse.ArgumentParser(description=help_text)
parser.add_argument('-s', '--src', type=argparse.FileType('r'),
parser.add_argument('-s', '--src', type=pathlib.Path,
required=True,
help='json dump from src qemu')
parser.add_argument('-d', '--dest', type=argparse.FileType('r'),
parser.add_argument('-d', '--dest', type=pathlib.Path,
required=True,
help='json dump from dest qemu')
parser.add_argument('--reverse', required=False, default=False,
@ -393,10 +394,10 @@ def main():
help='reverse the direction')
args = parser.parse_args()
src_data = json.load(args.src)
dest_data = json.load(args.dest)
args.src.close()
args.dest.close()
with open(args.src, 'r', encoding='utf-8') as src_fh:
src_data = json.load(src_fh)
with open(args.dest, 'r', encoding='utf-8') as dst_fh:
dest_data = json.load(dst_fh)
if args.reverse:
temp = src_data

View file

@ -11,7 +11,7 @@ int migrate_add_blocker_normal(Error **reasonp, Error **errp)
return 0;
}
int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...)
int migrate_add_blocker_modes(Error **reasonp, unsigned modes, Error **errp)
{
return 0;
}

View file

@ -2255,8 +2255,8 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
"Memory region %s uses guest_memfd, "
"which is not supported with CPR.",
memory_region_name(new_block->mr));
migrate_add_blocker_modes(&new_block->cpr_blocker, errp,
MIG_MODE_CPR_TRANSFER, -1);
migrate_add_blocker_modes(&new_block->cpr_blocker,
BIT(MIG_MODE_CPR_TRANSFER), errp);
}
}
@ -3011,6 +3011,9 @@ static void io_mem_init(void)
{
memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
NULL, UINT64_MAX);
/* Trivially thread-safe since memory accesses are rejected */
memory_region_enable_lockless_io(&io_mem_unassigned);
}
AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv)
@ -4462,8 +4465,8 @@ void ram_block_add_cpr_blocker(RAMBlock *rb, Error **errp)
"Memory region %s is not compatible with CPR. share=on is "
"required for memory-backend objects, and aux-ram-share=on is "
"required.", memory_region_name(rb->mr));
migrate_add_blocker_modes(&rb->cpr_blocker, errp, MIG_MODE_CPR_TRANSFER,
-1);
migrate_add_blocker_modes(&rb->cpr_blocker, BIT(MIG_MODE_CPR_TRANSFER),
errp);
}
void ram_block_del_cpr_blocker(RAMBlock *rb)

View file

@ -22,16 +22,14 @@ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RamBlockAttributes,
{ })
static size_t
ram_block_attributes_get_block_size(const RamBlockAttributes *attr)
ram_block_attributes_get_block_size(void)
{
/*
* Because page conversion could be manipulated in the size of at least 4K
* or 4K aligned, Use the host page size as the granularity to track the
* memory attribute.
*/
g_assert(attr && attr->ram_block);
g_assert(attr->ram_block->page_size == qemu_real_host_page_size());
return attr->ram_block->page_size;
return qemu_real_host_page_size();
}
@ -40,7 +38,7 @@ ram_block_attributes_rdm_is_populated(const RamDiscardManager *rdm,
const MemoryRegionSection *section)
{
const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
const size_t block_size = ram_block_attributes_get_block_size(attr);
const size_t block_size = ram_block_attributes_get_block_size();
const uint64_t first_bit = section->offset_within_region / block_size;
const uint64_t last_bit =
first_bit + int128_get64(section->size) / block_size - 1;
@ -81,7 +79,7 @@ ram_block_attributes_for_each_populated_section(const RamBlockAttributes *attr,
{
unsigned long first_bit, last_bit;
uint64_t offset, size;
const size_t block_size = ram_block_attributes_get_block_size(attr);
const size_t block_size = ram_block_attributes_get_block_size();
int ret = 0;
first_bit = section->offset_within_region / block_size;
@ -122,7 +120,7 @@ ram_block_attributes_for_each_discarded_section(const RamBlockAttributes *attr,
{
unsigned long first_bit, last_bit;
uint64_t offset, size;
const size_t block_size = ram_block_attributes_get_block_size(attr);
const size_t block_size = ram_block_attributes_get_block_size();
int ret = 0;
first_bit = section->offset_within_region / block_size;
@ -163,7 +161,7 @@ ram_block_attributes_rdm_get_min_granularity(const RamDiscardManager *rdm,
const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm);
g_assert(mr == attr->ram_block->mr);
return ram_block_attributes_get_block_size(attr);
return ram_block_attributes_get_block_size();
}
static void
@ -265,7 +263,7 @@ ram_block_attributes_is_valid_range(RamBlockAttributes *attr, uint64_t offset,
g_assert(mr);
uint64_t region_size = memory_region_size(mr);
const size_t block_size = ram_block_attributes_get_block_size(attr);
const size_t block_size = ram_block_attributes_get_block_size();
if (!QEMU_IS_ALIGNED(offset, block_size) ||
!QEMU_IS_ALIGNED(size, block_size)) {
@ -322,7 +320,7 @@ int ram_block_attributes_state_change(RamBlockAttributes *attr,
uint64_t offset, uint64_t size,
bool to_discard)
{
const size_t block_size = ram_block_attributes_get_block_size(attr);
const size_t block_size = ram_block_attributes_get_block_size();
const unsigned long first_bit = offset / block_size;
const unsigned long nbits = size / block_size;
const unsigned long last_bit = first_bit + nbits - 1;
@ -392,7 +390,7 @@ int ram_block_attributes_state_change(RamBlockAttributes *attr,
RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block)
{
const int block_size = qemu_real_host_page_size();
const int block_size = ram_block_attributes_get_block_size();
RamBlockAttributes *attr;
MemoryRegion *mr = ram_block->mr;

View file

@ -1661,7 +1661,6 @@ sev_snp_launch_finish(SevCommonState *sev_common)
ret = migrate_add_blocker(&sev_mig_blocker, &local_err);
if (local_err) {
error_report_err(local_err);
error_free(sev_mig_blocker);
exit(1);
}
}

View file

@ -76,7 +76,7 @@ with iotests.FilePath('source.img') as source_img_path, \
while True:
event1 = source_vm.event_wait('MIGRATION')
if event1['data']['status'] == 'postcopy-active':
if event1['data']['status'] in ('postcopy-device', 'postcopy-active'):
# This event is racy, it depends do we really do postcopy or bitmap
# was migrated during downtime (and no data to migrate in postcopy
# phase). So, don't log it.

View file

@ -1316,13 +1316,14 @@ void migration_test_add_precopy(MigrationTestEnv *env)
}
/* ensure new status don't go unnoticed */
assert(MIGRATION_STATUS__MAX == 15);
assert(MIGRATION_STATUS__MAX == 16);
for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) {
switch (i) {
case MIGRATION_STATUS_DEVICE: /* happens too fast */
case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */
case MIGRATION_STATUS_COLO: /* no support in tests */
case MIGRATION_STATUS_POSTCOPY_DEVICE: /* postcopy can't be cancelled */
case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: