Migration Pull request (20231102)

Hi
 
 In this pull request:
 
 - migration reboot mode (steve)
   * I disabled the test because our CI don't like programs using so
     much shared memory.  Searching for a fix.
 - test for postcopy recover (fabiano)
 - MigrateAddress QAPI (het)
 - better return path error handling (peter)
 - traces for downtime (peter)
 - vmstate_register() check for duplicates (juan)
   thomas find better solutions for s390x and ipmi.
   now also works on s390x
 
 Please, apply.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEGJn/jt6/WMzuA0uC9IfvGFhy1yMFAmVDipMACgkQ9IfvGFhy
 1yNYnQ/9E5Cywsoqljqa/9FiKBSII2qMrmkfu6JLKqePnsh5pFZiukbudYRuJCCe
 ZTDEmD0NmKRJbDx2xRU1qx/e6gKJy+gz37KP89Buuh/WwZHPboPYtxQpGvCSiH26
 J3i+1+TgaqmkLzcO35wa8tp6gneQclWeAwKgMvdb4cm2pJEhgWRKI62ccyLzxeve
 UCzFQn60t55ETyVZGnRD4YwdTQvGKH+DPlyTuJOLR3DePuvZd8EdH+ypvB4RLAy7
 3+CuQOxmF5LRXPbpJuAeOsudbmhhHzrO/yL7ZmsiKQTthsJv+SzC1bO94jhQrawZ
 Q7GCii5KpGq0KnRTRKZRGk6XKwxcYRduXMX3R5tXuVmDmCZsjhXzziU8yEdftph8
 5TJdk1o0Gb043EFu81mrsQYS+9yJqe6sy6m3PTJaec54cAty5ln+c17WOvpAOaSV
 +1phe05ftuVPmQ3KWhbIR/tCmavNLwEZxpVIfyaKJx04bFbtQ9gRpRyURORX4KXc
 s4WXvNirQEohxYBnP4TPvA09xBTW3V08pk/wRDwt0YDXnLiqCltOuxD8r05K8K4B
 MkCLcWj0g7he2tBkF60oz1KSIE0oTB81um9AzLIv5F2YSYLaJM5BIcoC437MR2f4
 MOR7drR1fP5GsRu/SeU5BWvhVq3IvdOxR7G2MLNRJJvl7ZtGXDc=
 =uaqL
 -----END PGP SIGNATURE-----

Merge tag 'migration-20231102-pull-request' of https://gitlab.com/juan.quintela/qemu into staging

Migration Pull request (20231102)

Hi

In this pull request:

- migration reboot mode (steve)
  * I disabled the test because our CI don't like programs using so
    much shared memory.  Searching for a fix.
- test for postcopy recover (fabiano)
- MigrateAddress QAPI (het)
- better return path error handling (peter)
- traces for downtime (peter)
- vmstate_register() check for duplicates (juan)
  thomas find better solutions for s390x and ipmi.
  now also works on s390x

Please, apply.

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEGJn/jt6/WMzuA0uC9IfvGFhy1yMFAmVDipMACgkQ9IfvGFhy
# 1yNYnQ/9E5Cywsoqljqa/9FiKBSII2qMrmkfu6JLKqePnsh5pFZiukbudYRuJCCe
# ZTDEmD0NmKRJbDx2xRU1qx/e6gKJy+gz37KP89Buuh/WwZHPboPYtxQpGvCSiH26
# J3i+1+TgaqmkLzcO35wa8tp6gneQclWeAwKgMvdb4cm2pJEhgWRKI62ccyLzxeve
# UCzFQn60t55ETyVZGnRD4YwdTQvGKH+DPlyTuJOLR3DePuvZd8EdH+ypvB4RLAy7
# 3+CuQOxmF5LRXPbpJuAeOsudbmhhHzrO/yL7ZmsiKQTthsJv+SzC1bO94jhQrawZ
# Q7GCii5KpGq0KnRTRKZRGk6XKwxcYRduXMX3R5tXuVmDmCZsjhXzziU8yEdftph8
# 5TJdk1o0Gb043EFu81mrsQYS+9yJqe6sy6m3PTJaec54cAty5ln+c17WOvpAOaSV
# +1phe05ftuVPmQ3KWhbIR/tCmavNLwEZxpVIfyaKJx04bFbtQ9gRpRyURORX4KXc
# s4WXvNirQEohxYBnP4TPvA09xBTW3V08pk/wRDwt0YDXnLiqCltOuxD8r05K8K4B
# MkCLcWj0g7he2tBkF60oz1KSIE0oTB81um9AzLIv5F2YSYLaJM5BIcoC437MR2f4
# MOR7drR1fP5GsRu/SeU5BWvhVq3IvdOxR7G2MLNRJJvl7ZtGXDc=
# =uaqL
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 02 Nov 2023 19:40:03 HKT
# gpg:                using RSA key 1899FF8EDEBF58CCEE034B82F487EF185872D723
# gpg: Good signature from "Juan Quintela <quintela@redhat.com>" [full]
# gpg:                 aka "Juan Quintela <quintela@trasno.org>" [full]
# Primary key fingerprint: 1899 FF8E DEBF 58CC EE03  4B82 F487 EF18 5872 D723

* tag 'migration-20231102-pull-request' of https://gitlab.com/juan.quintela/qemu: (40 commits)
  migration: modify test_multifd_tcp_none() to use new QAPI syntax.
  migration: Implement MigrateChannelList to hmp migration flow.
  migration: Implement MigrateChannelList to qmp migration flow.
  migration: modify migration_channels_and_uri_compatible() for new QAPI syntax
  migration: New migrate and migrate-incoming argument 'channels'
  migration: Convert the file backend to the new QAPI syntax
  migration: convert exec backend to accept MigrateAddress.
  migration: convert rdma backend to accept MigrateAddress
  migration: convert socket backend to accept MigrateAddress
  migration: convert migration 'uri' into 'MigrateAddress'
  migration: New QAPI type 'MigrateAddress'
  migration: Change ram_dirty_bitmap_reload() retval to bool
  tests/migration-test: Add a test for postcopy hangs during RECOVER
  migration: Allow network to fail even during recovery
  migration: Refactor error handling in source return path
  tests/qtest: migration: add reboot mode test
  cpr: reboot mode
  cpr: relax vhost migration blockers
  cpr: relax blockdev migration blockers
  migration: per-mode blockers
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
master
Stefan Hajnoczi 2023-11-03 09:57:32 +08:00
commit 75b7b25d44
61 changed files with 1319 additions and 479 deletions

View File

@ -1781,7 +1781,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
QTAILQ_INSERT_TAIL(&audio_states, s, list);
QLIST_INIT (&s->card_head);
vmstate_register (NULL, 0, &vmstate_audio, s);
vmstate_register_any(NULL, &vmstate_audio, s);
return s;
out:

View File

@ -426,8 +426,7 @@ dbus_vmstate_complete(UserCreatable *uc, Error **errp)
return;
}
if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
&dbus_vmstate, self) < 0) {
if (vmstate_register_any(VMSTATE_IF(self), &dbus_vmstate, self) < 0) {
error_setg(errp, "Failed to register vmstate");
}
}

View File

@ -975,8 +975,7 @@ static void tpm_emulator_inst_init(Object *obj)
qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change,
tpm_emu);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
&vmstate_tpm_emulator, obj);
vmstate_register_any(NULL, &vmstate_tpm_emulator, obj);
}
/*

View File

@ -1369,7 +1369,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
bdrv_get_device_or_node_name(bs));
bdrv_graph_rdunlock_main_loop();
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail;
}

View File

@ -307,7 +307,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
bdrv_get_device_or_node_name(bs));
bdrv_graph_rdunlock_main_loop();
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail;
}

View File

@ -498,7 +498,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
bdrv_get_device_or_node_name(bs));
bdrv_graph_rdunlock_main_loop();
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail_free_bmap;
}

View File

@ -1096,7 +1096,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
error_setg(&s->migration_blocker, "The vhdx format used by node '%s' "
"does not support live migration",
bdrv_get_device_or_node_name(bs));
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail;
}

View File

@ -1386,7 +1386,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
error_setg(&s->migration_blocker, "The vmdk format used by node '%s' "
"does not support live migration",
bdrv_get_device_or_node_name(bs));
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail;
}

View File

@ -452,7 +452,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bdrv_get_device_or_node_name(bs));
bdrv_graph_rdunlock_main_loop();
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail;
}

View File

@ -1268,7 +1268,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
"The vvfat (rw) format used by node '%s' "
"does not support live migration",
bdrv_get_device_or_node_name(bs));
ret = migrate_add_blocker(&s->migration_blocker, errp);
ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
if (ret < 0) {
goto fail;
}

View File

@ -167,13 +167,17 @@ An example (from hw/input/pckbd.c)
}
};
We are declaring the state with name "pckbd".
The ``version_id`` is 3, and the fields are 4 uint8_t in a KBDState structure.
We registered this with:
We are declaring the state with name "pckbd". The ``version_id`` is
3, and there are 4 uint8_t fields in the KBDState structure. We
registered this ``VMSTATEDescription`` with one of the following
functions. The first one will generate a device ``instance_id``
different for each registration. Use the second one if you already
have an id that is different for each instance of the device:
.. code:: c
vmstate_register(NULL, 0, &vmstate_kbd, s);
vmstate_register_any(NULL, &vmstate_kbd, s);
vmstate_register(NULL, instance_id, &vmstate_kbd, s);
For devices that are ``qdev`` based, we can register the device in the class
init function:

View File

@ -673,6 +673,20 @@ const PropertyInfo qdev_prop_multifd_compression = {
.set_default_value = qdev_propinfo_set_default_value_enum,
};
/* --- MigMode --- */
QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int));
const PropertyInfo qdev_prop_mig_mode = {
.name = "MigMode",
.description = "mig_mode values, "
"normal,cpr-reboot",
.enum_table = &MigMode_lookup,
.get = qdev_propinfo_get_enum,
.set = qdev_propinfo_set_enum,
.set_default_value = qdev_propinfo_set_default_value_enum,
};
/* --- Reserved Region --- */
/*

View File

@ -1264,7 +1264,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s,
vga_common_init(&s->vga, OBJECT(dev), &error_fatal);
vga_init(&s->vga, OBJECT(dev), address_space, io, true);
vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
vmstate_register_any(NULL, &vmstate_vga_common, &s->vga);
s->new_depth = 32;
}

View File

@ -64,7 +64,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name)
bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name));
QLIST_INIT(&bus->current_devs);
QSIMPLEQ_INIT(&bus->pending_masters);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus);
vmstate_register_any(NULL, &vmstate_i2c_bus, bus);
return bus;
}

View File

@ -73,7 +73,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp)
ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2);
ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2);
ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum));
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s);
vmstate_register_any(VMSTATE_IF(dev), &vmstate_ide_isa, s);
ide_bus_register_restart_cb(&s->bus);
}

View File

@ -247,7 +247,7 @@ static void adb_bus_realize(BusState *qbus, Error **errp)
adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
adb_bus);
vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus);
vmstate_register_any(NULL, &vmstate_adb_bus, adb_bus);
}
static void adb_bus_unrealize(BusState *qbus)

View File

@ -158,7 +158,7 @@ static void ads7846_realize(SSIPeripheral *d, Error **errp)
ads7846_int_update(s);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_ads7846, s);
vmstate_register_any(NULL, &vmstate_ads7846, s);
}
static void ads7846_class_init(ObjectClass *klass, void *data)

View File

@ -88,6 +88,5 @@ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
}
s->num_buttons = n;
qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
&vmstate_stellaris_gamepad, s);
vmstate_register_any(NULL, &vmstate_stellaris_gamepad, s);
}

View File

@ -335,8 +335,22 @@ static void icp_realize(DeviceState *dev, Error **errp)
return;
}
}
vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp);
/*
* The way that pre_2_10_icp is handling is really, really hacky.
* We used to have here this call:
*
* vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp);
*
* But we were doing:
* pre_2_10_vmstate_register_dummy_icp()
* this vmstate_register()
* pre_2_10_vmstate_unregister_dummy_icp()
*
* So for a short amount of time we had to vmstate entries with
* the same name. This fixes it.
*/
vmstate_replace_hack_for_ppc(NULL, icp->cs->cpu_index,
&vmstate_icp_server, icp);
}
static void icp_unrealize(DeviceState *dev)

View File

@ -453,19 +453,6 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
continue_send(ibe);
}
static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
{
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
if (!qemu_chr_fe_backend_connected(&ibe->chr)) {
error_setg(errp, "IPMI external bmc requires chardev attribute");
return;
}
qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
chr_event, NULL, ibe, NULL, true);
}
static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
{
IPMIBmcExtern *ibe = opaque;
@ -499,12 +486,26 @@ static const VMStateDescription vmstate_ipmi_bmc_extern = {
}
};
static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
{
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
if (!qemu_chr_fe_backend_connected(&ibe->chr)) {
error_setg(errp, "IPMI external bmc requires chardev attribute");
return;
}
qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
chr_event, NULL, ibe, NULL, true);
vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
}
static void ipmi_bmc_extern_init(Object *obj)
{
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
}
static void ipmi_bmc_extern_finalize(Object *obj)

View File

@ -68,6 +68,21 @@ static void isa_ipmi_bt_lower_irq(IPMIBT *ib)
qemu_irq_lower(iib->irq);
}
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
.name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt",
.version_id = 2,
.minimum_version_id = 2,
/*
* Version 1 had messed up the array transfer, it's not even usable
* because it used VMSTATE_VBUFFER_UINT32, but it did not transfer
* the buffer length, so random things would happen.
*/
.fields = (VMStateField[]) {
VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT),
VMSTATE_END_OF_LIST()
}
};
static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
{
Error *err = NULL;
@ -102,30 +117,15 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
}
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
.name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt",
.version_id = 2,
.minimum_version_id = 2,
/*
* Version 1 had messed up the array transfer, it's not even usable
* because it used VMSTATE_VBUFFER_UINT32, but it did not transfer
* the buffer length, so random things would happen.
*/
.fields = (VMStateField[]) {
VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT),
VMSTATE_END_OF_LIST()
}
};
vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, dev);
}
static void isa_ipmi_bt_init(Object *obj)
{
ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib);
}
static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)

View File

@ -67,6 +67,24 @@ static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik)
qemu_irq_lower(iik->irq);
}
static bool vmstate_kcs_before_version2(void *opaque, int version)
{
return version <= 1;
}
static const VMStateDescription vmstate_ISAIPMIKCSDevice = {
.name = TYPE_IPMI_INTERFACE,
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2,
0, vmstate_IPMIKCS, IPMIKCS, 1),
VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS,
IPMIKCS, 2),
VMSTATE_END_OF_LIST()
}
};
static void ipmi_isa_realize(DeviceState *dev, Error **errp)
{
Error *err = NULL;
@ -101,31 +119,6 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp)
qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
}
static bool vmstate_kcs_before_version2(void *opaque, int version)
{
return version <= 1;
}
static const VMStateDescription vmstate_ISAIPMIKCSDevice = {
.name = TYPE_IPMI_INTERFACE,
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2,
0, vmstate_IPMIKCS, IPMIKCS, 1),
VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS,
IPMIKCS, 2),
VMSTATE_END_OF_LIST()
}
};
static void isa_ipmi_kcs_init(Object *obj)
{
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
/*
* Version 1 had an incorrect name, it clashed with the BT
@ -135,6 +128,13 @@ static void isa_ipmi_kcs_init(Object *obj)
vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik);
}
static void isa_ipmi_kcs_init(Object *obj)
{
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
}
static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
{
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);

View File

@ -1883,8 +1883,7 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
s->vmstate->name = qemu_get_queue(s->nic)->model;
vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY,
s->vmstate, s);
vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
}
static void eepro100_instance_init(Object *obj)

View File

@ -321,7 +321,7 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords)
/* Output DO is tristate, read results in 1. */
eeprom->eedo = 1;
logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_eeprom, eeprom);
vmstate_register_any(VMSTATE_IF(dev), &vmstate_eeprom, eeprom);
return eeprom;
}

View File

@ -147,7 +147,7 @@ static void pci_bus_realize(BusState *qbus, Error **errp)
bus->machine_done.notify = pcibus_machine_done;
qemu_add_machine_init_done_notifier(&bus->machine_done);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_pcibus, bus);
vmstate_register_any(NULL, &vmstate_pcibus, bus);
}
static void pcie_bus_realize(BusState *qbus, Error **errp)

View File

@ -143,6 +143,11 @@ static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque)
}
static const VMStateDescription pre_2_10_vmstate_dummy_icp = {
/*
* Hack ahead. We can't have two devices with the same name and
* instance id. So I rename this to pass make check.
* Real help from people who knows the hardware is needed.
*/
.name = "icp/server",
.version_id = 1,
.minimum_version_id = 1,
@ -155,16 +160,32 @@ static const VMStateDescription pre_2_10_vmstate_dummy_icp = {
},
};
/*
* See comment in hw/intc/xics.c:icp_realize()
*
* You have to remove vmstate_replace_hack_for_ppc() when you remove
* the machine types that need the following function.
*/
static void pre_2_10_vmstate_register_dummy_icp(int i)
{
vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp,
(void *)(uintptr_t) i);
}
/*
* See comment in hw/intc/xics.c:icp_realize()
*
* You have to remove vmstate_replace_hack_for_ppc() when you remove
* the machine types that need the following function.
*/
static void pre_2_10_vmstate_unregister_dummy_icp(int i)
{
vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp,
(void *)(uintptr_t) i);
/*
* This used to be:
*
* vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp,
* (void *)(uintptr_t) i);
*/
}
int spapr_max_server_number(SpaprMachineState *spapr)

View File

@ -876,8 +876,7 @@ static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp)
s_nvdimm->hcall_flush_required = true;
}
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
&vmstate_spapr_nvdimm_states, dimm);
vmstate_register_any(NULL, &vmstate_spapr_nvdimm_states, dimm);
}
static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm)

View File

@ -12,6 +12,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
#include "hw/s390x/storage-keys.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-misc-target.h"
@ -432,58 +433,39 @@ static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
return ret;
}
static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
{
S390SKeysState *ss = S390_SKEYS(obj);
return ss->migration_enabled;
}
static SaveVMHandlers savevm_s390_storage_keys = {
.save_state = s390_storage_keys_save,
.load_state = s390_storage_keys_load,
};
static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
Error **errp)
static void s390_skeys_realize(DeviceState *dev, Error **errp)
{
S390SKeysState *ss = S390_SKEYS(obj);
/* Prevent double registration of savevm handler */
if (ss->migration_enabled == value) {
return;
}
ss->migration_enabled = value;
S390SKeysState *ss = S390_SKEYS(dev);
if (ss->migration_enabled) {
register_savevm_live(TYPE_S390_SKEYS, 0, 1,
&savevm_s390_storage_keys, ss);
} else {
unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss);
}
}
static void s390_skeys_instance_init(Object *obj)
{
object_property_add_bool(obj, "migration-enabled",
s390_skeys_get_migration_enabled,
s390_skeys_set_migration_enabled);
object_property_set_bool(obj, "migration-enabled", true, NULL);
}
static Property s390_skeys_props[] = {
DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true),
DEFINE_PROP_END_OF_LIST(),
};
static void s390_skeys_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->hotpluggable = false;
dc->realize = s390_skeys_realize;
device_class_set_props(dc, s390_skeys_props);
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo s390_skeys_info = {
.name = TYPE_S390_SKEYS,
.parent = TYPE_DEVICE,
.instance_init = s390_skeys_instance_init,
.instance_size = sizeof(S390SKeysState),
.class_init = s390_skeys_class_init,
.class_size = sizeof(S390SKeysClass),

View File

@ -13,6 +13,7 @@
#include "qemu/units.h"
#include "migration/qemu-file.h"
#include "migration/register.h"
#include "hw/qdev-properties.h"
#include "hw/s390x/storage-attributes.h"
#include "qemu/error-report.h"
#include "exec/ram_addr.h"
@ -330,41 +331,6 @@ static const TypeInfo qemu_s390_stattrib_info = {
/* Generic abstract object: */
static void s390_stattrib_realize(DeviceState *dev, Error **errp)
{
bool ambiguous = false;
object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous);
if (ambiguous) {
error_setg(errp, "storage_attributes device already exists");
}
}
static void s390_stattrib_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->hotpluggable = false;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->realize = s390_stattrib_realize;
}
static inline bool s390_stattrib_get_migration_enabled(Object *obj,
Error **errp)
{
S390StAttribState *s = S390_STATTRIB(obj);
return s->migration_enabled;
}
static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value,
Error **errp)
{
S390StAttribState *s = S390_STATTRIB(obj);
s->migration_enabled = value;
}
static SaveVMHandlers savevm_s390_stattrib_handlers = {
.save_setup = cmma_save_setup,
.save_live_iterate = cmma_save_iterate,
@ -376,17 +342,39 @@ static SaveVMHandlers savevm_s390_stattrib_handlers = {
.is_active = cmma_active,
};
static void s390_stattrib_realize(DeviceState *dev, Error **errp)
{
bool ambiguous = false;
object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous);
if (ambiguous) {
error_setg(errp, "storage_attributes device already exists");
return;
}
register_savevm_live(TYPE_S390_STATTRIB, 0, 0,
&savevm_s390_stattrib_handlers, dev);
}
static Property s390_stattrib_props[] = {
DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true),
DEFINE_PROP_END_OF_LIST(),
};
static void s390_stattrib_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->hotpluggable = false;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->realize = s390_stattrib_realize;
device_class_set_props(dc, s390_stattrib_props);
}
static void s390_stattrib_instance_init(Object *obj)
{
S390StAttribState *sas = S390_STATTRIB(obj);
register_savevm_live(TYPE_S390_STATTRIB, 0, 0,
&savevm_s390_stattrib_handlers, sas);
object_property_add_bool(obj, "migration-enabled",
s390_stattrib_get_migration_enabled,
s390_stattrib_set_migration_enabled);
object_property_set_bool(obj, "migration-enabled", true, NULL);
sas->migration_cur_gfn = 0;
}

View File

@ -210,7 +210,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
"When external environment supports it (Orchestrator migrates "
"target SCSI device state or use shared storage over network), "
"set 'migratable' property to true to enable migration.");
if (migrate_add_blocker(&vsc->migration_blocker, errp) < 0) {
if (migrate_add_blocker_normal(&vsc->migration_blocker, errp) < 0) {
goto free_virtio;
}
}

View File

@ -181,7 +181,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq)
s->control = TIMER_CTRL_IE;
s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_LEGACY);
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_arm_timer, s);
vmstate_register_any(NULL, &vmstate_arm_timer, s);
return s;
}

View File

@ -1527,7 +1527,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
}
if (hdev->migration_blocker != NULL) {
r = migrate_add_blocker(&hdev->migration_blocker, errp);
r = migrate_add_blocker_normal(&hdev->migration_blocker, errp);
if (r < 0) {
goto fail_busyloop;
}

View File

@ -1119,8 +1119,8 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
host_memory_backend_set_mapped(vmem->memdev, true);
vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem));
if (vmem->early_migration) {
vmstate_register(VMSTATE_IF(vmem), VMSTATE_INSTANCE_ID_ANY,
&vmstate_virtio_mem_device_early, vmem);
vmstate_register_any(VMSTATE_IF(vmem),
&vmstate_virtio_mem_device_early, vmem);
}
qemu_register_reset(virtio_mem_system_reset, vmem);

View File

@ -7,6 +7,7 @@ extern const PropertyInfo qdev_prop_chr;
extern const PropertyInfo qdev_prop_macaddr;
extern const PropertyInfo qdev_prop_reserved_region;
extern const PropertyInfo qdev_prop_multifd_compression;
extern const PropertyInfo qdev_prop_mig_mode;
extern const PropertyInfo qdev_prop_losttickpolicy;
extern const PropertyInfo qdev_prop_blockdev_on_error;
extern const PropertyInfo qdev_prop_bios_chs_trans;
@ -42,6 +43,9 @@ extern const PropertyInfo qdev_prop_cpus390entitlement;
#define DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \
MultiFDCompression)
#define DEFINE_PROP_MIG_MODE(_n, _s, _f, _d) \
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_mig_mode, \
MigMode)
#define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \
LostTickPolicy)

View File

@ -14,8 +14,12 @@
#ifndef MIGRATION_BLOCKER_H
#define MIGRATION_BLOCKER_H
#include "qapi/qapi-types-migration.h"
#define MIG_MODE_ALL MIG_MODE__MAX
/**
* @migrate_add_blocker - prevent migration from proceeding
* @migrate_add_blocker - prevent all modes of migration from proceeding
*
* @reasonp - address of an error to be returned whenever migration is attempted
*
@ -30,8 +34,8 @@
int migrate_add_blocker(Error **reasonp, Error **errp);
/**
* @migrate_add_blocker_internal - prevent migration from proceeding without
* only-migrate implications
* @migrate_add_blocker_internal - prevent all modes of migration from
* proceeding, but ignore -only-migratable
*
* @reasonp - address of an error to be returned whenever migration is attempted
*
@ -50,7 +54,7 @@ int migrate_add_blocker(Error **reasonp, Error **errp);
int migrate_add_blocker_internal(Error **reasonp, Error **errp);
/**
* @migrate_del_blocker - remove a blocking error from migration and free it.
* @migrate_del_blocker - remove a migration blocker from all modes and free it.
*
* @reasonp - address of the error blocking migration
*
@ -58,4 +62,36 @@ int migrate_add_blocker_internal(Error **reasonp, Error **errp);
*/
void migrate_del_blocker(Error **reasonp);
/**
* @migrate_add_blocker_normal - prevent normal migration mode from proceeding
*
* @reasonp - address of an error to be returned whenever migration is attempted
*
* @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, except by
* calling migrate_del_blocker.
*/
int migrate_add_blocker_normal(Error **reasonp, Error **errp);
/**
* @migrate_add_blocker_modes - prevent some modes of migration from proceeding
*
* @reasonp - address of an error to be returned whenever migration is attempted
*
* @errp - [out] The reason (if any) we cannot block migration right now.
*
* @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.
*
* @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, ...);
#endif

View File

@ -15,6 +15,7 @@
#define MIGRATION_MISC_H
#include "qemu/notify.h"
#include "qapi/qapi-types-migration.h"
#include "qapi/qapi-types-net.h"
/* migration/ram.c */

View File

@ -1230,6 +1230,34 @@ static inline int vmstate_register(VMStateIf *obj, int instance_id,
opaque, -1, 0, NULL);
}
/**
* vmstate_replace_hack_for_ppc() - ppc used to abuse vmstate_register
*
* Don't even think about using this function in new code.
*
* Returns: 0 on success, -1 on failure
*/
int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id,
const VMStateDescription *vmsd,
void *opaque);
/**
* vmstate_register_any() - legacy function to register state
* serialisation description and let the function choose the id
*
* New code shouldn't be using this function as QOM-ified devices have
* dc->vmsd to store the serialisation description.
*
* Returns: 0 on success, -1 on failure
*/
static inline int vmstate_register_any(VMStateIf *obj,
const VMStateDescription *vmsd,
void *opaque)
{
return vmstate_register_with_alias_id(obj, VMSTATE_INSTANCE_ID_ANY, vmsd,
opaque, -1, 0, NULL);
}
void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd,
void *opaque);

View File

@ -27,7 +27,6 @@
#include "qemu/cutils.h"
#ifdef WIN32
const char *exec_get_cmd_path(void);
const char *exec_get_cmd_path(void)
{
g_autofree char *detected_path = g_new(char, MAX_PATH);
@ -40,20 +39,51 @@ const char *exec_get_cmd_path(void)
}
#endif
void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp)
/* provides the length of strList */
static int
str_list_length(strList *list)
{
int len = 0;
strList *elem;
for (elem = list; elem != NULL; elem = elem->next) {
len++;
}
return len;
}
static void
init_exec_array(strList *command, char **argv, Error **errp)
{
int i = 0;
strList *lst;
for (lst = command; lst; lst = lst->next) {
argv[i++] = lst->value;
}
argv[i] = NULL;
return;
}
void exec_start_outgoing_migration(MigrationState *s, strList *command,
Error **errp)
{
QIOChannel *ioc;
#ifdef WIN32
const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL };
#else
const char *argv[] = { "/bin/sh", "-c", command, NULL };
#endif
int length = str_list_length(command);
g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
trace_migration_exec_outgoing(command);
ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
O_RDWR,
errp));
init_exec_array(command, argv, errp);
g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
trace_migration_exec_outgoing(new_command);
ioc = QIO_CHANNEL(
qio_channel_command_new_spawn(
(const char * const *) g_steal_pointer(&argv),
O_RDWR,
errp));
if (!ioc) {
return;
}
@ -72,20 +102,22 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc,
return G_SOURCE_REMOVE;
}
void exec_start_incoming_migration(const char *command, Error **errp)
void exec_start_incoming_migration(strList *command, Error **errp)
{
QIOChannel *ioc;
#ifdef WIN32
const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL };
#else
const char *argv[] = { "/bin/sh", "-c", command, NULL };
#endif
int length = str_list_length(command);
g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
trace_migration_exec_incoming(command);
ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
O_RDWR,
errp));
init_exec_array(command, argv, errp);
g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
trace_migration_exec_incoming(new_command);
ioc = QIO_CHANNEL(
qio_channel_command_new_spawn(
(const char * const *) g_steal_pointer(&argv),
O_RDWR,
errp));
if (!ioc) {
return;
}

View File

@ -19,8 +19,12 @@
#ifndef QEMU_MIGRATION_EXEC_H
#define QEMU_MIGRATION_EXEC_H
void exec_start_incoming_migration(const char *host_port, Error **errp);
void exec_start_outgoing_migration(MigrationState *s, const char *host_port,
#ifdef WIN32
const char *exec_get_cmd_path(void);
#endif
void exec_start_incoming_migration(strList *host_port, Error **errp);
void exec_start_outgoing_migration(MigrationState *s, strList *host_port,
Error **errp);
#endif

View File

@ -19,7 +19,7 @@
/* Remove the offset option from @filespec and return it in @offsetp. */
static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
{
char *option = strstr(filespec, OFFSET_OPTION);
int ret;
@ -36,20 +36,16 @@ static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
return 0;
}
void file_start_outgoing_migration(MigrationState *s, const char *filespec,
Error **errp)
void file_start_outgoing_migration(MigrationState *s,
FileMigrationArgs *file_args, Error **errp)
{
g_autofree char *filename = g_strdup(filespec);
g_autoptr(QIOChannelFile) fioc = NULL;
uint64_t offset = 0;
g_autofree char *filename = g_strdup(file_args->filename);
uint64_t offset = file_args->offset;
QIOChannel *ioc;
trace_migration_file_outgoing(filename);
if (file_parse_offset(filename, &offset, errp)) {
return;
}
fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC,
0600, errp);
if (!fioc) {
@ -73,19 +69,15 @@ static gboolean file_accept_incoming_migration(QIOChannel *ioc,
return G_SOURCE_REMOVE;
}
void file_start_incoming_migration(const char *filespec, Error **errp)
void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp)
{
g_autofree char *filename = g_strdup(filespec);
g_autofree char *filename = g_strdup(file_args->filename);
QIOChannelFile *fioc = NULL;
uint64_t offset = 0;
uint64_t offset = file_args->offset;
QIOChannel *ioc;
trace_migration_file_incoming(filename);
if (file_parse_offset(filename, &offset, errp)) {
return;
}
fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp);
if (!fioc) {
return;

View File

@ -7,8 +7,12 @@
#ifndef QEMU_MIGRATION_FILE_H
#define QEMU_MIGRATION_FILE_H
void file_start_incoming_migration(const char *filename, Error **errp);
void file_start_outgoing_migration(MigrationState *s, const char *filename,
Error **errp);
#include "qapi/qapi-types-migration.h"
void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp);
void file_start_outgoing_migration(MigrationState *s,
FileMigrationArgs *file_args, Error **errp);
int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp);
#endif

View File

@ -387,6 +387,11 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%s: %" PRIu64 " MB/s\n",
MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
params->vcpu_dirty_limit);
assert(params->has_mode);
monitor_printf(mon, "%s: %s\n",
MigrationParameter_str(MIGRATION_PARAMETER_MODE),
qapi_enum_lookup(&MigMode_lookup, params->mode));
}
qapi_free_MigrationParameters(params);
@ -446,9 +451,18 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
const char *uri = qdict_get_str(qdict, "uri");
MigrationChannelList *caps = NULL;
g_autoptr(MigrationChannel) channel = NULL;
qmp_migrate_incoming(uri, &err);
if (!migrate_uri_parse(uri, &channel, &err)) {
goto end;
}
QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
qmp_migrate_incoming(NULL, true, caps, &err);
qapi_free_MigrationChannelList(caps);
end:
hmp_handle_error(mon, err);
}
@ -661,6 +675,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
p->has_vcpu_dirty_limit = true;
visit_type_size(v, param, &p->vcpu_dirty_limit, &err);
break;
case MIGRATION_PARAMETER_MODE:
p->has_mode = true;
visit_type_MigMode(v, param, &p->mode, &err);
break;
default:
assert(0);
}
@ -744,6 +762,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
bool resume = qdict_get_try_bool(qdict, "resume", false);
const char *uri = qdict_get_str(qdict, "uri");
Error *err = NULL;
MigrationChannelList *caps = NULL;
g_autoptr(MigrationChannel) channel = NULL;
if (inc) {
warn_report("option '-i' is deprecated;"
@ -755,12 +775,20 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
" use blockdev-mirror with NBD instead");
}
qmp_migrate(uri, !!blk, blk, !!inc, inc,
false, false, true, resume, &err);
if (!migrate_uri_parse(uri, &channel, &err)) {
hmp_handle_error(mon, err);
return;
}
QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
qmp_migrate(NULL, true, caps, !!blk, blk, !!inc, inc,
false, false, true, resume, &err);
if (hmp_handle_error(mon, err)) {
return;
}
qapi_free_MigrationChannelList(caps);
if (!detach) {
HMPMigrationStatus *status;

View File

@ -66,6 +66,7 @@
#include "sysemu/qtest.h"
#include "options.h"
#include "sysemu/dirtylimit.h"
#include "qemu/sockets.h"
static NotifierList migration_state_notifiers =
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
@ -92,31 +93,55 @@ enum mig_rp_message_type {
static MigrationState *current_migration;
static MigrationIncomingState *current_incoming;
static GSList *migration_blockers;
static GSList *migration_blockers[MIG_MODE__MAX];
static bool migration_object_check(MigrationState *ms, Error **errp);
static int migration_maybe_pause(MigrationState *s,
int *current_active_state,
int new_state);
static void migrate_fd_cancel(MigrationState *s);
static int close_return_path_on_source(MigrationState *s);
static bool close_return_path_on_source(MigrationState *s);
static void migration_downtime_start(MigrationState *s)
{
trace_vmstate_downtime_checkpoint("src-downtime-start");
s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
}
static void migration_downtime_end(MigrationState *s)
{
int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
/*
* If downtime already set, should mean that postcopy already set it,
* then that should be the real downtime already.
*/
if (!s->downtime) {
s->downtime = now - s->downtime_start;
}
trace_vmstate_downtime_checkpoint("src-downtime-end");
}
static bool migration_needs_multiple_sockets(void)
{
return migrate_multifd() || migrate_postcopy_preempt();
}
static bool uri_supports_multi_channels(const char *uri)
static bool transport_supports_multi_channels(SocketAddress *saddr)
{
return strstart(uri, "tcp:", NULL) || strstart(uri, "unix:", NULL) ||
strstart(uri, "vsock:", NULL);
return saddr->type == SOCKET_ADDRESS_TYPE_INET ||
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK;
}
static bool
migration_channels_and_uri_compatible(const char *uri, Error **errp)
migration_channels_and_transport_compatible(MigrationAddress *addr,
Error **errp)
{
if (migration_needs_multiple_sockets() &&
!uri_supports_multi_channels(uri)) {
(addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) &&
!transport_supports_multi_channels(&addr->u.socket)) {
error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)");
return false;
}
@ -131,6 +156,15 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp)
return (a > b) - (a < b);
}
int migration_stop_vm(RunState state)
{
int ret = vm_stop_force_state(state);
trace_vmstate_downtime_checkpoint("src-vm-stopped");
return ret;
}
void migration_object_init(void)
{
/* This can only be called once. */
@ -423,25 +457,114 @@ void migrate_add_address(SocketAddress *address)
QAPI_CLONE(SocketAddress, address));
}
static void qemu_start_incoming_migration(const char *uri, Error **errp)
bool migrate_uri_parse(const char *uri, MigrationChannel **channel,
Error **errp)
{
const char *p = NULL;
g_autoptr(MigrationChannel) val = g_new0(MigrationChannel, 1);
g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1);
SocketAddress *saddr = NULL;
InetSocketAddress *isock = &addr->u.rdma;
strList **tail = &addr->u.exec.args;
if (strstart(uri, "exec:", NULL)) {
addr->transport = MIGRATION_ADDRESS_TYPE_EXEC;
#ifdef WIN32
QAPI_LIST_APPEND(tail, g_strdup(exec_get_cmd_path()));
QAPI_LIST_APPEND(tail, g_strdup("/c"));
#else
QAPI_LIST_APPEND(tail, g_strdup("/bin/sh"));
QAPI_LIST_APPEND(tail, g_strdup("-c"));
#endif
QAPI_LIST_APPEND(tail, g_strdup(uri + strlen("exec:")));
} else if (strstart(uri, "rdma:", NULL)) {
if (inet_parse(isock, uri + strlen("rdma:"), errp)) {
qapi_free_InetSocketAddress(isock);
return false;
}
addr->transport = MIGRATION_ADDRESS_TYPE_RDMA;
} else if (strstart(uri, "tcp:", NULL) ||
strstart(uri, "unix:", NULL) ||
strstart(uri, "vsock:", NULL) ||
strstart(uri, "fd:", NULL)) {
addr->transport = MIGRATION_ADDRESS_TYPE_SOCKET;
saddr = socket_parse(uri, errp);
if (!saddr) {
return false;
}
addr->u.socket.type = saddr->type;
addr->u.socket.u = saddr->u;
} else if (strstart(uri, "file:", NULL)) {
addr->transport = MIGRATION_ADDRESS_TYPE_FILE;
addr->u.file.filename = g_strdup(uri + strlen("file:"));
if (file_parse_offset(addr->u.file.filename, &addr->u.file.offset,
errp)) {
return false;
}
} else {
error_setg(errp, "unknown migration protocol: %s", uri);
return false;
}
val->channel_type = MIGRATION_CHANNEL_TYPE_MAIN;
val->addr = g_steal_pointer(&addr);
*channel = g_steal_pointer(&val);
return true;
}
static void qemu_start_incoming_migration(const char *uri, bool has_channels,
MigrationChannelList *channels,
Error **errp)
{
MigrationChannel *channel = NULL;
MigrationAddress *addr = NULL;
MigrationIncomingState *mis = migration_incoming_get_current();
/* URI is not suitable for migration? */
if (!migration_channels_and_uri_compatible(uri, errp)) {
/*
* Having preliminary checks for uri and channel
*/
if (uri && has_channels) {
error_setg(errp, "'uri' and 'channels' arguments are mutually "
"exclusive; exactly one of the two should be present in "
"'migrate-incoming' qmp command ");
return;
} else if (channels) {
/* To verify that Migrate channel list has only item */
if (channels->next) {
error_setg(errp, "Channel list has more than one entries");
return;
}
channel = channels->value;
} else if (uri) {
/* caller uses the old URI syntax */
if (!migrate_uri_parse(uri, &channel, errp)) {
return;
}
} else {
error_setg(errp, "neither 'uri' or 'channels' argument are "
"specified in 'migrate-incoming' qmp command ");
return;
}
addr = channel->addr;
/* transport mechanism not suitable for migration? */
if (!migration_channels_and_transport_compatible(addr, errp)) {
return;
}
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
MIGRATION_STATUS_SETUP);
if (strstart(uri, "tcp:", &p) ||
strstart(uri, "unix:", NULL) ||
strstart(uri, "vsock:", NULL)) {
socket_start_incoming_migration(p ? p : uri, errp);
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
SocketAddress *saddr = &addr->u.socket;
if (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) {
socket_start_incoming_migration(saddr, errp);
} else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) {
fd_start_incoming_migration(saddr->u.fd.str, errp);
}
#ifdef CONFIG_RDMA
} else if (strstart(uri, "rdma:", &p)) {
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
if (migrate_compress()) {
error_setg(errp, "RDMA and compression can't be used together");
return;
@ -454,14 +577,12 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp)
error_setg(errp, "RDMA and multifd can't be used together");
return;
}
rdma_start_incoming_migration(p, errp);
rdma_start_incoming_migration(&addr->u.rdma, errp);
#endif
} else if (strstart(uri, "exec:", &p)) {
exec_start_incoming_migration(p, errp);
} else if (strstart(uri, "fd:", &p)) {
fd_start_incoming_migration(p, errp);
} else if (strstart(uri, "file:", &p)) {
file_start_incoming_migration(p, errp);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) {
exec_start_incoming_migration(addr->u.exec.args, errp);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
file_start_incoming_migration(&addr->u.file, errp);
} else {
error_setg(errp, "unknown migration protocol: %s", uri);
}
@ -472,6 +593,8 @@ static void process_incoming_migration_bh(void *opaque)
Error *local_err = NULL;
MigrationIncomingState *mis = opaque;
trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter");
/* If capability late_block_activate is set:
* Only fire up the block code now if we're going to restart the
* VM, else 'cont' will do it.
@ -497,6 +620,8 @@ static void process_incoming_migration_bh(void *opaque)
*/
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
trace_vmstate_downtime_checkpoint("dst-precopy-bh-announced");
multifd_load_shutdown();
dirty_bitmap_mig_before_vm_start();
@ -514,6 +639,7 @@ static void process_incoming_migration_bh(void *opaque)
} else {
runstate_set(global_state_get_runstate());
}
trace_vmstate_downtime_checkpoint("dst-precopy-bh-vm-started");
/*
* This must happen after any state changes since as soon as an external
* observer sees this event they might start to prod at the VM assuming
@ -548,6 +674,8 @@ process_incoming_migration_co(void *opaque)
ret = qemu_loadvm_state(mis->from_src_file);
mis->loadvm_co = NULL;
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) {
@ -1006,7 +1134,7 @@ static void fill_source_migration_info(MigrationInfo *info)
{
MigrationState *s = migrate_get_current();
int state = qatomic_read(&s->state);
GSList *cur_blocker = migration_blockers;
GSList *cur_blocker = migration_blockers[migrate_mode()];
info->blocked_reasons = NULL;
@ -1356,6 +1484,17 @@ bool migration_in_postcopy(void)
}
}
bool migration_postcopy_is_alive(int state)
{
switch (state) {
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_RECOVER:
return true;
default:
return false;
}
}
bool migration_in_postcopy_after_devices(MigrationState *s)
{
return migration_in_postcopy() && s->postcopy_after_devices;
@ -1438,7 +1577,6 @@ int migrate_init(MigrationState *s, Error **errp)
s->to_dst_file = NULL;
s->state = MIGRATION_STATUS_NONE;
s->rp_state.from_dst_file = NULL;
s->rp_state.error = false;
s->mbps = 0.0;
s->pages_per_second = 0.0;
s->downtime = 0;
@ -1470,44 +1608,112 @@ int migrate_init(MigrationState *s, Error **errp)
return 0;
}
int migrate_add_blocker_internal(Error **reasonp, Error **errp)
static bool is_busy(Error **reasonp, Error **errp)
{
ERRP_GUARD();
/* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */
if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) {
error_propagate_prepend(errp, *reasonp,
"disallowing migration blocker "
"(migration/snapshot in progress) for: ");
*reasonp = NULL;
return -EBUSY;
return true;
}
return false;
}
migration_blockers = g_slist_prepend(migration_blockers, *reasonp);
static bool is_only_migratable(Error **reasonp, Error **errp, int modes)
{
ERRP_GUARD();
if (only_migratable && (modes & BIT(MIG_MODE_NORMAL))) {
error_propagate_prepend(errp, *reasonp,
"disallowing migration blocker "
"(--only-migratable) for: ");
*reasonp = NULL;
return true;
}
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)
{
for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
if (modes & BIT(mode)) {
migration_blockers[mode] = g_slist_prepend(migration_blockers[mode],
*reasonp);
}
}
return 0;
}
int migrate_add_blocker(Error **reasonp, Error **errp)
{
if (only_migratable) {
error_propagate_prepend(errp, *reasonp,
"disallowing migration blocker "
"(--only-migratable) for: ");
*reasonp = NULL;
return -EACCES;
}
return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_ALL);
}
return migrate_add_blocker_internal(reasonp, errp);
int migrate_add_blocker_normal(Error **reasonp, Error **errp)
{
return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_NORMAL, -1);
}
int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...)
{
int modes;
va_list ap;
va_start(ap, mode);
modes = get_modes(mode, ap);
va_end(ap);
if (is_only_migratable(reasonp, errp, modes)) {
return -EACCES;
} else if (is_busy(reasonp, errp)) {
return -EBUSY;
}
return add_blockers(reasonp, errp, modes);
}
int migrate_add_blocker_internal(Error **reasonp, Error **errp)
{
int modes = BIT(MIG_MODE__MAX) - 1;
if (is_busy(reasonp, errp)) {
return -EBUSY;
}
return add_blockers(reasonp, errp, modes);
}
void migrate_del_blocker(Error **reasonp)
{
if (*reasonp) {
migration_blockers = g_slist_remove(migration_blockers, *reasonp);
for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) {
migration_blockers[mode] = g_slist_remove(migration_blockers[mode],
*reasonp);
}
error_free(*reasonp);
*reasonp = NULL;
}
}
void qmp_migrate_incoming(const char *uri, Error **errp)
void qmp_migrate_incoming(const char *uri, bool has_channels,
MigrationChannelList *channels, Error **errp)
{
Error *local_err = NULL;
static bool once = true;
@ -1525,7 +1731,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
return;
}
qemu_start_incoming_migration(uri, &local_err);
qemu_start_incoming_migration(uri, has_channels, channels, &local_err);
if (local_err) {
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
@ -1561,7 +1767,7 @@ void qmp_migrate_recover(const char *uri, Error **errp)
* only re-setup the migration stream and poke existing migration
* to continue using that newly established channel.
*/
qemu_start_incoming_migration(uri, errp);
qemu_start_incoming_migration(uri, false, NULL, errp);
}
void qmp_migrate_pause(Error **errp)
@ -1570,8 +1776,15 @@ void qmp_migrate_pause(Error **errp)
MigrationIncomingState *mis = migration_incoming_get_current();
int ret = 0;
if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
if (migration_postcopy_is_alive(ms->state)) {
/* Source side, during postcopy */
Error *error = NULL;
/* Tell the core migration that we're pausing */
error_setg(&error, "Postcopy migration is paused by the user");
migrate_set_error(ms, error);
error_free(error);
qemu_mutex_lock(&ms->qemu_file_lock);
if (ms->to_dst_file) {
ret = qemu_file_shutdown(ms->to_dst_file);
@ -1580,10 +1793,17 @@ void qmp_migrate_pause(Error **errp)
if (ret) {
error_setg(errp, "Failed to pause source migration");
}
/*
* Kick the migration thread out of any waiting windows (on behalf
* of the rp thread).
*/
migration_rp_kick(ms);
return;
}
if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
if (migration_postcopy_is_alive(mis->state)) {
ret = qemu_file_shutdown(mis->from_src_file);
if (ret) {
error_setg(errp, "Failed to pause destination migration");
@ -1592,17 +1812,19 @@ void qmp_migrate_pause(Error **errp)
}
error_setg(errp, "migrate-pause is currently only supported "
"during postcopy-active state");
"during postcopy-active or postcopy-recover state");
}
bool migration_is_blocked(Error **errp)
{
GSList *blockers = migration_blockers[migrate_mode()];
if (qemu_savevm_state_blocked(errp)) {
return true;
}
if (migration_blockers) {
error_propagate(errp, error_copy(migration_blockers->data));
if (blockers) {
error_propagate(errp, error_copy(blockers->data));
return true;
}
@ -1702,17 +1924,46 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
return true;
}
void qmp_migrate(const char *uri, bool has_blk, bool blk,
void qmp_migrate(const char *uri, bool has_channels,
MigrationChannelList *channels, bool has_blk, bool blk,
bool has_inc, bool inc, bool has_detach, bool detach,
bool has_resume, bool resume, Error **errp)
{
bool resume_requested;
Error *local_err = NULL;
MigrationState *s = migrate_get_current();
const char *p = NULL;
MigrationChannel *channel = NULL;
MigrationAddress *addr = NULL;
/* URI is not suitable for migration? */
if (!migration_channels_and_uri_compatible(uri, errp)) {
/*
* Having preliminary checks for uri and channel
*/
if (uri && has_channels) {
error_setg(errp, "'uri' and 'channels' arguments are mutually "
"exclusive; exactly one of the two should be present in "
"'migrate' qmp command ");
return;
} else if (channels) {
/* To verify that Migrate channel list has only item */
if (channels->next) {
error_setg(errp, "Channel list has more than one entries");
return;
}
channel = channels->value;
} else if (uri) {
/* caller uses the old URI syntax */
if (!migrate_uri_parse(uri, &channel, errp)) {
return;
}
} else {
error_setg(errp, "neither 'uri' or 'channels' argument are "
"specified in 'migrate' qmp command ");
return;
}
addr = channel->addr;
/* transport mechanism not suitable for migration? */
if (!migration_channels_and_transport_compatible(addr, errp)) {
return;
}
@ -1729,20 +1980,23 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
}
}
if (strstart(uri, "tcp:", &p) ||
strstart(uri, "unix:", NULL) ||
strstart(uri, "vsock:", NULL)) {
socket_start_outgoing_migration(s, p ? p : uri, &local_err);
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
SocketAddress *saddr = &addr->u.socket;
if (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) {
socket_start_outgoing_migration(s, saddr, &local_err);
} else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) {
fd_start_outgoing_migration(s, saddr->u.fd.str, &local_err);
}
#ifdef CONFIG_RDMA
} else if (strstart(uri, "rdma:", &p)) {
rdma_start_outgoing_migration(s, p, &local_err);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
rdma_start_outgoing_migration(s, &addr->u.rdma, &local_err);
#endif
} else if (strstart(uri, "exec:", &p)) {
exec_start_outgoing_migration(s, p, &local_err);
} else if (strstart(uri, "fd:", &p)) {
fd_start_outgoing_migration(s, p, &local_err);
} else if (strstart(uri, "file:", &p)) {
file_start_outgoing_migration(s, p, &local_err);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) {
exec_start_outgoing_migration(s, addr->u.exec.args, &local_err);
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
file_start_outgoing_migration(s, &addr->u.file, &local_err);
} else {
error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri",
"a valid migration protocol");
@ -1777,19 +2031,21 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp)
qemu_sem_post(&s->pause_sem);
}
/* migration thread support */
/*
* Something bad happened to the RP stream, mark an error
* The caller shall print or trace something to indicate why
*/
static void mark_source_rp_bad(MigrationState *s)
int migration_rp_wait(MigrationState *s)
{
s->rp_state.error = true;
}
/* If migration has failure already, ignore the wait */
if (migrate_has_error(s)) {
return -1;
}
void migration_rp_wait(MigrationState *s)
{
qemu_sem_wait(&s->rp_state.rp_sem);
/* After wait, double check that there's no failure */
if (migrate_has_error(s)) {
return -1;
}
return 0;
}
void migration_rp_kick(MigrationState *s)
@ -1817,8 +2073,9 @@ static struct rp_cmd_args {
* We're allowed to send more than requested (e.g. to round to our page size)
* and we don't need to send pages that have already been sent.
*/
static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname,
ram_addr_t start, size_t len)
static void
migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname,
ram_addr_t start, size_t len, Error **errp)
{
long our_host_ps = qemu_real_host_page_size();
@ -1830,38 +2087,37 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname,
*/
if (!QEMU_IS_ALIGNED(start, our_host_ps) ||
!QEMU_IS_ALIGNED(len, our_host_ps)) {
error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT
" len: %zd", __func__, start, len);
mark_source_rp_bad(ms);
error_setg(errp, "MIG_RP_MSG_REQ_PAGES: Misaligned page request, start:"
RAM_ADDR_FMT " len: %zd", start, len);
return;
}
if (ram_save_queue_pages(rbname, start, len)) {
mark_source_rp_bad(ms);
}
ram_save_queue_pages(rbname, start, len, errp);
}
static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name)
static bool migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name,
Error **errp)
{
RAMBlock *block = qemu_ram_block_by_name(block_name);
if (!block) {
error_report("%s: invalid block name '%s'", __func__, block_name);
return -EINVAL;
error_setg(errp, "MIG_RP_MSG_RECV_BITMAP has invalid block name '%s'",
block_name);
return false;
}
/* Fetch the received bitmap and refresh the dirty bitmap */
return ram_dirty_bitmap_reload(s, block);
return ram_dirty_bitmap_reload(s, block, errp);
}
static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value)
static bool migrate_handle_rp_resume_ack(MigrationState *s,
uint32_t value, Error **errp)
{
trace_source_return_path_thread_resume_ack(value);
if (value != MIGRATION_RESUME_ACK_VALUE) {
error_report("%s: illegal resume_ack value %"PRIu32,
__func__, value);
return -1;
error_setg(errp, "illegal resume_ack value %"PRIu32, value);
return false;
}
/* Now both sides are active. */
@ -1871,7 +2127,7 @@ static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value)
/* Notify send thread that time to continue send pages */
migration_rp_kick(s);
return 0;
return true;
}
/*
@ -1919,48 +2175,46 @@ static void *source_return_path_thread(void *opaque)
uint32_t tmp32, sibling_error;
ram_addr_t start = 0; /* =0 to silence warning */
size_t len = 0, expected_len;
Error *err = NULL;
int res;
trace_source_return_path_thread_entry();
rcu_register_thread();
while (!ms->rp_state.error && !qemu_file_get_error(rp) &&
migration_is_setup_or_active(ms->state)) {
while (migration_is_setup_or_active(ms->state)) {
trace_source_return_path_thread_loop_top();
header_type = qemu_get_be16(rp);
header_len = qemu_get_be16(rp);
if (qemu_file_get_error(rp)) {
mark_source_rp_bad(ms);
qemu_file_get_error_obj(rp, &err);
goto out;
}
if (header_type >= MIG_RP_MSG_MAX ||
header_type == MIG_RP_MSG_INVALID) {
error_report("RP: Received invalid message 0x%04x length 0x%04x",
header_type, header_len);
mark_source_rp_bad(ms);
error_setg(&err, "Received invalid message 0x%04x length 0x%04x",
header_type, header_len);
goto out;
}
if ((rp_cmd_args[header_type].len != -1 &&
header_len != rp_cmd_args[header_type].len) ||
header_len > sizeof(buf)) {
error_report("RP: Received '%s' message (0x%04x) with"
"incorrect length %d expecting %zu",
rp_cmd_args[header_type].name, header_type, header_len,
(size_t)rp_cmd_args[header_type].len);
mark_source_rp_bad(ms);
error_setg(&err, "Received '%s' message (0x%04x) with"
"incorrect length %d expecting %zu",
rp_cmd_args[header_type].name, header_type, header_len,
(size_t)rp_cmd_args[header_type].len);
goto out;
}
/* We know we've got a valid header by this point */
res = qemu_get_buffer(rp, buf, header_len);
if (res != header_len) {
error_report("RP: Failed reading data for message 0x%04x"
" read %d expected %d",
header_type, res, header_len);
mark_source_rp_bad(ms);
error_setg(&err, "Failed reading data for message 0x%04x"
" read %d expected %d",
header_type, res, header_len);
goto out;
}
@ -1970,8 +2224,7 @@ static void *source_return_path_thread(void *opaque)
sibling_error = ldl_be_p(buf);
trace_source_return_path_thread_shut(sibling_error);
if (sibling_error) {
error_report("RP: Sibling indicated error %d", sibling_error);
mark_source_rp_bad(ms);
error_setg(&err, "Sibling indicated error %d", sibling_error);
}
/*
* We'll let the main thread deal with closing the RP
@ -1989,7 +2242,10 @@ static void *source_return_path_thread(void *opaque)
case MIG_RP_MSG_REQ_PAGES:
start = ldq_be_p(buf);
len = ldl_be_p(buf + 8);
migrate_handle_rp_req_pages(ms, NULL, start, len);
migrate_handle_rp_req_pages(ms, NULL, start, len, &err);
if (err) {
goto out;
}
break;
case MIG_RP_MSG_REQ_PAGES_ID:
@ -2004,32 +2260,32 @@ static void *source_return_path_thread(void *opaque)
expected_len += tmp32;
}
if (header_len != expected_len) {
error_report("RP: Req_Page_id with length %d expecting %zd",
header_len, expected_len);
mark_source_rp_bad(ms);
error_setg(&err, "Req_Page_id with length %d expecting %zd",
header_len, expected_len);
goto out;
}
migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len,
&err);
if (err) {
goto out;
}
migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len);
break;
case MIG_RP_MSG_RECV_BITMAP:
if (header_len < 1) {
error_report("%s: missing block name", __func__);
mark_source_rp_bad(ms);
error_setg(&err, "MIG_RP_MSG_RECV_BITMAP missing block name");
goto out;
}
/* Format: len (1B) + idstr (<255B). This ends the idstr. */
buf[buf[0] + 1] = '\0';
if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) {
mark_source_rp_bad(ms);
if (!migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) {
goto out;
}
break;
case MIG_RP_MSG_RESUME_ACK:
tmp32 = ldl_be_p(buf);
if (migrate_handle_rp_resume_ack(ms, tmp32)) {
mark_source_rp_bad(ms);
if (!migrate_handle_rp_resume_ack(ms, tmp32, &err)) {
goto out;
}
break;
@ -2045,13 +2301,29 @@ static void *source_return_path_thread(void *opaque)
}
out:
if (qemu_file_get_error(rp)) {
if (err) {
migrate_set_error(ms, err);
error_free(err);
trace_source_return_path_thread_bad_end();
mark_source_rp_bad(ms);
}
if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER) {
/*
* this will be extremely unlikely: that we got yet another network
* issue during recovering of the 1st network failure.. during this
* period the main migration thread can be waiting on rp_sem for
* this thread to sync with the other side.
*
* When this happens, explicitly kick the migration thread out of
* RECOVER stage and back to PAUSED, so the admin can try
* everything again.
*/
migration_rp_kick(ms);
}
trace_source_return_path_thread_end();
rcu_unregister_thread();
return NULL;
}
@ -2073,12 +2345,11 @@ static int open_return_path_on_source(MigrationState *ms)
return 0;
}
static int close_return_path_on_source(MigrationState *ms)
/* Return true if error detected, or false otherwise */
static bool close_return_path_on_source(MigrationState *ms)
{
int ret;
if (!ms->rp_state.rp_thread_created) {
return 0;
return false;
}
trace_migration_return_path_end_before();
@ -2096,18 +2367,13 @@ static int close_return_path_on_source(MigrationState *ms)
}
}
trace_await_return_path_close_on_source_joining();
qemu_thread_join(&ms->rp_state.rp_thread);
ms->rp_state.rp_thread_created = false;
trace_await_return_path_close_on_source_close();
ret = ms->rp_state.error;
ms->rp_state.error = false;
migration_release_dst_files(ms);
trace_migration_return_path_end_after();
trace_migration_return_path_end_after(ret);
return ret;
/* Return path will persist the error in MigrationState when quit */
return migrate_has_error(ms);
}
static inline void
@ -2126,7 +2392,6 @@ static int postcopy_start(MigrationState *ms, Error **errp)
int ret;
QIOChannelBuffer *bioc;
QEMUFile *fb;
int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
uint64_t bandwidth = migrate_max_postcopy_bandwidth();
bool restart_block = false;
int cur_state = MIGRATION_STATUS_ACTIVE;
@ -2148,9 +2413,11 @@ static int postcopy_start(MigrationState *ms, Error **errp)
qemu_mutex_lock_iothread();
trace_postcopy_start_set_run();
migration_downtime_start(ms);
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
global_state_store();
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE);
if (ret < 0) {
goto fail;
}
@ -2250,7 +2517,7 @@ static int postcopy_start(MigrationState *ms, Error **errp)
ms->postcopy_after_devices = true;
migration_call_notifiers(ms);
ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop;
migration_downtime_end(ms);
qemu_mutex_unlock_iothread();
@ -2346,13 +2613,13 @@ static int migration_completion_precopy(MigrationState *s,
int ret;
qemu_mutex_lock_iothread();
s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
migration_downtime_start(s);
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
s->vm_old_state = runstate_get();
global_state_store();
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE);
trace_migration_completion_vm_stop(ret);
if (ret < 0) {
goto out_unlock;
@ -2519,7 +2786,9 @@ static int postcopy_resume_handshake(MigrationState *s)
qemu_savevm_send_postcopy_resume(s->to_dst_file);
while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) {
migration_rp_wait(s);
if (migration_rp_wait(s)) {
return -1;
}
}
if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
@ -2703,15 +2972,8 @@ static void migration_calculate_complete(MigrationState *s)
int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
int64_t transfer_time;
migration_downtime_end(s);
s->total_time = end_time - s->start_time;
if (!s->downtime) {
/*
* It's still not set, so we are precopy migration. For
* postcopy, downtime is calculated during postcopy_start().
*/
s->downtime = end_time - s->downtime_start;
}
transfer_time = s->total_time - s->setup_time;
if (transfer_time) {
s->mbps = ((double) bytes * 8.0) / transfer_time / 1000;
@ -3130,7 +3392,7 @@ static void bg_migration_vm_start_bh(void *opaque)
s->vm_start_bh = NULL;
vm_start();
s->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - s->downtime_start;
migration_downtime_end(s);
}
/**
@ -3197,7 +3459,7 @@ static void *bg_migration_thread(void *opaque)
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
trace_migration_thread_setup_complete();
s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
migration_downtime_start(s);
qemu_mutex_lock_iothread();
@ -3210,7 +3472,7 @@ static void *bg_migration_thread(void *opaque)
global_state_store();
/* Forcibly stop VM before saving state of vCPUs and devices */
if (vm_stop_force_state(RUN_STATE_PAUSED)) {
if (migration_stop_vm(RUN_STATE_PAUSED)) {
goto fail;
}
/*

View File

@ -308,7 +308,6 @@ struct MigrationState {
/* Protected by qemu_file_lock */
QEMUFile *from_dst_file;
QemuThread rp_thread;
bool error;
/*
* We can also check non-zero of rp_thread, but there's no "official"
* way to do this, so this bool makes it slightly more elegant.
@ -495,6 +494,7 @@ int migrate_init(MigrationState *s, Error **errp);
bool migration_is_blocked(Error **errp);
/* True if outgoing migration has entered postcopy phase */
bool migration_in_postcopy(void);
bool migration_postcopy_is_alive(int state);
MigrationState *migrate_get_current(void);
uint64_t ram_get_total_transferred_pages(void);
@ -520,7 +520,8 @@ bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList *bbm,
Error **errp);
void migrate_add_address(SocketAddress *address);
bool migrate_uri_parse(const char *uri, MigrationChannel **channel,
Error **errp);
int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque);
#define qemu_ram_foreach_block \
@ -535,8 +536,11 @@ void migration_populate_vfio_info(MigrationInfo *info);
void migration_reset_vfio_bytes_transferred(void);
void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page);
/* Migration thread waiting for return path thread. */
void migration_rp_wait(MigrationState *s);
/*
* Migration thread waiting for return path thread. Return non-zero if an
* error is detected.
*/
int migration_rp_wait(MigrationState *s);
/*
* Kick the migration thread waiting for return path messages. NOTE: the
* name can be slightly confusing (when read as "kick the rp thread"), just
@ -544,4 +548,6 @@ void migration_rp_wait(MigrationState *s);
*/
void migration_rp_kick(MigrationState *s);
int migration_stop_vm(RunState state);
#endif

View File

@ -176,6 +176,9 @@ Property migration_properties[] = {
DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState,
parameters.vcpu_dirty_limit,
DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT),
DEFINE_PROP_MIG_MODE("mode", MigrationState,
parameters.mode,
MIG_MODE_NORMAL),
/* Migration capabilities */
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
@ -827,6 +830,13 @@ uint64_t migrate_max_postcopy_bandwidth(void)
return s->parameters.max_postcopy_bandwidth;
}
MigMode migrate_mode(void)
{
MigrationState *s = migrate_get_current();
return s->parameters.mode;
}
int migrate_multifd_channels(void)
{
MigrationState *s = migrate_get_current();
@ -999,6 +1009,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
params->has_vcpu_dirty_limit = true;
params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
params->has_mode = true;
params->mode = s->parameters.mode;
return params;
}
@ -1034,6 +1046,7 @@ void migrate_params_init(MigrationParameters *params)
params->has_announce_step = true;
params->has_x_vcpu_dirty_limit_period = true;
params->has_vcpu_dirty_limit = true;
params->has_mode = true;
}
/*
@ -1331,6 +1344,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
if (params->has_vcpu_dirty_limit) {
dest->vcpu_dirty_limit = params->vcpu_dirty_limit;
}
if (params->has_mode) {
dest->mode = params->mode;
}
}
static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
@ -1471,6 +1488,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
if (params->has_vcpu_dirty_limit) {
s->parameters.vcpu_dirty_limit = params->vcpu_dirty_limit;
}
if (params->has_mode) {
s->parameters.mode = params->mode;
}
}
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)

View File

@ -83,6 +83,7 @@ uint8_t migrate_max_cpu_throttle(void);
uint64_t migrate_max_bandwidth(void);
uint64_t migrate_avail_switchover_bandwidth(void);
uint64_t migrate_max_postcopy_bandwidth(void);
MigMode migrate_mode(void);
int migrate_multifd_channels(void);
MultiFDCompression migrate_multifd_compression(void);
int migrate_multifd_zlib_level(void);

View File

@ -137,7 +137,7 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc)
*
* If errp is specified, a verbose error message will be copied over.
*/
static int qemu_file_get_error_obj(QEMUFile *f, Error **errp)
int qemu_file_get_error_obj(QEMUFile *f, Error **errp)
{
if (!f->last_error) {
return 0;

View File

@ -68,6 +68,7 @@ int coroutine_mixed_fn qemu_peek_byte(QEMUFile *f, int offset);
void qemu_file_skip(QEMUFile *f, int size);
int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp);
void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err);
int qemu_file_get_error_obj(QEMUFile *f, Error **errp);
void qemu_file_set_error(QEMUFile *f, int ret);
int qemu_file_shutdown(QEMUFile *f);
QEMUFile *qemu_file_get_return_path(QEMUFile *f);

View File

@ -1898,7 +1898,8 @@ static void migration_page_queue_free(RAMState *rs)
* @start: starting address from the start of the RAMBlock
* @len: length (in bytes) to send
*/
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len,
Error **errp)
{
RAMBlock *ramblock;
RAMState *rs = ram_state;
@ -1915,7 +1916,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
* Shouldn't happen, we can't reuse the last RAMBlock if
* it's the 1st request.
*/
error_report("ram_save_queue_pages no previous block");
error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no previous block");
return -1;
}
} else {
@ -1923,16 +1924,17 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
if (!ramblock) {
/* We shouldn't be asked for a non-existent RAMBlock */
error_report("ram_save_queue_pages no block '%s'", rbname);
error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no block '%s'", rbname);
return -1;
}
rs->last_req_rb = ramblock;
}
trace_ram_save_queue_pages(ramblock->idstr, start, len);
if (!offset_in_ramblock(ramblock, start + len - 1)) {
error_report("%s request overrun start=" RAM_ADDR_FMT " len="
RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT,
__func__, start, len, ramblock->used_length);
error_setg(errp, "MIG_RP_MSG_REQ_PAGES request overrun, "
"start=" RAM_ADDR_FMT " len="
RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT,
start, len, ramblock->used_length);
return -1;
}
@ -1964,9 +1966,9 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
assert(len % page_size == 0);
while (len) {
if (ram_save_host_page_urgent(pss)) {
error_report("%s: ram_save_host_page_urgent() failed: "
"ramblock=%s, start_addr=0x"RAM_ADDR_FMT,
__func__, ramblock->idstr, start);
error_setg(errp, "ram_save_host_page_urgent() failed: "
"ramblock=%s, start_addr=0x"RAM_ADDR_FMT,
ramblock->idstr, start);
ret = -1;
break;
}
@ -4097,7 +4099,9 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs)
/* Wait until all the ramblocks' dirty bitmap synced */
while (qatomic_read(&rs->postcopy_bmap_sync_requested)) {
migration_rp_wait(s);
if (migration_rp_wait(s)) {
return -1;
}
}
trace_ram_dirty_bitmap_sync_complete();
@ -4109,10 +4113,11 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs)
* Read the received bitmap, revert it as the initial dirty bitmap.
* This is only used when the postcopy migration is paused but wants
* to resume from a middle point.
*
* Returns true if succeeded, false for errors.
*/
int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block)
bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp)
{
int ret = -EINVAL;
/* from_dst_file is always valid because we're within rp_thread */
QEMUFile *file = s->rp_state.from_dst_file;
g_autofree unsigned long *le_bitmap = NULL;
@ -4124,9 +4129,9 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block)
trace_ram_dirty_bitmap_reload_begin(block->idstr);
if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) {
error_report("%s: incorrect state %s", __func__,
MigrationStatus_str(s->state));
return -EINVAL;
error_setg(errp, "Reload bitmap in incorrect state %s",
MigrationStatus_str(s->state));
return false;
}
/*
@ -4142,27 +4147,25 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block)
/* The size of the bitmap should match with our ramblock */
if (size != local_size) {
error_report("%s: ramblock '%s' bitmap size mismatch "
"(0x%"PRIx64" != 0x%"PRIx64")", __func__,
block->idstr, size, local_size);
return -EINVAL;
error_setg(errp, "ramblock '%s' bitmap size mismatch (0x%"PRIx64
" != 0x%"PRIx64")", block->idstr, size, local_size);
return false;
}
size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size);
end_mark = qemu_get_be64(file);
ret = qemu_file_get_error(file);
if (ret || size != local_size) {
error_report("%s: read bitmap failed for ramblock '%s': %d"
" (size 0x%"PRIx64", got: 0x%"PRIx64")",
__func__, block->idstr, ret, local_size, size);
return -EIO;
if (qemu_file_get_error(file) || size != local_size) {
error_setg(errp, "read bitmap failed for ramblock '%s': "
"(size 0x%"PRIx64", got: 0x%"PRIx64")",
block->idstr, local_size, size);
return false;
}
if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) {
error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIx64,
__func__, block->idstr, end_mark);
return -EINVAL;
error_setg(errp, "ramblock '%s' end mark incorrect: 0x%"PRIx64,
block->idstr, end_mark);
return false;
}
/*
@ -4194,7 +4197,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block)
*/
migration_rp_kick(s);
return 0;
return true;
}
static int ram_resume_prepare(MigrationState *s, void *opaque)

View File

@ -50,7 +50,8 @@ uint64_t ram_bytes_total(void);
void mig_throttle_counter_reset(void);
uint64_t ram_pagesize_summary(void);
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len);
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len,
Error **errp);
void ram_postcopy_migrated_memory_release(MigrationState *ms);
/* For outgoing discard bitmap */
void ram_postcopy_send_discard_bitmap(MigrationState *ms);
@ -70,7 +71,7 @@ void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr);
void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr);
int64_t ramblock_recv_bitmap_send(QEMUFile *file,
const char *block_name);
int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb);
bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp);
bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start);
void postcopy_preempt_shutdown_file(MigrationState *s);
void *postcopy_preempt_thread(void *opaque);

View File

@ -289,7 +289,6 @@ typedef struct RDMALocalBlocks {
typedef struct RDMAContext {
char *host;
int port;
char *host_port;
RDMAWorkRequestData wr_data[RDMA_WRID_MAX];
@ -2431,9 +2430,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
rdma->channel = NULL;
}
g_free(rdma->host);
g_free(rdma->host_port);
rdma->host = NULL;
rdma->host_port = NULL;
}
@ -2723,28 +2720,16 @@ static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path,
rdma_return_path->is_return_path = true;
}
static RDMAContext *qemu_rdma_data_init(const char *host_port, Error **errp)
static RDMAContext *qemu_rdma_data_init(InetSocketAddress *saddr, Error **errp)
{
RDMAContext *rdma = NULL;
InetSocketAddress *addr;
rdma = g_new0(RDMAContext, 1);
rdma->current_index = -1;
rdma->current_chunk = -1;
addr = g_new(InetSocketAddress, 1);
if (!inet_parse(addr, host_port, NULL)) {
rdma->port = atoi(addr->port);
rdma->host = g_strdup(addr->host);
rdma->host_port = g_strdup(host_port);
} else {
error_setg(errp, "RDMA ERROR: bad RDMA migration address '%s'",
host_port);
g_free(rdma);
rdma = NULL;
}
qapi_free_InetSocketAddress(addr);
rdma->host = g_strdup(saddr->host);
rdma->port = atoi(saddr->port);
return rdma;
}
@ -3353,6 +3338,7 @@ static int qemu_rdma_accept(RDMAContext *rdma)
.private_data_len = sizeof(cap),
};
RDMAContext *rdma_return_path = NULL;
g_autoptr(InetSocketAddress) isock = g_new0(InetSocketAddress, 1);
struct rdma_cm_event *cm_event;
struct ibv_context *verbs;
int ret;
@ -3367,13 +3353,16 @@ static int qemu_rdma_accept(RDMAContext *rdma)
goto err_rdma_dest_wait;
}
isock->host = rdma->host;
isock->port = g_strdup_printf("%d", rdma->port);
/*
* initialize the RDMAContext for return path for postcopy after first
* connection request reached.
*/
if ((migrate_postcopy() || migrate_return_path())
&& !rdma->is_return_path) {
rdma_return_path = qemu_rdma_data_init(rdma->host_port, NULL);
rdma_return_path = qemu_rdma_data_init(isock, NULL);
if (rdma_return_path == NULL) {
rdma_ack_cm_event(cm_event);
goto err_rdma_dest_wait;
@ -4074,7 +4063,8 @@ static void rdma_accept_incoming_migration(void *opaque)
}
}
void rdma_start_incoming_migration(const char *host_port, Error **errp)
void rdma_start_incoming_migration(InetSocketAddress *host_port,
Error **errp)
{
MigrationState *s = migrate_get_current();
int ret;
@ -4118,13 +4108,12 @@ cleanup_rdma:
err:
if (rdma) {
g_free(rdma->host);
g_free(rdma->host_port);
}
g_free(rdma);
}
void rdma_start_outgoing_migration(void *opaque,
const char *host_port, Error **errp)
InetSocketAddress *host_port, Error **errp)
{
MigrationState *s = opaque;
RDMAContext *rdma_return_path = NULL;

View File

@ -14,15 +14,17 @@
*
*/
#include "qemu/sockets.h"
#ifndef QEMU_MIGRATION_RDMA_H
#define QEMU_MIGRATION_RDMA_H
#include "exec/memory.h"
void rdma_start_outgoing_migration(void *opaque, const char *host_port,
void rdma_start_outgoing_migration(void *opaque, InetSocketAddress *host_port,
Error **errp);
void rdma_start_incoming_migration(const char *host_port, Error **errp);
void rdma_start_incoming_migration(InetSocketAddress *host_port, Error **errp);
/*
* Constants used by rdma return codes

View File

@ -237,6 +237,8 @@ static SaveState savevm_state = {
.global_section_id = 0,
};
static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id);
static bool should_validate_capability(int capability)
{
assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX);
@ -716,6 +718,18 @@ static void savevm_state_handler_insert(SaveStateEntry *nse)
assert(priority <= MIG_PRI_MAX);
/*
* This should never happen otherwise migration will probably fail
* silently somewhere because we can be wrongly applying one
* object properties upon another one. Bail out ASAP.
*/
if (find_se(nse->idstr, nse->instance_id)) {
error_report("%s: Detected duplicate SaveStateEntry: "
"id=%s, instance_id=0x%"PRIx32, __func__,
nse->idstr, nse->instance_id);
exit(EXIT_FAILURE);
}
for (i = priority - 1; i >= 0; i--) {
se = savevm_state.handler_pri_head[i];
if (se != NULL) {
@ -846,6 +860,24 @@ static void vmstate_check(const VMStateDescription *vmsd)
}
}
/*
* See comment in hw/intc/xics.c:icp_realize()
*
* This function can be removed when
* pre_2_10_vmstate_register_dummy_icp() is removed.
*/
int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id,
const VMStateDescription *vmsd,
void *opaque)
{
SaveStateEntry *se = find_se(vmsd->name, instance_id);
if (se) {
savevm_state_handler_remove(se);
}
return vmstate_register(obj, instance_id, vmsd, opaque);
}
int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id,
const VMStateDescription *vmsd,
void *opaque, int alias_id,
@ -1459,6 +1491,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
static
int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
{
int64_t start_ts_each, end_ts_each;
SaveStateEntry *se;
int ret;
@ -1475,6 +1508,8 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
continue;
}
}
start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_savevm_section_start(se->idstr, se->section_id);
save_section_header(f, se, QEMU_VM_SECTION_END);
@ -1486,8 +1521,13 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
qemu_file_set_error(f, ret);
return -1;
}
end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id,
end_ts_each - start_ts_each);
}
trace_vmstate_downtime_checkpoint("src-iterable-saved");
return 0;
}
@ -1496,6 +1536,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
bool inactivate_disks)
{
MigrationState *ms = migrate_get_current();
int64_t start_ts_each, end_ts_each;
JSONWriter *vmdesc = ms->vmdesc;
int vmdesc_len;
SaveStateEntry *se;
@ -1507,11 +1548,17 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
continue;
}
start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
ret = vmstate_save(f, se, vmdesc);
if (ret) {
qemu_file_set_error(f, ret);
return ret;
}
end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_vmstate_downtime_save("non-iterable", se->idstr, se->instance_id,
end_ts_each - start_ts_each);
}
if (inactivate_disks) {
@ -1547,6 +1594,8 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
json_writer_free(vmdesc);
ms->vmdesc = NULL;
trace_vmstate_downtime_checkpoint("src-non-iterable-saved");
return 0;
}
@ -2088,18 +2137,18 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
Error *local_err = NULL;
MigrationIncomingState *mis = opaque;
trace_loadvm_postcopy_handle_run_bh("enter");
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter");
/* TODO we should move all of this lot into postcopy_ram.c or a shared code
* in migration.c
*/
cpu_synchronize_all_post_init();
trace_loadvm_postcopy_handle_run_bh("after cpu sync");
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cpu-synced");
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
trace_loadvm_postcopy_handle_run_bh("after announce");
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-announced");
/* Make sure all file formats throw away their mutable metadata.
* If we get an error here, just don't restart the VM yet. */
@ -2110,7 +2159,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
autostart = false;
}
trace_loadvm_postcopy_handle_run_bh("after invalidate cache");
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated");
dirty_bitmap_mig_before_vm_start();
@ -2124,7 +2173,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
qemu_bh_delete(mis->bh);
trace_loadvm_postcopy_handle_run_bh("return");
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-vm-started");
}
/* After all discards we can start running and asking for pages */
@ -2505,9 +2554,12 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
}
static int
qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis,
uint8_t type)
{
bool trace_downtime = (type == QEMU_VM_SECTION_FULL);
uint32_t instance_id, version_id, section_id;
int64_t start_ts, end_ts;
SaveStateEntry *se;
char idstr[256];
int ret;
@ -2556,12 +2608,23 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
return -EINVAL;
}
if (trace_downtime) {
start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
}
ret = vmstate_load(f, se);
if (ret < 0) {
error_report("error while loading state for instance 0x%"PRIx32" of"
" device '%s'", instance_id, idstr);
return ret;
}
if (trace_downtime) {
end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_vmstate_downtime_load("non-iterable", se->idstr,
se->instance_id, end_ts - start_ts);
}
if (!check_section_footer(f, se)) {
return -EINVAL;
}
@ -2570,8 +2633,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
}
static int
qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis)
qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis,
uint8_t type)
{
bool trace_downtime = (type == QEMU_VM_SECTION_END);
int64_t start_ts, end_ts;
uint32_t section_id;
SaveStateEntry *se;
int ret;
@ -2596,12 +2662,23 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis)
return -EINVAL;
}
if (trace_downtime) {
start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
}
ret = vmstate_load(f, se);
if (ret < 0) {
error_report("error while loading state section id %d(%s)",
section_id, se->idstr);
return ret;
}
if (trace_downtime) {
end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
trace_vmstate_downtime_load("iterable", se->idstr,
se->instance_id, end_ts - start_ts);
}
if (!check_section_footer(f, se)) {
return -EINVAL;
}
@ -2790,14 +2867,14 @@ retry:
switch (section_type) {
case QEMU_VM_SECTION_START:
case QEMU_VM_SECTION_FULL:
ret = qemu_loadvm_section_start_full(f, mis);
ret = qemu_loadvm_section_start_full(f, mis, section_type);
if (ret < 0) {
goto out;
}
break;
case QEMU_VM_SECTION_PART:
case QEMU_VM_SECTION_END:
ret = qemu_loadvm_section_part_end(f, mis);
ret = qemu_loadvm_section_part_end(f, mis, section_type);
if (ret < 0) {
goto out;
}

View File

@ -28,6 +28,8 @@
#include "trace.h"
#include "postcopy-ram.h"
#include "options.h"
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-sockets.h"
struct SocketOutgoingArgs {
SocketAddress *saddr;
@ -108,19 +110,19 @@ out:
object_unref(OBJECT(sioc));
}
static void
socket_start_outgoing_migration_internal(MigrationState *s,
SocketAddress *saddr,
Error **errp)
void socket_start_outgoing_migration(MigrationState *s,
SocketAddress *saddr,
Error **errp)
{
QIOChannelSocket *sioc = qio_channel_socket_new();
struct SocketConnectData *data = g_new0(struct SocketConnectData, 1);
SocketAddress *addr = QAPI_CLONE(SocketAddress, saddr);
data->s = s;
/* in case previous migration leaked it */
qapi_free_SocketAddress(outgoing_args.saddr);
outgoing_args.saddr = saddr;
outgoing_args.saddr = addr;
if (saddr->type == SOCKET_ADDRESS_TYPE_INET) {
data->hostname = g_strdup(saddr->u.inet.host);
@ -135,18 +137,6 @@ socket_start_outgoing_migration_internal(MigrationState *s,
NULL);
}
void socket_start_outgoing_migration(MigrationState *s,
const char *str,
Error **errp)
{
Error *err = NULL;
SocketAddress *saddr = socket_parse(str, &err);
if (!err) {
socket_start_outgoing_migration_internal(s, saddr, &err);
}
error_propagate(errp, err);
}
static void socket_accept_incoming_migration(QIONetListener *listener,
QIOChannelSocket *cioc,
gpointer opaque)
@ -172,9 +162,8 @@ socket_incoming_migration_end(void *opaque)
object_unref(OBJECT(listener));
}
static void
socket_start_incoming_migration_internal(SocketAddress *saddr,
Error **errp)
void socket_start_incoming_migration(SocketAddress *saddr,
Error **errp)
{
QIONetListener *listener = qio_net_listener_new();
MigrationIncomingState *mis = migration_incoming_get_current();
@ -213,13 +202,3 @@ socket_start_incoming_migration_internal(SocketAddress *saddr,
}
}
void socket_start_incoming_migration(const char *str, Error **errp)
{
Error *err = NULL;
SocketAddress *saddr = socket_parse(str, &err);
if (!err) {
socket_start_incoming_migration_internal(saddr, &err);
}
qapi_free_SocketAddress(saddr);
error_propagate(errp, err);
}

View File

@ -19,13 +19,14 @@
#include "io/channel.h"
#include "io/task.h"
#include "qemu/sockets.h"
void socket_send_channel_create(QIOTaskFunc f, void *data);
QIOChannel *socket_send_channel_create_sync(Error **errp);
int socket_send_channel_destroy(QIOChannel *send);
void socket_start_incoming_migration(const char *str, Error **errp);
void socket_start_incoming_migration(SocketAddress *saddr, Error **errp);
void socket_start_outgoing_migration(MigrationState *s, const char *str,
Error **errp);
void socket_start_outgoing_migration(MigrationState *s,
SocketAddress *saddr, Error **errp);
#endif

View File

@ -17,7 +17,6 @@ loadvm_handle_recv_bitmap(char *s) "%s"
loadvm_postcopy_handle_advise(void) ""
loadvm_postcopy_handle_listen(const char *str) "%s"
loadvm_postcopy_handle_run(void) ""
loadvm_postcopy_handle_run_bh(const char *str) "%s"
loadvm_postcopy_handle_resume(void) ""
loadvm_postcopy_ram_handle_discard(void) ""
loadvm_postcopy_ram_handle_discard_end(void) ""
@ -48,6 +47,9 @@ savevm_state_cleanup(void) ""
savevm_state_complete_precopy(void) ""
vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s"
vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s"
vmstate_downtime_save(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64
vmstate_downtime_load(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64
vmstate_downtime_checkpoint(const char *checkpoint) "%s"
postcopy_pause_incoming(void) ""
postcopy_pause_incoming_continued(void) ""
postcopy_page_req_sync(void *host_addr) "sync page req %p"
@ -148,8 +150,6 @@ multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p"
multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s"
# migration.c
await_return_path_close_on_source_close(void) ""
await_return_path_close_on_source_joining(void) ""
migrate_set_state(const char *new_state) "new state %s"
migrate_fd_cleanup(void) ""
migrate_fd_error(const char *error_desc) "error=%s"
@ -166,7 +166,7 @@ migration_completion_postcopy_end_after_complete(void) ""
migration_rate_limit_pre(int ms) "%d ms"
migration_rate_limit_post(int urgent) "urgent: %d"
migration_return_path_end_before(void) ""
migration_return_path_end_after(int rp_error) "%d"
migration_return_path_end_after(void) ""
migration_thread_after_loop(void) ""
migration_thread_file_err(void) ""
migration_thread_setup_complete(void) ""

View File

@ -46,6 +46,7 @@
#include "qapi/qmp/qdict.h"
#include "util.h"
#include "migration/register.h"
#include "migration/vmstate.h"
#include "migration/qemu-file-types.h"
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
@ -659,8 +660,8 @@ static int net_slirp_init(NetClientState *peer, const char *model,
* specific version?
*/
g_assert(slirp_state_version() == 4);
register_savevm_live("slirp", 0, slirp_state_version(),
&savevm_slirp_state, s->slirp);
register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY,
slirp_state_version(), &savevm_slirp_state, s->slirp);
s->poll_notifier.notify = net_slirp_poll_notify;
main_loop_poll_add_notifier(&s->poll_notifier);

View File

@ -631,6 +631,28 @@
'data': [ 'none', 'zlib',
{ 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] }
##
# @MigMode:
#
# @normal: the original form of migration. (since 8.2)
#
# @cpr-reboot: The migrate command saves state to a file, allowing one to
# quit qemu, reboot to an updated kernel, and restart an updated
# version of qemu. The caller must specify a migration URI
# that writes to and reads from a file. Unlike normal mode,
# the use of certain local storage options does not block the
# migration, but the caller must not modify guest block devices
# between the quit and restart. To avoid saving guest RAM to the
# file, the memory backend must be shared, and the @x-ignore-shared
# migration capability must be set. Guest RAM must be non-volatile
# across reboot, such as by backing it with a dax device, but this
# is not enforced. The restarted qemu arguments must match those
# used to initially start qemu, plus the -incoming option.
# (since 8.2)
##
{ 'enum': 'MigMode',
'data': [ 'normal', 'cpr-reboot' ] }
##
# @BitmapMigrationBitmapAliasTransform:
#
@ -849,6 +871,9 @@
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
# Defaults to 1. (Since 8.1)
#
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@ -881,7 +906,8 @@
'multifd-zlib-level', 'multifd-zstd-level',
'block-bitmap-mapping',
{ 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
'vcpu-dirty-limit'] }
'vcpu-dirty-limit',
'mode'] }
##
# @MigrateSetParameters:
@ -1033,6 +1059,9 @@
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
# Defaults to 1. (Since 8.1)
#
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@ -1085,7 +1114,8 @@
'*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
'features': [ 'unstable' ] },
'*vcpu-dirty-limit': 'uint64'} }
'*vcpu-dirty-limit': 'uint64',
'*mode': 'MigMode'} }
##
# @migrate-set-parameters:
@ -1257,6 +1287,9 @@
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
# Defaults to 1. (Since 8.1)
#
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@ -1306,7 +1339,8 @@
'*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
'features': [ 'unstable' ] },
'*vcpu-dirty-limit': 'uint64'} }
'*vcpu-dirty-limit': 'uint64',
'*mode': 'MigMode'} }
##
# @query-migrate-parameters:
@ -1550,6 +1584,91 @@
##
{ 'command': 'migrate-continue', 'data': {'state': 'MigrationStatus'} }
##
# @MigrationAddressType:
#
# The migration stream transport mechanisms.
#
# @socket: Migrate via socket.
#
# @exec: Direct the migration stream to another process.
#
# @rdma: Migrate via RDMA.
#
# @file: Direct the migration stream to a file.
#
# Since 8.2
##
{ 'enum': 'MigrationAddressType',
'data': [ 'socket', 'exec', 'rdma', 'file' ] }
##
# @FileMigrationArgs:
#
# @filename: The file to receive the migration stream
#
# @offset: The file offset where the migration stream will start
#
# Since 8.2
##
{ 'struct': 'FileMigrationArgs',
'data': { 'filename': 'str',
'offset': 'uint64' } }
##
# @MigrationExecCommand:
#
# @args: command (list head) and arguments to execute.
#
# Since 8.2
##
{ 'struct': 'MigrationExecCommand',
'data': {'args': [ 'str' ] } }
##
# @MigrationAddress:
#
# Migration endpoint configuration.
#
# Since 8.2
##
{ 'union': 'MigrationAddress',
'base': { 'transport' : 'MigrationAddressType'},
'discriminator': 'transport',
'data': {
'socket': 'SocketAddress',
'exec': 'MigrationExecCommand',
'rdma': 'InetSocketAddress',
'file': 'FileMigrationArgs' } }
##
# @MigrationChannelType:
#
# The migration channel-type request options.
#
# @main: Main outbound migration channel.
#
# Since 8.1
##
{ 'enum': 'MigrationChannelType',
'data': [ 'main' ] }
##
# @MigrationChannel:
#
# Migration stream channel parameters.
#
# @channel-type: Channel type for transfering packet information.
#
# @addr: Migration endpoint configuration on destination interface.
#
# Since 8.1
##
{ 'struct': 'MigrationChannel',
'data': {
'channel-type': 'MigrationChannelType',
'addr': 'MigrationAddress' } }
##
# @migrate:
#
@ -1557,6 +1676,9 @@
#
# @uri: the Uniform Resource Identifier of the destination VM
#
# @channels: list of migration stream channels with each stream in the
# list connected to a destination interface endpoint.
#
# @blk: do block migration (full disk copy)
#
# @inc: incremental disk copy migration
@ -1586,13 +1708,57 @@
# 3. The user Monitor's "detach" argument is invalid in QMP and should
# not be used
#
# 4. The uri argument should have the Uniform Resource Identifier of
# default destination VM. This connection will be bound to default
# network.
#
# 5. For now, number of migration streams is restricted to one, i.e
# number of items in 'channels' list is just 1.
#
# 6. The 'uri' and 'channels' arguments are mutually exclusive;
# exactly one of the two should be present.
#
# Example:
#
# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
# <- { "return": {} }
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "socket",
# "type": "inet",
# "host": "10.12.34.9",
# "port": "1050" } } ] } }
# <- { "return": {} }
#
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "exec",
# "args": [ "/bin/nc", "-p", "6000",
# "/some/sock" ] } } ] } }
# <- { "return": {} }
#
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "rdma",
# "host": "10.12.34.9",
# "port": "1050" } } ] } }
# <- { "return": {} }
#
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "file",
# "filename": "/tmp/migfile",
# "offset": "0x1000" } } ] } }
# <- { "return": {} }
#
##
{ 'command': 'migrate',
'data': {'uri': 'str',
'*channels': [ 'MigrationChannel' ],
'*blk': { 'type': 'bool', 'features': [ 'deprecated' ] },
'*inc': { 'type': 'bool', 'features': [ 'deprecated' ] },
'*detach': 'bool', '*resume': 'bool' } }
@ -1606,6 +1772,9 @@
# @uri: The Uniform Resource Identifier identifying the source or
# address to listen on
#
# @channels: list of migration stream channels with each stream in the
# list connected to a destination interface endpoint.
#
# Returns: nothing on success
#
# Since: 2.3
@ -1621,13 +1790,46 @@
#
# 3. The uri format is the same as for -incoming
#
# 5. For now, number of migration streams is restricted to one, i.e
# number of items in 'channels' list is just 1.
#
# 4. The 'uri' and 'channels' arguments are mutually exclusive;
# exactly one of the two should be present.
#
# Example:
#
# -> { "execute": "migrate-incoming",
# "arguments": { "uri": "tcp::4446" } }
# <- { "return": {} }
#
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "socket",
# "type": "inet",
# "host": "10.12.34.9",
# "port": "1050" } } ] } }
# <- { "return": {} }
#
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "exec",
# "args": [ "/bin/nc", "-p", "6000",
# "/some/sock" ] } } ] } }
# <- { "return": {} }
#
# -> { "execute": "migrate",
# "arguments": {
# "channels": [ { "channel-type": "main",
# "addr": { "transport": "rdma",
# "host": "10.12.34.9",
# "port": "1050" } } ] } }
# <- { "return": {} }
##
{ 'command': 'migrate-incoming', 'data': {'uri': 'str' } }
{ 'command': 'migrate-incoming',
'data': {'*uri': 'str',
'*channels': [ 'MigrationChannel' ] } }
##
# @xen-save-devices-state:

View File

@ -6,6 +6,16 @@ int migrate_add_blocker(Error **reasonp, Error **errp)
return 0;
}
int migrate_add_blocker_normal(Error **reasonp, Error **errp)
{
return 0;
}
int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...)
{
return 0;
}
void migrate_del_blocker(Error **reasonp)
{
}

View File

@ -2702,7 +2702,7 @@ void qmp_x_exit_preconfig(Error **errp)
if (incoming) {
Error *local_err = NULL;
if (strcmp(incoming, "defer") != 0) {
qmp_migrate_incoming(incoming, &local_err);
qmp_migrate_incoming(incoming, false, NULL, &local_err);
if (local_err) {
error_reportf_err(local_err, "-incoming %s: ", incoming);
exit(1);

View File

@ -728,6 +728,7 @@ typedef struct {
/* Postcopy specific fields */
void *postcopy_data;
bool postcopy_preempt;
bool postcopy_recovery_test_fail;
} MigrateCommon;
static int test_migrate_start(QTestState **from, QTestState **to,
@ -1309,7 +1310,12 @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
migrate_prepare_for_dirty_mem(from);
qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
" 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}");
" 'arguments': { "
" 'channels': [ { 'channel-type': 'main',"
" 'addr': { 'transport': 'socket',"
" 'type': 'inet',"
" 'host': '127.0.0.1',"
" 'port': '0' } } ] } }");
/* Wait for the first serial output from the source */
wait_for_serial("src_serial");
@ -1404,6 +1410,80 @@ static void test_postcopy_preempt_tls_psk(void)
}
#endif
static void wait_for_postcopy_status(QTestState *one, const char *status)
{
wait_for_migration_status(one, status,
(const char * []) { "failed", "active",
"completed", NULL });
}
#ifndef _WIN32
static void postcopy_recover_fail(QTestState *from, QTestState *to)
{
int ret, pair1[2], pair2[2];
char c;
/* Create two unrelated socketpairs */
ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1);
g_assert_cmpint(ret, ==, 0);
ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2);
g_assert_cmpint(ret, ==, 0);
/*
* Give the guests unpaired ends of the sockets, so they'll all blocked
* at reading. This mimics a wrong channel established.
*/
qtest_qmp_fds_assert_success(from, &pair1[0], 1,
"{ 'execute': 'getfd',"
" 'arguments': { 'fdname': 'fd-mig' }}");
qtest_qmp_fds_assert_success(to, &pair2[0], 1,
"{ 'execute': 'getfd',"
" 'arguments': { 'fdname': 'fd-mig' }}");
/*
* Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to
* emulate the 1st byte of a real recovery, but stops from there to
* keep dest QEMU in RECOVER. This is needed so that we can kick off
* the recover process on dest QEMU (by triggering the G_IO_IN event).
*
* NOTE: this trick is not needed on src QEMUs, because src doesn't
* rely on an pre-existing G_IO_IN event, so it will always trigger the
* upcoming recovery anyway even if it can read nothing.
*/
#define QEMU_VM_COMMAND 0x08
c = QEMU_VM_COMMAND;
ret = send(pair2[1], &c, 1, 0);
g_assert_cmpint(ret, ==, 1);
migrate_recover(to, "fd:fd-mig");
migrate_qmp(from, "fd:fd-mig", "{'resume': true}");
/*
* Make sure both QEMU instances will go into RECOVER stage, then test
* kicking them out using migrate-pause.
*/
wait_for_postcopy_status(from, "postcopy-recover");
wait_for_postcopy_status(to, "postcopy-recover");
/*
* This would be issued by the admin upon noticing the hang, we should
* make sure we're able to kick this out.
*/
migrate_pause(from);
wait_for_postcopy_status(from, "postcopy-paused");
/* Do the same test on dest */
migrate_pause(to);
wait_for_postcopy_status(to, "postcopy-paused");
close(pair1[0]);
close(pair1[1]);
close(pair2[0]);
close(pair2[1]);
}
#endif /* _WIN32 */
static void test_postcopy_recovery_common(MigrateCommon *args)
{
QTestState *from, *to;
@ -1439,9 +1519,19 @@ static void test_postcopy_recovery_common(MigrateCommon *args)
* migrate-recover command can only succeed if destination machine
* is in the paused state
*/
wait_for_migration_status(to, "postcopy-paused",
(const char * []) { "failed", "active",
"completed", NULL });
wait_for_postcopy_status(to, "postcopy-paused");
wait_for_postcopy_status(from, "postcopy-paused");
#ifndef _WIN32
if (args->postcopy_recovery_test_fail) {
/*
* Test when a wrong socket specified for recover, and then the
* ability to kick it out, and continue with a correct socket.
*/
postcopy_recover_fail(from, to);
/* continue with a good recovery */
}
#endif /* _WIN32 */
/*
* Create a new socket to emulate a new channel that is different
@ -1455,9 +1545,6 @@ static void test_postcopy_recovery_common(MigrateCommon *args)
* Try to rebuild the migration channel using the resume flag and
* the newly created channel
*/
wait_for_migration_status(from, "postcopy-paused",
(const char * []) { "failed", "active",
"completed", NULL });
migrate_qmp(from, uri, "{'resume': true}");
/* Restore the postcopy bandwidth to unlimited */
@ -1482,6 +1569,17 @@ static void test_postcopy_recovery_compress(void)
test_postcopy_recovery_common(&args);
}
#ifndef _WIN32
static void test_postcopy_recovery_double_fail(void)
{
MigrateCommon args = {
.postcopy_recovery_test_fail = true,
};
test_postcopy_recovery_common(&args);
}
#endif /* _WIN32 */
#ifdef CONFIG_GNUTLS
static void test_postcopy_recovery_tls_psk(void)
{
@ -2026,6 +2124,31 @@ static void test_precopy_file_offset_bad(void)
test_file_common(&args, false);
}
static void *test_mode_reboot_start(QTestState *from, QTestState *to)
{
migrate_set_parameter_str(from, "mode", "cpr-reboot");
migrate_set_parameter_str(to, "mode", "cpr-reboot");
migrate_set_capability(from, "x-ignore-shared", true);
migrate_set_capability(to, "x-ignore-shared", true);
return NULL;
}
static void test_mode_reboot(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
FILE_TEST_FILENAME);
MigrateCommon args = {
.start.use_shmem = true,
.connect_uri = uri,
.listen_uri = "defer",
.start_hook = test_mode_reboot_start
};
test_file_common(&args, true);
}
static void test_precopy_tcp_plain(void)
{
MigrateCommon args = {
@ -3068,6 +3191,11 @@ int main(int argc, char **argv)
qtest_add_func("/migration/postcopy/recovery/compress/plain",
test_postcopy_recovery_compress);
}
#ifndef _WIN32
qtest_add_func("/migration/postcopy/recovery/double-failures",
test_postcopy_recovery_double_fail);
#endif /* _WIN32 */
}
qtest_add_func("/migration/bad_dest", test_baddest);
@ -3096,6 +3224,14 @@ int main(int argc, char **argv)
qtest_add_func("/migration/precopy/file/offset/bad",
test_precopy_file_offset_bad);
/*
* Our CI system has problems with shared memory.
* Don't run this test until we find a workaround.
*/
if (getenv("QEMU_TEST_FLAKY_TESTS")) {
qtest_add_func("/migration/mode/reboot", test_mode_reboot);
}
#ifdef CONFIG_GNUTLS
qtest_add_func("/migration/precopy/unix/tls/psk",
test_precopy_unix_tls_psk);