mirror of https://github.com/xemu-project/xemu.git
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>
This commit is contained in:
commit
75b7b25d44
|
@ -1781,7 +1781,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&audio_states, s, list);
|
QTAILQ_INSERT_TAIL(&audio_states, s, list);
|
||||||
QLIST_INIT (&s->card_head);
|
QLIST_INIT (&s->card_head);
|
||||||
vmstate_register (NULL, 0, &vmstate_audio, s);
|
vmstate_register_any(NULL, &vmstate_audio, s);
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -426,8 +426,7 @@ dbus_vmstate_complete(UserCreatable *uc, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
|
if (vmstate_register_any(VMSTATE_IF(self), &dbus_vmstate, self) < 0) {
|
||||||
&dbus_vmstate, self) < 0) {
|
|
||||||
error_setg(errp, "Failed to register vmstate");
|
error_setg(errp, "Failed to register vmstate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -975,8 +975,7 @@ static void tpm_emulator_inst_init(Object *obj)
|
||||||
qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change,
|
qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change,
|
||||||
tpm_emu);
|
tpm_emu);
|
||||||
|
|
||||||
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
|
vmstate_register_any(NULL, &vmstate_tpm_emulator, obj);
|
||||||
&vmstate_tpm_emulator, obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1369,7 +1369,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
bdrv_get_device_or_node_name(bs));
|
bdrv_get_device_or_node_name(bs));
|
||||||
bdrv_graph_rdunlock_main_loop();
|
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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,7 +307,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
bdrv_get_device_or_node_name(bs));
|
bdrv_get_device_or_node_name(bs));
|
||||||
bdrv_graph_rdunlock_main_loop();
|
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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,7 +498,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
bdrv_get_device_or_node_name(bs));
|
bdrv_get_device_or_node_name(bs));
|
||||||
bdrv_graph_rdunlock_main_loop();
|
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) {
|
if (ret < 0) {
|
||||||
goto fail_free_bmap;
|
goto fail_free_bmap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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' "
|
error_setg(&s->migration_blocker, "The vhdx format used by node '%s' "
|
||||||
"does not support live migration",
|
"does not support live migration",
|
||||||
bdrv_get_device_or_node_name(bs));
|
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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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' "
|
error_setg(&s->migration_blocker, "The vmdk format used by node '%s' "
|
||||||
"does not support live migration",
|
"does not support live migration",
|
||||||
bdrv_get_device_or_node_name(bs));
|
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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,7 +452,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
bdrv_get_device_or_node_name(bs));
|
bdrv_get_device_or_node_name(bs));
|
||||||
bdrv_graph_rdunlock_main_loop();
|
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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1268,7 +1268,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
"The vvfat (rw) format used by node '%s' "
|
"The vvfat (rw) format used by node '%s' "
|
||||||
"does not support live migration",
|
"does not support live migration",
|
||||||
bdrv_get_device_or_node_name(bs));
|
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) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,13 +167,17 @@ An example (from hw/input/pckbd.c)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
We are declaring the state with name "pckbd".
|
We are declaring the state with name "pckbd". The ``version_id`` is
|
||||||
The ``version_id`` is 3, and the fields are 4 uint8_t in a KBDState structure.
|
3, and there are 4 uint8_t fields in the KBDState structure. We
|
||||||
We registered this with:
|
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
|
.. 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
|
For devices that are ``qdev`` based, we can register the device in the class
|
||||||
init function:
|
init function:
|
||||||
|
|
|
@ -673,6 +673,20 @@ const PropertyInfo qdev_prop_multifd_compression = {
|
||||||
.set_default_value = qdev_propinfo_set_default_value_enum,
|
.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 --- */
|
/* --- Reserved Region --- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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_common_init(&s->vga, OBJECT(dev), &error_fatal);
|
||||||
vga_init(&s->vga, OBJECT(dev), address_space, io, true);
|
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;
|
s->new_depth = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name)
|
||||||
bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name));
|
bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name));
|
||||||
QLIST_INIT(&bus->current_devs);
|
QLIST_INIT(&bus->current_devs);
|
||||||
QSIMPLEQ_INIT(&bus->pending_masters);
|
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;
|
return bus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2);
|
||||||
ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2);
|
ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2);
|
||||||
ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum));
|
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);
|
ide_bus_register_restart_cb(&s->bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll,
|
||||||
adb_bus);
|
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)
|
static void adb_bus_unrealize(BusState *qbus)
|
||||||
|
|
|
@ -158,7 +158,7 @@ static void ads7846_realize(SSIPeripheral *d, Error **errp)
|
||||||
|
|
||||||
ads7846_int_update(s);
|
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)
|
static void ads7846_class_init(ObjectClass *klass, void *data)
|
||||||
|
|
|
@ -88,6 +88,5 @@ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
|
||||||
}
|
}
|
||||||
s->num_buttons = n;
|
s->num_buttons = n;
|
||||||
qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
|
qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
|
||||||
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
|
vmstate_register_any(NULL, &vmstate_stellaris_gamepad, s);
|
||||||
&vmstate_stellaris_gamepad, s);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -335,8 +335,22 @@ static void icp_realize(DeviceState *dev, Error **errp)
|
||||||
return;
|
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)
|
static void icp_unrealize(DeviceState *dev)
|
||||||
|
|
|
@ -453,19 +453,6 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
|
||||||
continue_send(ibe);
|
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)
|
static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
IPMIBmcExtern *ibe = opaque;
|
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)
|
static void ipmi_bmc_extern_init(Object *obj)
|
||||||
{
|
{
|
||||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
|
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
|
||||||
|
|
||||||
ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
|
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)
|
static void ipmi_bmc_extern_finalize(Object *obj)
|
||||||
|
|
|
@ -68,6 +68,21 @@ static void isa_ipmi_bt_lower_irq(IPMIBT *ib)
|
||||||
qemu_irq_lower(iib->irq);
|
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)
|
static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
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);
|
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);
|
isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
|
||||||
}
|
|
||||||
|
|
||||||
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
|
vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, dev);
|
||||||
.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_init(Object *obj)
|
static void isa_ipmi_bt_init(Object *obj)
|
||||||
{
|
{
|
||||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
|
ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
|
||||||
|
|
||||||
ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
|
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)
|
static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
|
||||||
|
|
|
@ -67,6 +67,24 @@ static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik)
|
||||||
qemu_irq_lower(iik->irq);
|
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)
|
static void ipmi_isa_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
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);
|
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);
|
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
|
* 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);
|
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)
|
static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
|
||||||
{
|
{
|
||||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
|
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
|
||||||
|
|
|
@ -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 = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
|
||||||
s->vmstate->name = qemu_get_queue(s->nic)->model;
|
s->vmstate->name = qemu_get_queue(s->nic)->model;
|
||||||
vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY,
|
vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
|
||||||
s->vmstate, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void eepro100_instance_init(Object *obj)
|
static void eepro100_instance_init(Object *obj)
|
||||||
|
|
|
@ -321,7 +321,7 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords)
|
||||||
/* Output DO is tristate, read results in 1. */
|
/* Output DO is tristate, read results in 1. */
|
||||||
eeprom->eedo = 1;
|
eeprom->eedo = 1;
|
||||||
logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
|
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;
|
return eeprom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ static void pci_bus_realize(BusState *qbus, Error **errp)
|
||||||
bus->machine_done.notify = pcibus_machine_done;
|
bus->machine_done.notify = pcibus_machine_done;
|
||||||
qemu_add_machine_init_done_notifier(&bus->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)
|
static void pcie_bus_realize(BusState *qbus, Error **errp)
|
||||||
|
|
|
@ -143,6 +143,11 @@ static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription pre_2_10_vmstate_dummy_icp = {
|
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",
|
.name = "icp/server",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_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)
|
static void pre_2_10_vmstate_register_dummy_icp(int i)
|
||||||
{
|
{
|
||||||
vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp,
|
vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp,
|
||||||
(void *)(uintptr_t) i);
|
(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)
|
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)
|
int spapr_max_server_number(SpaprMachineState *spapr)
|
||||||
|
|
|
@ -876,8 +876,7 @@ static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp)
|
||||||
s_nvdimm->hcall_flush_required = true;
|
s_nvdimm->hcall_flush_required = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
|
vmstate_register_any(NULL, &vmstate_spapr_nvdimm_states, dimm);
|
||||||
&vmstate_spapr_nvdimm_states, dimm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm)
|
static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/units.h"
|
#include "qemu/units.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
#include "hw/s390x/storage-keys.h"
|
#include "hw/s390x/storage-keys.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-commands-misc-target.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;
|
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 = {
|
static SaveVMHandlers savevm_s390_storage_keys = {
|
||||||
.save_state = s390_storage_keys_save,
|
.save_state = s390_storage_keys_save,
|
||||||
.load_state = s390_storage_keys_load,
|
.load_state = s390_storage_keys_load,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
|
static void s390_skeys_realize(DeviceState *dev, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
S390SKeysState *ss = S390_SKEYS(obj);
|
S390SKeysState *ss = S390_SKEYS(dev);
|
||||||
|
|
||||||
/* Prevent double registration of savevm handler */
|
|
||||||
if (ss->migration_enabled == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ss->migration_enabled = value;
|
|
||||||
|
|
||||||
if (ss->migration_enabled) {
|
if (ss->migration_enabled) {
|
||||||
register_savevm_live(TYPE_S390_SKEYS, 0, 1,
|
register_savevm_live(TYPE_S390_SKEYS, 0, 1,
|
||||||
&savevm_s390_storage_keys, ss);
|
&savevm_s390_storage_keys, ss);
|
||||||
} else {
|
|
||||||
unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s390_skeys_instance_init(Object *obj)
|
static Property s390_skeys_props[] = {
|
||||||
{
|
DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true),
|
||||||
object_property_add_bool(obj, "migration-enabled",
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
s390_skeys_get_migration_enabled,
|
};
|
||||||
s390_skeys_set_migration_enabled);
|
|
||||||
object_property_set_bool(obj, "migration-enabled", true, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s390_skeys_class_init(ObjectClass *oc, void *data)
|
static void s390_skeys_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
|
||||||
dc->hotpluggable = false;
|
dc->hotpluggable = false;
|
||||||
|
dc->realize = s390_skeys_realize;
|
||||||
|
device_class_set_props(dc, s390_skeys_props);
|
||||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo s390_skeys_info = {
|
static const TypeInfo s390_skeys_info = {
|
||||||
.name = TYPE_S390_SKEYS,
|
.name = TYPE_S390_SKEYS,
|
||||||
.parent = TYPE_DEVICE,
|
.parent = TYPE_DEVICE,
|
||||||
.instance_init = s390_skeys_instance_init,
|
|
||||||
.instance_size = sizeof(S390SKeysState),
|
.instance_size = sizeof(S390SKeysState),
|
||||||
.class_init = s390_skeys_class_init,
|
.class_init = s390_skeys_class_init,
|
||||||
.class_size = sizeof(S390SKeysClass),
|
.class_size = sizeof(S390SKeysClass),
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "qemu/units.h"
|
#include "qemu/units.h"
|
||||||
#include "migration/qemu-file.h"
|
#include "migration/qemu-file.h"
|
||||||
#include "migration/register.h"
|
#include "migration/register.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
#include "hw/s390x/storage-attributes.h"
|
#include "hw/s390x/storage-attributes.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "exec/ram_addr.h"
|
#include "exec/ram_addr.h"
|
||||||
|
@ -330,41 +331,6 @@ static const TypeInfo qemu_s390_stattrib_info = {
|
||||||
|
|
||||||
/* Generic abstract object: */
|
/* 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 = {
|
static SaveVMHandlers savevm_s390_stattrib_handlers = {
|
||||||
.save_setup = cmma_save_setup,
|
.save_setup = cmma_save_setup,
|
||||||
.save_live_iterate = cmma_save_iterate,
|
.save_live_iterate = cmma_save_iterate,
|
||||||
|
@ -376,17 +342,39 @@ static SaveVMHandlers savevm_s390_stattrib_handlers = {
|
||||||
.is_active = cmma_active,
|
.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)
|
static void s390_stattrib_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
S390StAttribState *sas = S390_STATTRIB(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;
|
sas->migration_cur_gfn = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
|
||||||
"When external environment supports it (Orchestrator migrates "
|
"When external environment supports it (Orchestrator migrates "
|
||||||
"target SCSI device state or use shared storage over network), "
|
"target SCSI device state or use shared storage over network), "
|
||||||
"set 'migratable' property to true to enable migration.");
|
"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;
|
goto free_virtio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq)
|
||||||
s->control = TIMER_CTRL_IE;
|
s->control = TIMER_CTRL_IE;
|
||||||
|
|
||||||
s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_LEGACY);
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1527,7 +1527,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdev->migration_blocker != NULL) {
|
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) {
|
if (r < 0) {
|
||||||
goto fail_busyloop;
|
goto fail_busyloop;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1119,8 +1119,8 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
|
||||||
host_memory_backend_set_mapped(vmem->memdev, true);
|
host_memory_backend_set_mapped(vmem->memdev, true);
|
||||||
vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem));
|
vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem));
|
||||||
if (vmem->early_migration) {
|
if (vmem->early_migration) {
|
||||||
vmstate_register(VMSTATE_IF(vmem), VMSTATE_INSTANCE_ID_ANY,
|
vmstate_register_any(VMSTATE_IF(vmem),
|
||||||
&vmstate_virtio_mem_device_early, vmem);
|
&vmstate_virtio_mem_device_early, vmem);
|
||||||
}
|
}
|
||||||
qemu_register_reset(virtio_mem_system_reset, vmem);
|
qemu_register_reset(virtio_mem_system_reset, vmem);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ extern const PropertyInfo qdev_prop_chr;
|
||||||
extern const PropertyInfo qdev_prop_macaddr;
|
extern const PropertyInfo qdev_prop_macaddr;
|
||||||
extern const PropertyInfo qdev_prop_reserved_region;
|
extern const PropertyInfo qdev_prop_reserved_region;
|
||||||
extern const PropertyInfo qdev_prop_multifd_compression;
|
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_losttickpolicy;
|
||||||
extern const PropertyInfo qdev_prop_blockdev_on_error;
|
extern const PropertyInfo qdev_prop_blockdev_on_error;
|
||||||
extern const PropertyInfo qdev_prop_bios_chs_trans;
|
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 DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \
|
||||||
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \
|
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \
|
||||||
MultiFDCompression)
|
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 DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
|
||||||
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \
|
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \
|
||||||
LostTickPolicy)
|
LostTickPolicy)
|
||||||
|
|
|
@ -14,8 +14,12 @@
|
||||||
#ifndef MIGRATION_BLOCKER_H
|
#ifndef MIGRATION_BLOCKER_H
|
||||||
#define 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
|
* @reasonp - address of an error to be returned whenever migration is attempted
|
||||||
*
|
*
|
||||||
|
@ -30,8 +34,8 @@
|
||||||
int migrate_add_blocker(Error **reasonp, Error **errp);
|
int migrate_add_blocker(Error **reasonp, Error **errp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @migrate_add_blocker_internal - prevent migration from proceeding without
|
* @migrate_add_blocker_internal - prevent all modes of migration from
|
||||||
* only-migrate implications
|
* proceeding, but ignore -only-migratable
|
||||||
*
|
*
|
||||||
* @reasonp - address of an error to be returned whenever migration is attempted
|
* @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);
|
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
|
* @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);
|
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
|
#endif
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#define MIGRATION_MISC_H
|
#define MIGRATION_MISC_H
|
||||||
|
|
||||||
#include "qemu/notify.h"
|
#include "qemu/notify.h"
|
||||||
|
#include "qapi/qapi-types-migration.h"
|
||||||
#include "qapi/qapi-types-net.h"
|
#include "qapi/qapi-types-net.h"
|
||||||
|
|
||||||
/* migration/ram.c */
|
/* migration/ram.c */
|
||||||
|
|
|
@ -1230,6 +1230,34 @@ static inline int vmstate_register(VMStateIf *obj, int instance_id,
|
||||||
opaque, -1, 0, NULL);
|
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 vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd,
|
||||||
void *opaque);
|
void *opaque);
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
const char *exec_get_cmd_path(void);
|
|
||||||
const char *exec_get_cmd_path(void)
|
const char *exec_get_cmd_path(void)
|
||||||
{
|
{
|
||||||
g_autofree char *detected_path = g_new(char, MAX_PATH);
|
g_autofree char *detected_path = g_new(char, MAX_PATH);
|
||||||
|
@ -40,20 +39,51 @@ const char *exec_get_cmd_path(void)
|
||||||
}
|
}
|
||||||
#endif
|
#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;
|
QIOChannel *ioc;
|
||||||
|
|
||||||
#ifdef WIN32
|
int length = str_list_length(command);
|
||||||
const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL };
|
g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
|
||||||
#else
|
|
||||||
const char *argv[] = { "/bin/sh", "-c", command, NULL };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
trace_migration_exec_outgoing(command);
|
init_exec_array(command, argv, errp);
|
||||||
ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
|
g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
|
||||||
O_RDWR,
|
|
||||||
errp));
|
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) {
|
if (!ioc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -72,20 +102,22 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc,
|
||||||
return G_SOURCE_REMOVE;
|
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;
|
QIOChannel *ioc;
|
||||||
|
|
||||||
#ifdef WIN32
|
int length = str_list_length(command);
|
||||||
const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL };
|
g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
|
||||||
#else
|
|
||||||
const char *argv[] = { "/bin/sh", "-c", command, NULL };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
trace_migration_exec_incoming(command);
|
init_exec_array(command, argv, errp);
|
||||||
ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
|
g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
|
||||||
O_RDWR,
|
|
||||||
errp));
|
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) {
|
if (!ioc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,12 @@
|
||||||
|
|
||||||
#ifndef QEMU_MIGRATION_EXEC_H
|
#ifndef QEMU_MIGRATION_EXEC_H
|
||||||
#define 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);
|
Error **errp);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
/* Remove the offset option from @filespec and return it in @offsetp. */
|
/* 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);
|
char *option = strstr(filespec, OFFSET_OPTION);
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -36,20 +36,16 @@ static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void file_start_outgoing_migration(MigrationState *s, const char *filespec,
|
void file_start_outgoing_migration(MigrationState *s,
|
||||||
Error **errp)
|
FileMigrationArgs *file_args, Error **errp)
|
||||||
{
|
{
|
||||||
g_autofree char *filename = g_strdup(filespec);
|
|
||||||
g_autoptr(QIOChannelFile) fioc = NULL;
|
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;
|
QIOChannel *ioc;
|
||||||
|
|
||||||
trace_migration_file_outgoing(filename);
|
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,
|
fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC,
|
||||||
0600, errp);
|
0600, errp);
|
||||||
if (!fioc) {
|
if (!fioc) {
|
||||||
|
@ -73,19 +69,15 @@ static gboolean file_accept_incoming_migration(QIOChannel *ioc,
|
||||||
return G_SOURCE_REMOVE;
|
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;
|
QIOChannelFile *fioc = NULL;
|
||||||
uint64_t offset = 0;
|
uint64_t offset = file_args->offset;
|
||||||
QIOChannel *ioc;
|
QIOChannel *ioc;
|
||||||
|
|
||||||
trace_migration_file_incoming(filename);
|
trace_migration_file_incoming(filename);
|
||||||
|
|
||||||
if (file_parse_offset(filename, &offset, errp)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp);
|
fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp);
|
||||||
if (!fioc) {
|
if (!fioc) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,8 +7,12 @@
|
||||||
|
|
||||||
#ifndef QEMU_MIGRATION_FILE_H
|
#ifndef QEMU_MIGRATION_FILE_H
|
||||||
#define 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,
|
#include "qapi/qapi-types-migration.h"
|
||||||
Error **errp);
|
|
||||||
|
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
|
#endif
|
||||||
|
|
|
@ -387,6 +387,11 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
|
||||||
monitor_printf(mon, "%s: %" PRIu64 " MB/s\n",
|
monitor_printf(mon, "%s: %" PRIu64 " MB/s\n",
|
||||||
MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
|
MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT),
|
||||||
params->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);
|
qapi_free_MigrationParameters(params);
|
||||||
|
@ -446,9 +451,18 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
const char *uri = qdict_get_str(qdict, "uri");
|
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);
|
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;
|
p->has_vcpu_dirty_limit = true;
|
||||||
visit_type_size(v, param, &p->vcpu_dirty_limit, &err);
|
visit_type_size(v, param, &p->vcpu_dirty_limit, &err);
|
||||||
break;
|
break;
|
||||||
|
case MIGRATION_PARAMETER_MODE:
|
||||||
|
p->has_mode = true;
|
||||||
|
visit_type_MigMode(v, param, &p->mode, &err);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
@ -744,6 +762,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
|
||||||
bool resume = qdict_get_try_bool(qdict, "resume", false);
|
bool resume = qdict_get_try_bool(qdict, "resume", false);
|
||||||
const char *uri = qdict_get_str(qdict, "uri");
|
const char *uri = qdict_get_str(qdict, "uri");
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
MigrationChannelList *caps = NULL;
|
||||||
|
g_autoptr(MigrationChannel) channel = NULL;
|
||||||
|
|
||||||
if (inc) {
|
if (inc) {
|
||||||
warn_report("option '-i' is deprecated;"
|
warn_report("option '-i' is deprecated;"
|
||||||
|
@ -755,12 +775,20 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
|
||||||
" use blockdev-mirror with NBD instead");
|
" use blockdev-mirror with NBD instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
qmp_migrate(uri, !!blk, blk, !!inc, inc,
|
if (!migrate_uri_parse(uri, &channel, &err)) {
|
||||||
false, false, true, resume, &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)) {
|
if (hmp_handle_error(mon, err)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qapi_free_MigrationChannelList(caps);
|
||||||
|
|
||||||
if (!detach) {
|
if (!detach) {
|
||||||
HMPMigrationStatus *status;
|
HMPMigrationStatus *status;
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@
|
||||||
#include "sysemu/qtest.h"
|
#include "sysemu/qtest.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "sysemu/dirtylimit.h"
|
#include "sysemu/dirtylimit.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
|
||||||
static NotifierList migration_state_notifiers =
|
static NotifierList migration_state_notifiers =
|
||||||
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
|
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
|
||||||
|
@ -92,31 +93,55 @@ enum mig_rp_message_type {
|
||||||
static MigrationState *current_migration;
|
static MigrationState *current_migration;
|
||||||
static MigrationIncomingState *current_incoming;
|
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 bool migration_object_check(MigrationState *ms, Error **errp);
|
||||||
static int migration_maybe_pause(MigrationState *s,
|
static int migration_maybe_pause(MigrationState *s,
|
||||||
int *current_active_state,
|
int *current_active_state,
|
||||||
int new_state);
|
int new_state);
|
||||||
static void migrate_fd_cancel(MigrationState *s);
|
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)
|
static bool migration_needs_multiple_sockets(void)
|
||||||
{
|
{
|
||||||
return migrate_multifd() || migrate_postcopy_preempt();
|
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) ||
|
return saddr->type == SOCKET_ADDRESS_TYPE_INET ||
|
||||||
strstart(uri, "vsock:", NULL);
|
saddr->type == SOCKET_ADDRESS_TYPE_UNIX ||
|
||||||
|
saddr->type == SOCKET_ADDRESS_TYPE_VSOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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() &&
|
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)");
|
error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -131,6 +156,15 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp)
|
||||||
return (a > b) - (a < b);
|
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)
|
void migration_object_init(void)
|
||||||
{
|
{
|
||||||
/* This can only be called once. */
|
/* This can only be called once. */
|
||||||
|
@ -423,25 +457,114 @@ void migrate_add_address(SocketAddress *address)
|
||||||
QAPI_CLONE(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();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
|
||||||
MIGRATION_STATUS_SETUP);
|
MIGRATION_STATUS_SETUP);
|
||||||
|
|
||||||
if (strstart(uri, "tcp:", &p) ||
|
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
|
||||||
strstart(uri, "unix:", NULL) ||
|
SocketAddress *saddr = &addr->u.socket;
|
||||||
strstart(uri, "vsock:", NULL)) {
|
if (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
|
||||||
socket_start_incoming_migration(p ? p : uri, errp);
|
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
|
#ifdef CONFIG_RDMA
|
||||||
} else if (strstart(uri, "rdma:", &p)) {
|
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
|
||||||
if (migrate_compress()) {
|
if (migrate_compress()) {
|
||||||
error_setg(errp, "RDMA and compression can't be used together");
|
error_setg(errp, "RDMA and compression can't be used together");
|
||||||
return;
|
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");
|
error_setg(errp, "RDMA and multifd can't be used together");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rdma_start_incoming_migration(p, errp);
|
rdma_start_incoming_migration(&addr->u.rdma, errp);
|
||||||
#endif
|
#endif
|
||||||
} else if (strstart(uri, "exec:", &p)) {
|
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) {
|
||||||
exec_start_incoming_migration(p, errp);
|
exec_start_incoming_migration(addr->u.exec.args, errp);
|
||||||
} else if (strstart(uri, "fd:", &p)) {
|
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
|
||||||
fd_start_incoming_migration(p, errp);
|
file_start_incoming_migration(&addr->u.file, errp);
|
||||||
} else if (strstart(uri, "file:", &p)) {
|
|
||||||
file_start_incoming_migration(p, errp);
|
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "unknown migration protocol: %s", uri);
|
error_setg(errp, "unknown migration protocol: %s", uri);
|
||||||
}
|
}
|
||||||
|
@ -472,6 +593,8 @@ static void process_incoming_migration_bh(void *opaque)
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
MigrationIncomingState *mis = opaque;
|
MigrationIncomingState *mis = opaque;
|
||||||
|
|
||||||
|
trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter");
|
||||||
|
|
||||||
/* If capability late_block_activate is set:
|
/* If capability late_block_activate is set:
|
||||||
* Only fire up the block code now if we're going to restart the
|
* Only fire up the block code now if we're going to restart the
|
||||||
* VM, else 'cont' will do it.
|
* 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());
|
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
|
||||||
|
|
||||||
|
trace_vmstate_downtime_checkpoint("dst-precopy-bh-announced");
|
||||||
|
|
||||||
multifd_load_shutdown();
|
multifd_load_shutdown();
|
||||||
|
|
||||||
dirty_bitmap_mig_before_vm_start();
|
dirty_bitmap_mig_before_vm_start();
|
||||||
|
@ -514,6 +639,7 @@ static void process_incoming_migration_bh(void *opaque)
|
||||||
} else {
|
} else {
|
||||||
runstate_set(global_state_get_runstate());
|
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
|
* 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
|
* 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);
|
ret = qemu_loadvm_state(mis->from_src_file);
|
||||||
mis->loadvm_co = NULL;
|
mis->loadvm_co = NULL;
|
||||||
|
|
||||||
|
trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
|
||||||
|
|
||||||
ps = postcopy_state_get();
|
ps = postcopy_state_get();
|
||||||
trace_process_incoming_migration_co_end(ret, ps);
|
trace_process_incoming_migration_co_end(ret, ps);
|
||||||
if (ps != POSTCOPY_INCOMING_NONE) {
|
if (ps != POSTCOPY_INCOMING_NONE) {
|
||||||
|
@ -1006,7 +1134,7 @@ static void fill_source_migration_info(MigrationInfo *info)
|
||||||
{
|
{
|
||||||
MigrationState *s = migrate_get_current();
|
MigrationState *s = migrate_get_current();
|
||||||
int state = qatomic_read(&s->state);
|
int state = qatomic_read(&s->state);
|
||||||
GSList *cur_blocker = migration_blockers;
|
GSList *cur_blocker = migration_blockers[migrate_mode()];
|
||||||
|
|
||||||
info->blocked_reasons = NULL;
|
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)
|
bool migration_in_postcopy_after_devices(MigrationState *s)
|
||||||
{
|
{
|
||||||
return migration_in_postcopy() && s->postcopy_after_devices;
|
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->to_dst_file = NULL;
|
||||||
s->state = MIGRATION_STATUS_NONE;
|
s->state = MIGRATION_STATUS_NONE;
|
||||||
s->rp_state.from_dst_file = NULL;
|
s->rp_state.from_dst_file = NULL;
|
||||||
s->rp_state.error = false;
|
|
||||||
s->mbps = 0.0;
|
s->mbps = 0.0;
|
||||||
s->pages_per_second = 0.0;
|
s->pages_per_second = 0.0;
|
||||||
s->downtime = 0;
|
s->downtime = 0;
|
||||||
|
@ -1470,44 +1608,112 @@ int migrate_init(MigrationState *s, Error **errp)
|
||||||
return 0;
|
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. */
|
/* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */
|
||||||
if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) {
|
if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) {
|
||||||
error_propagate_prepend(errp, *reasonp,
|
error_propagate_prepend(errp, *reasonp,
|
||||||
"disallowing migration blocker "
|
"disallowing migration blocker "
|
||||||
"(migration/snapshot in progress) for: ");
|
"(migration/snapshot in progress) for: ");
|
||||||
*reasonp = NULL;
|
*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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int migrate_add_blocker(Error **reasonp, Error **errp)
|
int migrate_add_blocker(Error **reasonp, Error **errp)
|
||||||
{
|
{
|
||||||
if (only_migratable) {
|
return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_ALL);
|
||||||
error_propagate_prepend(errp, *reasonp,
|
}
|
||||||
"disallowing migration blocker "
|
|
||||||
"(--only-migratable) for: ");
|
|
||||||
*reasonp = NULL;
|
|
||||||
return -EACCES;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
void migrate_del_blocker(Error **reasonp)
|
||||||
{
|
{
|
||||||
if (*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);
|
error_free(*reasonp);
|
||||||
*reasonp = NULL;
|
*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;
|
Error *local_err = NULL;
|
||||||
static bool once = true;
|
static bool once = true;
|
||||||
|
@ -1525,7 +1731,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_start_incoming_migration(uri, &local_err);
|
qemu_start_incoming_migration(uri, has_channels, channels, &local_err);
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
|
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
|
* only re-setup the migration stream and poke existing migration
|
||||||
* to continue using that newly established channel.
|
* 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)
|
void qmp_migrate_pause(Error **errp)
|
||||||
|
@ -1570,8 +1776,15 @@ void qmp_migrate_pause(Error **errp)
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
if (migration_postcopy_is_alive(ms->state)) {
|
||||||
/* Source side, during postcopy */
|
/* 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);
|
qemu_mutex_lock(&ms->qemu_file_lock);
|
||||||
if (ms->to_dst_file) {
|
if (ms->to_dst_file) {
|
||||||
ret = qemu_file_shutdown(ms->to_dst_file);
|
ret = qemu_file_shutdown(ms->to_dst_file);
|
||||||
|
@ -1580,10 +1793,17 @@ void qmp_migrate_pause(Error **errp)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_setg(errp, "Failed to pause source migration");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) {
|
if (migration_postcopy_is_alive(mis->state)) {
|
||||||
ret = qemu_file_shutdown(mis->from_src_file);
|
ret = qemu_file_shutdown(mis->from_src_file);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_setg(errp, "Failed to pause destination migration");
|
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 "
|
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)
|
bool migration_is_blocked(Error **errp)
|
||||||
{
|
{
|
||||||
|
GSList *blockers = migration_blockers[migrate_mode()];
|
||||||
|
|
||||||
if (qemu_savevm_state_blocked(errp)) {
|
if (qemu_savevm_state_blocked(errp)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (migration_blockers) {
|
if (blockers) {
|
||||||
error_propagate(errp, error_copy(migration_blockers->data));
|
error_propagate(errp, error_copy(blockers->data));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1702,17 +1924,46 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
|
||||||
return true;
|
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_inc, bool inc, bool has_detach, bool detach,
|
||||||
bool has_resume, bool resume, Error **errp)
|
bool has_resume, bool resume, Error **errp)
|
||||||
{
|
{
|
||||||
bool resume_requested;
|
bool resume_requested;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
MigrationState *s = migrate_get_current();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1729,20 +1980,23 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstart(uri, "tcp:", &p) ||
|
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
|
||||||
strstart(uri, "unix:", NULL) ||
|
SocketAddress *saddr = &addr->u.socket;
|
||||||
strstart(uri, "vsock:", NULL)) {
|
if (saddr->type == SOCKET_ADDRESS_TYPE_INET ||
|
||||||
socket_start_outgoing_migration(s, p ? p : uri, &local_err);
|
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
|
#ifdef CONFIG_RDMA
|
||||||
} else if (strstart(uri, "rdma:", &p)) {
|
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
|
||||||
rdma_start_outgoing_migration(s, p, &local_err);
|
rdma_start_outgoing_migration(s, &addr->u.rdma, &local_err);
|
||||||
#endif
|
#endif
|
||||||
} else if (strstart(uri, "exec:", &p)) {
|
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) {
|
||||||
exec_start_outgoing_migration(s, p, &local_err);
|
exec_start_outgoing_migration(s, addr->u.exec.args, &local_err);
|
||||||
} else if (strstart(uri, "fd:", &p)) {
|
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) {
|
||||||
fd_start_outgoing_migration(s, p, &local_err);
|
file_start_outgoing_migration(s, &addr->u.file, &local_err);
|
||||||
} else if (strstart(uri, "file:", &p)) {
|
|
||||||
file_start_outgoing_migration(s, p, &local_err);
|
|
||||||
} else {
|
} else {
|
||||||
error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri",
|
error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri",
|
||||||
"a valid migration protocol");
|
"a valid migration protocol");
|
||||||
|
@ -1777,19 +2031,21 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp)
|
||||||
qemu_sem_post(&s->pause_sem);
|
qemu_sem_post(&s->pause_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* migration thread support */
|
int migration_rp_wait(MigrationState *s)
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
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);
|
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)
|
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)
|
* 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.
|
* 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,
|
static void
|
||||||
ram_addr_t start, size_t len)
|
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();
|
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) ||
|
if (!QEMU_IS_ALIGNED(start, our_host_ps) ||
|
||||||
!QEMU_IS_ALIGNED(len, our_host_ps)) {
|
!QEMU_IS_ALIGNED(len, our_host_ps)) {
|
||||||
error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT
|
error_setg(errp, "MIG_RP_MSG_REQ_PAGES: Misaligned page request, start:"
|
||||||
" len: %zd", __func__, start, len);
|
RAM_ADDR_FMT " len: %zd", start, len);
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ram_save_queue_pages(rbname, start, len)) {
|
ram_save_queue_pages(rbname, start, len, errp);
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
RAMBlock *block = qemu_ram_block_by_name(block_name);
|
||||||
|
|
||||||
if (!block) {
|
if (!block) {
|
||||||
error_report("%s: invalid block name '%s'", __func__, block_name);
|
error_setg(errp, "MIG_RP_MSG_RECV_BITMAP has invalid block name '%s'",
|
||||||
return -EINVAL;
|
block_name);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch the received bitmap and refresh the dirty bitmap */
|
/* 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);
|
trace_source_return_path_thread_resume_ack(value);
|
||||||
|
|
||||||
if (value != MIGRATION_RESUME_ACK_VALUE) {
|
if (value != MIGRATION_RESUME_ACK_VALUE) {
|
||||||
error_report("%s: illegal resume_ack value %"PRIu32,
|
error_setg(errp, "illegal resume_ack value %"PRIu32, value);
|
||||||
__func__, value);
|
return false;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now both sides are active. */
|
/* 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 */
|
/* Notify send thread that time to continue send pages */
|
||||||
migration_rp_kick(s);
|
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;
|
uint32_t tmp32, sibling_error;
|
||||||
ram_addr_t start = 0; /* =0 to silence warning */
|
ram_addr_t start = 0; /* =0 to silence warning */
|
||||||
size_t len = 0, expected_len;
|
size_t len = 0, expected_len;
|
||||||
|
Error *err = NULL;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
trace_source_return_path_thread_entry();
|
trace_source_return_path_thread_entry();
|
||||||
rcu_register_thread();
|
rcu_register_thread();
|
||||||
|
|
||||||
while (!ms->rp_state.error && !qemu_file_get_error(rp) &&
|
while (migration_is_setup_or_active(ms->state)) {
|
||||||
migration_is_setup_or_active(ms->state)) {
|
|
||||||
trace_source_return_path_thread_loop_top();
|
trace_source_return_path_thread_loop_top();
|
||||||
|
|
||||||
header_type = qemu_get_be16(rp);
|
header_type = qemu_get_be16(rp);
|
||||||
header_len = qemu_get_be16(rp);
|
header_len = qemu_get_be16(rp);
|
||||||
|
|
||||||
if (qemu_file_get_error(rp)) {
|
if (qemu_file_get_error(rp)) {
|
||||||
mark_source_rp_bad(ms);
|
qemu_file_get_error_obj(rp, &err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header_type >= MIG_RP_MSG_MAX ||
|
if (header_type >= MIG_RP_MSG_MAX ||
|
||||||
header_type == MIG_RP_MSG_INVALID) {
|
header_type == MIG_RP_MSG_INVALID) {
|
||||||
error_report("RP: Received invalid message 0x%04x length 0x%04x",
|
error_setg(&err, "Received invalid message 0x%04x length 0x%04x",
|
||||||
header_type, header_len);
|
header_type, header_len);
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rp_cmd_args[header_type].len != -1 &&
|
if ((rp_cmd_args[header_type].len != -1 &&
|
||||||
header_len != rp_cmd_args[header_type].len) ||
|
header_len != rp_cmd_args[header_type].len) ||
|
||||||
header_len > sizeof(buf)) {
|
header_len > sizeof(buf)) {
|
||||||
error_report("RP: Received '%s' message (0x%04x) with"
|
error_setg(&err, "Received '%s' message (0x%04x) with"
|
||||||
"incorrect length %d expecting %zu",
|
"incorrect length %d expecting %zu",
|
||||||
rp_cmd_args[header_type].name, header_type, header_len,
|
rp_cmd_args[header_type].name, header_type, header_len,
|
||||||
(size_t)rp_cmd_args[header_type].len);
|
(size_t)rp_cmd_args[header_type].len);
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We know we've got a valid header by this point */
|
/* We know we've got a valid header by this point */
|
||||||
res = qemu_get_buffer(rp, buf, header_len);
|
res = qemu_get_buffer(rp, buf, header_len);
|
||||||
if (res != header_len) {
|
if (res != header_len) {
|
||||||
error_report("RP: Failed reading data for message 0x%04x"
|
error_setg(&err, "Failed reading data for message 0x%04x"
|
||||||
" read %d expected %d",
|
" read %d expected %d",
|
||||||
header_type, res, header_len);
|
header_type, res, header_len);
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1970,8 +2224,7 @@ static void *source_return_path_thread(void *opaque)
|
||||||
sibling_error = ldl_be_p(buf);
|
sibling_error = ldl_be_p(buf);
|
||||||
trace_source_return_path_thread_shut(sibling_error);
|
trace_source_return_path_thread_shut(sibling_error);
|
||||||
if (sibling_error) {
|
if (sibling_error) {
|
||||||
error_report("RP: Sibling indicated error %d", sibling_error);
|
error_setg(&err, "Sibling indicated error %d", sibling_error);
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* We'll let the main thread deal with closing the RP
|
* 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:
|
case MIG_RP_MSG_REQ_PAGES:
|
||||||
start = ldq_be_p(buf);
|
start = ldq_be_p(buf);
|
||||||
len = ldl_be_p(buf + 8);
|
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;
|
break;
|
||||||
|
|
||||||
case MIG_RP_MSG_REQ_PAGES_ID:
|
case MIG_RP_MSG_REQ_PAGES_ID:
|
||||||
|
@ -2004,32 +2260,32 @@ static void *source_return_path_thread(void *opaque)
|
||||||
expected_len += tmp32;
|
expected_len += tmp32;
|
||||||
}
|
}
|
||||||
if (header_len != expected_len) {
|
if (header_len != expected_len) {
|
||||||
error_report("RP: Req_Page_id with length %d expecting %zd",
|
error_setg(&err, "Req_Page_id with length %d expecting %zd",
|
||||||
header_len, expected_len);
|
header_len, expected_len);
|
||||||
mark_source_rp_bad(ms);
|
goto out;
|
||||||
|
}
|
||||||
|
migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len,
|
||||||
|
&err);
|
||||||
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MIG_RP_MSG_RECV_BITMAP:
|
case MIG_RP_MSG_RECV_BITMAP:
|
||||||
if (header_len < 1) {
|
if (header_len < 1) {
|
||||||
error_report("%s: missing block name", __func__);
|
error_setg(&err, "MIG_RP_MSG_RECV_BITMAP missing block name");
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
/* Format: len (1B) + idstr (<255B). This ends the idstr. */
|
/* Format: len (1B) + idstr (<255B). This ends the idstr. */
|
||||||
buf[buf[0] + 1] = '\0';
|
buf[buf[0] + 1] = '\0';
|
||||||
if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) {
|
if (!migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) {
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MIG_RP_MSG_RESUME_ACK:
|
case MIG_RP_MSG_RESUME_ACK:
|
||||||
tmp32 = ldl_be_p(buf);
|
tmp32 = ldl_be_p(buf);
|
||||||
if (migrate_handle_rp_resume_ack(ms, tmp32)) {
|
if (!migrate_handle_rp_resume_ack(ms, tmp32, &err)) {
|
||||||
mark_source_rp_bad(ms);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2045,13 +2301,29 @@ static void *source_return_path_thread(void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (qemu_file_get_error(rp)) {
|
if (err) {
|
||||||
|
migrate_set_error(ms, err);
|
||||||
|
error_free(err);
|
||||||
trace_source_return_path_thread_bad_end();
|
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();
|
trace_source_return_path_thread_end();
|
||||||
rcu_unregister_thread();
|
rcu_unregister_thread();
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2073,12 +2345,11 @@ static int open_return_path_on_source(MigrationState *ms)
|
||||||
return 0;
|
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) {
|
if (!ms->rp_state.rp_thread_created) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_migration_return_path_end_before();
|
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);
|
qemu_thread_join(&ms->rp_state.rp_thread);
|
||||||
ms->rp_state.rp_thread_created = false;
|
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);
|
migration_release_dst_files(ms);
|
||||||
|
trace_migration_return_path_end_after();
|
||||||
|
|
||||||
trace_migration_return_path_end_after(ret);
|
/* Return path will persist the error in MigrationState when quit */
|
||||||
return ret;
|
return migrate_has_error(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -2126,7 +2392,6 @@ static int postcopy_start(MigrationState *ms, Error **errp)
|
||||||
int ret;
|
int ret;
|
||||||
QIOChannelBuffer *bioc;
|
QIOChannelBuffer *bioc;
|
||||||
QEMUFile *fb;
|
QEMUFile *fb;
|
||||||
int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
|
||||||
uint64_t bandwidth = migrate_max_postcopy_bandwidth();
|
uint64_t bandwidth = migrate_max_postcopy_bandwidth();
|
||||||
bool restart_block = false;
|
bool restart_block = false;
|
||||||
int cur_state = MIGRATION_STATUS_ACTIVE;
|
int cur_state = MIGRATION_STATUS_ACTIVE;
|
||||||
|
@ -2148,9 +2413,11 @@ static int postcopy_start(MigrationState *ms, Error **errp)
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
trace_postcopy_start_set_run();
|
trace_postcopy_start_set_run();
|
||||||
|
|
||||||
|
migration_downtime_start(ms);
|
||||||
|
|
||||||
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
|
||||||
global_state_store();
|
global_state_store();
|
||||||
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
|
ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -2250,7 +2517,7 @@ static int postcopy_start(MigrationState *ms, Error **errp)
|
||||||
ms->postcopy_after_devices = true;
|
ms->postcopy_after_devices = true;
|
||||||
migration_call_notifiers(ms);
|
migration_call_notifiers(ms);
|
||||||
|
|
||||||
ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop;
|
migration_downtime_end(ms);
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
|
||||||
|
@ -2346,13 +2613,13 @@ static int migration_completion_precopy(MigrationState *s,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
qemu_mutex_lock_iothread();
|
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);
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
|
||||||
|
|
||||||
s->vm_old_state = runstate_get();
|
s->vm_old_state = runstate_get();
|
||||||
global_state_store();
|
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);
|
trace_migration_completion_vm_stop(ret);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
@ -2519,7 +2786,9 @@ static int postcopy_resume_handshake(MigrationState *s)
|
||||||
qemu_savevm_send_postcopy_resume(s->to_dst_file);
|
qemu_savevm_send_postcopy_resume(s->to_dst_file);
|
||||||
|
|
||||||
while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) {
|
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) {
|
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 end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
||||||
int64_t transfer_time;
|
int64_t transfer_time;
|
||||||
|
|
||||||
|
migration_downtime_end(s);
|
||||||
s->total_time = end_time - s->start_time;
|
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;
|
transfer_time = s->total_time - s->setup_time;
|
||||||
if (transfer_time) {
|
if (transfer_time) {
|
||||||
s->mbps = ((double) bytes * 8.0) / transfer_time / 1000;
|
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;
|
s->vm_start_bh = NULL;
|
||||||
|
|
||||||
vm_start();
|
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;
|
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
|
||||||
|
|
||||||
trace_migration_thread_setup_complete();
|
trace_migration_thread_setup_complete();
|
||||||
s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
|
migration_downtime_start(s);
|
||||||
|
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
|
|
||||||
|
@ -3210,7 +3472,7 @@ static void *bg_migration_thread(void *opaque)
|
||||||
|
|
||||||
global_state_store();
|
global_state_store();
|
||||||
/* Forcibly stop VM before saving state of vCPUs and devices */
|
/* 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;
|
goto fail;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -308,7 +308,6 @@ struct MigrationState {
|
||||||
/* Protected by qemu_file_lock */
|
/* Protected by qemu_file_lock */
|
||||||
QEMUFile *from_dst_file;
|
QEMUFile *from_dst_file;
|
||||||
QemuThread rp_thread;
|
QemuThread rp_thread;
|
||||||
bool error;
|
|
||||||
/*
|
/*
|
||||||
* We can also check non-zero of rp_thread, but there's no "official"
|
* 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.
|
* 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);
|
bool migration_is_blocked(Error **errp);
|
||||||
/* True if outgoing migration has entered postcopy phase */
|
/* True if outgoing migration has entered postcopy phase */
|
||||||
bool migration_in_postcopy(void);
|
bool migration_in_postcopy(void);
|
||||||
|
bool migration_postcopy_is_alive(int state);
|
||||||
MigrationState *migrate_get_current(void);
|
MigrationState *migrate_get_current(void);
|
||||||
|
|
||||||
uint64_t ram_get_total_transferred_pages(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);
|
Error **errp);
|
||||||
|
|
||||||
void migrate_add_address(SocketAddress *address);
|
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);
|
int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque);
|
||||||
|
|
||||||
#define qemu_ram_foreach_block \
|
#define qemu_ram_foreach_block \
|
||||||
|
@ -535,8 +536,11 @@ void migration_populate_vfio_info(MigrationInfo *info);
|
||||||
void migration_reset_vfio_bytes_transferred(void);
|
void migration_reset_vfio_bytes_transferred(void);
|
||||||
void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page);
|
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
|
* Kick the migration thread waiting for return path messages. NOTE: the
|
||||||
* name can be slightly confusing (when read as "kick the rp thread"), just
|
* 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);
|
void migration_rp_kick(MigrationState *s);
|
||||||
|
|
||||||
|
int migration_stop_vm(RunState state);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -176,6 +176,9 @@ Property migration_properties[] = {
|
||||||
DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState,
|
DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState,
|
||||||
parameters.vcpu_dirty_limit,
|
parameters.vcpu_dirty_limit,
|
||||||
DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT),
|
DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT),
|
||||||
|
DEFINE_PROP_MIG_MODE("mode", MigrationState,
|
||||||
|
parameters.mode,
|
||||||
|
MIG_MODE_NORMAL),
|
||||||
|
|
||||||
/* Migration capabilities */
|
/* Migration capabilities */
|
||||||
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
|
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;
|
return s->parameters.max_postcopy_bandwidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MigMode migrate_mode(void)
|
||||||
|
{
|
||||||
|
MigrationState *s = migrate_get_current();
|
||||||
|
|
||||||
|
return s->parameters.mode;
|
||||||
|
}
|
||||||
|
|
||||||
int migrate_multifd_channels(void)
|
int migrate_multifd_channels(void)
|
||||||
{
|
{
|
||||||
MigrationState *s = migrate_get_current();
|
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->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period;
|
||||||
params->has_vcpu_dirty_limit = true;
|
params->has_vcpu_dirty_limit = true;
|
||||||
params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
|
params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
|
||||||
|
params->has_mode = true;
|
||||||
|
params->mode = s->parameters.mode;
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -1034,6 +1046,7 @@ void migrate_params_init(MigrationParameters *params)
|
||||||
params->has_announce_step = true;
|
params->has_announce_step = true;
|
||||||
params->has_x_vcpu_dirty_limit_period = true;
|
params->has_x_vcpu_dirty_limit_period = true;
|
||||||
params->has_vcpu_dirty_limit = 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) {
|
if (params->has_vcpu_dirty_limit) {
|
||||||
dest->vcpu_dirty_limit = params->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)
|
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) {
|
if (params->has_vcpu_dirty_limit) {
|
||||||
s->parameters.vcpu_dirty_limit = params->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)
|
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
|
||||||
|
|
|
@ -83,6 +83,7 @@ uint8_t migrate_max_cpu_throttle(void);
|
||||||
uint64_t migrate_max_bandwidth(void);
|
uint64_t migrate_max_bandwidth(void);
|
||||||
uint64_t migrate_avail_switchover_bandwidth(void);
|
uint64_t migrate_avail_switchover_bandwidth(void);
|
||||||
uint64_t migrate_max_postcopy_bandwidth(void);
|
uint64_t migrate_max_postcopy_bandwidth(void);
|
||||||
|
MigMode migrate_mode(void);
|
||||||
int migrate_multifd_channels(void);
|
int migrate_multifd_channels(void);
|
||||||
MultiFDCompression migrate_multifd_compression(void);
|
MultiFDCompression migrate_multifd_compression(void);
|
||||||
int migrate_multifd_zlib_level(void);
|
int migrate_multifd_zlib_level(void);
|
||||||
|
|
|
@ -137,7 +137,7 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc)
|
||||||
*
|
*
|
||||||
* If errp is specified, a verbose error message will be copied over.
|
* 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) {
|
if (!f->last_error) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -68,6 +68,7 @@ int coroutine_mixed_fn qemu_peek_byte(QEMUFile *f, int offset);
|
||||||
void qemu_file_skip(QEMUFile *f, int size);
|
void qemu_file_skip(QEMUFile *f, int size);
|
||||||
int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp);
|
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);
|
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);
|
void qemu_file_set_error(QEMUFile *f, int ret);
|
||||||
int qemu_file_shutdown(QEMUFile *f);
|
int qemu_file_shutdown(QEMUFile *f);
|
||||||
QEMUFile *qemu_file_get_return_path(QEMUFile *f);
|
QEMUFile *qemu_file_get_return_path(QEMUFile *f);
|
||||||
|
|
|
@ -1898,7 +1898,8 @@ static void migration_page_queue_free(RAMState *rs)
|
||||||
* @start: starting address from the start of the RAMBlock
|
* @start: starting address from the start of the RAMBlock
|
||||||
* @len: length (in bytes) to send
|
* @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;
|
RAMBlock *ramblock;
|
||||||
RAMState *rs = ram_state;
|
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
|
* Shouldn't happen, we can't reuse the last RAMBlock if
|
||||||
* it's the 1st request.
|
* 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;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1923,16 +1924,17 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len)
|
||||||
|
|
||||||
if (!ramblock) {
|
if (!ramblock) {
|
||||||
/* We shouldn't be asked for a non-existent 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;
|
return -1;
|
||||||
}
|
}
|
||||||
rs->last_req_rb = ramblock;
|
rs->last_req_rb = ramblock;
|
||||||
}
|
}
|
||||||
trace_ram_save_queue_pages(ramblock->idstr, start, len);
|
trace_ram_save_queue_pages(ramblock->idstr, start, len);
|
||||||
if (!offset_in_ramblock(ramblock, start + len - 1)) {
|
if (!offset_in_ramblock(ramblock, start + len - 1)) {
|
||||||
error_report("%s request overrun start=" RAM_ADDR_FMT " len="
|
error_setg(errp, "MIG_RP_MSG_REQ_PAGES request overrun, "
|
||||||
RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT,
|
"start=" RAM_ADDR_FMT " len="
|
||||||
__func__, start, len, ramblock->used_length);
|
RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT,
|
||||||
|
start, len, ramblock->used_length);
|
||||||
return -1;
|
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);
|
assert(len % page_size == 0);
|
||||||
while (len) {
|
while (len) {
|
||||||
if (ram_save_host_page_urgent(pss)) {
|
if (ram_save_host_page_urgent(pss)) {
|
||||||
error_report("%s: ram_save_host_page_urgent() failed: "
|
error_setg(errp, "ram_save_host_page_urgent() failed: "
|
||||||
"ramblock=%s, start_addr=0x"RAM_ADDR_FMT,
|
"ramblock=%s, start_addr=0x"RAM_ADDR_FMT,
|
||||||
__func__, ramblock->idstr, start);
|
ramblock->idstr, start);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4097,7 +4099,9 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs)
|
||||||
|
|
||||||
/* Wait until all the ramblocks' dirty bitmap synced */
|
/* Wait until all the ramblocks' dirty bitmap synced */
|
||||||
while (qatomic_read(&rs->postcopy_bmap_sync_requested)) {
|
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();
|
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.
|
* Read the received bitmap, revert it as the initial dirty bitmap.
|
||||||
* This is only used when the postcopy migration is paused but wants
|
* This is only used when the postcopy migration is paused but wants
|
||||||
* to resume from a middle point.
|
* 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 */
|
/* from_dst_file is always valid because we're within rp_thread */
|
||||||
QEMUFile *file = s->rp_state.from_dst_file;
|
QEMUFile *file = s->rp_state.from_dst_file;
|
||||||
g_autofree unsigned long *le_bitmap = NULL;
|
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);
|
trace_ram_dirty_bitmap_reload_begin(block->idstr);
|
||||||
|
|
||||||
if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) {
|
if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) {
|
||||||
error_report("%s: incorrect state %s", __func__,
|
error_setg(errp, "Reload bitmap in incorrect state %s",
|
||||||
MigrationStatus_str(s->state));
|
MigrationStatus_str(s->state));
|
||||||
return -EINVAL;
|
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 */
|
/* The size of the bitmap should match with our ramblock */
|
||||||
if (size != local_size) {
|
if (size != local_size) {
|
||||||
error_report("%s: ramblock '%s' bitmap size mismatch "
|
error_setg(errp, "ramblock '%s' bitmap size mismatch (0x%"PRIx64
|
||||||
"(0x%"PRIx64" != 0x%"PRIx64")", __func__,
|
" != 0x%"PRIx64")", block->idstr, size, local_size);
|
||||||
block->idstr, size, local_size);
|
return false;
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size);
|
size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size);
|
||||||
end_mark = qemu_get_be64(file);
|
end_mark = qemu_get_be64(file);
|
||||||
|
|
||||||
ret = qemu_file_get_error(file);
|
if (qemu_file_get_error(file) || size != local_size) {
|
||||||
if (ret || size != local_size) {
|
error_setg(errp, "read bitmap failed for ramblock '%s': "
|
||||||
error_report("%s: read bitmap failed for ramblock '%s': %d"
|
"(size 0x%"PRIx64", got: 0x%"PRIx64")",
|
||||||
" (size 0x%"PRIx64", got: 0x%"PRIx64")",
|
block->idstr, local_size, size);
|
||||||
__func__, block->idstr, ret, local_size, size);
|
return false;
|
||||||
return -EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) {
|
if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) {
|
||||||
error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIx64,
|
error_setg(errp, "ramblock '%s' end mark incorrect: 0x%"PRIx64,
|
||||||
__func__, block->idstr, end_mark);
|
block->idstr, end_mark);
|
||||||
return -EINVAL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4194,7 +4197,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block)
|
||||||
*/
|
*/
|
||||||
migration_rp_kick(s);
|
migration_rp_kick(s);
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ram_resume_prepare(MigrationState *s, void *opaque)
|
static int ram_resume_prepare(MigrationState *s, void *opaque)
|
||||||
|
|
|
@ -50,7 +50,8 @@ uint64_t ram_bytes_total(void);
|
||||||
void mig_throttle_counter_reset(void);
|
void mig_throttle_counter_reset(void);
|
||||||
|
|
||||||
uint64_t ram_pagesize_summary(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);
|
void ram_postcopy_migrated_memory_release(MigrationState *ms);
|
||||||
/* For outgoing discard bitmap */
|
/* For outgoing discard bitmap */
|
||||||
void ram_postcopy_send_discard_bitmap(MigrationState *ms);
|
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);
|
void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr);
|
||||||
int64_t ramblock_recv_bitmap_send(QEMUFile *file,
|
int64_t ramblock_recv_bitmap_send(QEMUFile *file,
|
||||||
const char *block_name);
|
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);
|
bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start);
|
||||||
void postcopy_preempt_shutdown_file(MigrationState *s);
|
void postcopy_preempt_shutdown_file(MigrationState *s);
|
||||||
void *postcopy_preempt_thread(void *opaque);
|
void *postcopy_preempt_thread(void *opaque);
|
||||||
|
|
|
@ -289,7 +289,6 @@ typedef struct RDMALocalBlocks {
|
||||||
typedef struct RDMAContext {
|
typedef struct RDMAContext {
|
||||||
char *host;
|
char *host;
|
||||||
int port;
|
int port;
|
||||||
char *host_port;
|
|
||||||
|
|
||||||
RDMAWorkRequestData wr_data[RDMA_WRID_MAX];
|
RDMAWorkRequestData wr_data[RDMA_WRID_MAX];
|
||||||
|
|
||||||
|
@ -2431,9 +2430,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
|
||||||
rdma->channel = NULL;
|
rdma->channel = NULL;
|
||||||
}
|
}
|
||||||
g_free(rdma->host);
|
g_free(rdma->host);
|
||||||
g_free(rdma->host_port);
|
|
||||||
rdma->host = NULL;
|
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;
|
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;
|
RDMAContext *rdma = NULL;
|
||||||
InetSocketAddress *addr;
|
|
||||||
|
|
||||||
rdma = g_new0(RDMAContext, 1);
|
rdma = g_new0(RDMAContext, 1);
|
||||||
rdma->current_index = -1;
|
rdma->current_index = -1;
|
||||||
rdma->current_chunk = -1;
|
rdma->current_chunk = -1;
|
||||||
|
|
||||||
addr = g_new(InetSocketAddress, 1);
|
rdma->host = g_strdup(saddr->host);
|
||||||
if (!inet_parse(addr, host_port, NULL)) {
|
rdma->port = atoi(saddr->port);
|
||||||
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);
|
|
||||||
return rdma;
|
return rdma;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3353,6 +3338,7 @@ static int qemu_rdma_accept(RDMAContext *rdma)
|
||||||
.private_data_len = sizeof(cap),
|
.private_data_len = sizeof(cap),
|
||||||
};
|
};
|
||||||
RDMAContext *rdma_return_path = NULL;
|
RDMAContext *rdma_return_path = NULL;
|
||||||
|
g_autoptr(InetSocketAddress) isock = g_new0(InetSocketAddress, 1);
|
||||||
struct rdma_cm_event *cm_event;
|
struct rdma_cm_event *cm_event;
|
||||||
struct ibv_context *verbs;
|
struct ibv_context *verbs;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -3367,13 +3353,16 @@ static int qemu_rdma_accept(RDMAContext *rdma)
|
||||||
goto err_rdma_dest_wait;
|
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
|
* initialize the RDMAContext for return path for postcopy after first
|
||||||
* connection request reached.
|
* connection request reached.
|
||||||
*/
|
*/
|
||||||
if ((migrate_postcopy() || migrate_return_path())
|
if ((migrate_postcopy() || migrate_return_path())
|
||||||
&& !rdma->is_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) {
|
if (rdma_return_path == NULL) {
|
||||||
rdma_ack_cm_event(cm_event);
|
rdma_ack_cm_event(cm_event);
|
||||||
goto err_rdma_dest_wait;
|
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();
|
MigrationState *s = migrate_get_current();
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -4118,13 +4108,12 @@ cleanup_rdma:
|
||||||
err:
|
err:
|
||||||
if (rdma) {
|
if (rdma) {
|
||||||
g_free(rdma->host);
|
g_free(rdma->host);
|
||||||
g_free(rdma->host_port);
|
|
||||||
}
|
}
|
||||||
g_free(rdma);
|
g_free(rdma);
|
||||||
}
|
}
|
||||||
|
|
||||||
void rdma_start_outgoing_migration(void *opaque,
|
void rdma_start_outgoing_migration(void *opaque,
|
||||||
const char *host_port, Error **errp)
|
InetSocketAddress *host_port, Error **errp)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
MigrationState *s = opaque;
|
||||||
RDMAContext *rdma_return_path = NULL;
|
RDMAContext *rdma_return_path = NULL;
|
||||||
|
|
|
@ -14,15 +14,17 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
|
||||||
#ifndef QEMU_MIGRATION_RDMA_H
|
#ifndef QEMU_MIGRATION_RDMA_H
|
||||||
#define QEMU_MIGRATION_RDMA_H
|
#define QEMU_MIGRATION_RDMA_H
|
||||||
|
|
||||||
#include "exec/memory.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);
|
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
|
* Constants used by rdma return codes
|
||||||
|
|
|
@ -237,6 +237,8 @@ static SaveState savevm_state = {
|
||||||
.global_section_id = 0,
|
.global_section_id = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id);
|
||||||
|
|
||||||
static bool should_validate_capability(int capability)
|
static bool should_validate_capability(int capability)
|
||||||
{
|
{
|
||||||
assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX);
|
assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX);
|
||||||
|
@ -716,6 +718,18 @@ static void savevm_state_handler_insert(SaveStateEntry *nse)
|
||||||
|
|
||||||
assert(priority <= MIG_PRI_MAX);
|
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--) {
|
for (i = priority - 1; i >= 0; i--) {
|
||||||
se = savevm_state.handler_pri_head[i];
|
se = savevm_state.handler_pri_head[i];
|
||||||
if (se != NULL) {
|
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,
|
int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id,
|
||||||
const VMStateDescription *vmsd,
|
const VMStateDescription *vmsd,
|
||||||
void *opaque, int alias_id,
|
void *opaque, int alias_id,
|
||||||
|
@ -1459,6 +1491,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
|
||||||
static
|
static
|
||||||
int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
|
int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
|
||||||
{
|
{
|
||||||
|
int64_t start_ts_each, end_ts_each;
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1475,6 +1508,8 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
|
||||||
trace_savevm_section_start(se->idstr, se->section_id);
|
trace_savevm_section_start(se->idstr, se->section_id);
|
||||||
|
|
||||||
save_section_header(f, se, QEMU_VM_SECTION_END);
|
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);
|
qemu_file_set_error(f, ret);
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1496,6 +1536,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
|
||||||
bool inactivate_disks)
|
bool inactivate_disks)
|
||||||
{
|
{
|
||||||
MigrationState *ms = migrate_get_current();
|
MigrationState *ms = migrate_get_current();
|
||||||
|
int64_t start_ts_each, end_ts_each;
|
||||||
JSONWriter *vmdesc = ms->vmdesc;
|
JSONWriter *vmdesc = ms->vmdesc;
|
||||||
int vmdesc_len;
|
int vmdesc_len;
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
|
@ -1507,11 +1548,17 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
|
||||||
|
|
||||||
ret = vmstate_save(f, se, vmdesc);
|
ret = vmstate_save(f, se, vmdesc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
qemu_file_set_error(f, ret);
|
qemu_file_set_error(f, ret);
|
||||||
return 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) {
|
if (inactivate_disks) {
|
||||||
|
@ -1547,6 +1594,8 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
|
||||||
json_writer_free(vmdesc);
|
json_writer_free(vmdesc);
|
||||||
ms->vmdesc = NULL;
|
ms->vmdesc = NULL;
|
||||||
|
|
||||||
|
trace_vmstate_downtime_checkpoint("src-non-iterable-saved");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2088,18 +2137,18 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
MigrationIncomingState *mis = opaque;
|
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
|
/* TODO we should move all of this lot into postcopy_ram.c or a shared code
|
||||||
* in migration.c
|
* in migration.c
|
||||||
*/
|
*/
|
||||||
cpu_synchronize_all_post_init();
|
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());
|
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.
|
/* Make sure all file formats throw away their mutable metadata.
|
||||||
* If we get an error here, just don't restart the VM yet. */
|
* 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;
|
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();
|
dirty_bitmap_mig_before_vm_start();
|
||||||
|
|
||||||
|
@ -2124,7 +2173,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
||||||
|
|
||||||
qemu_bh_delete(mis->bh);
|
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 */
|
/* 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
|
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;
|
uint32_t instance_id, version_id, section_id;
|
||||||
|
int64_t start_ts, end_ts;
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
char idstr[256];
|
char idstr[256];
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -2556,12 +2608,23 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trace_downtime) {
|
||||||
|
start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
|
||||||
|
}
|
||||||
|
|
||||||
ret = vmstate_load(f, se);
|
ret = vmstate_load(f, se);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error while loading state for instance 0x%"PRIx32" of"
|
error_report("error while loading state for instance 0x%"PRIx32" of"
|
||||||
" device '%s'", instance_id, idstr);
|
" device '%s'", instance_id, idstr);
|
||||||
return ret;
|
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)) {
|
if (!check_section_footer(f, se)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -2570,8 +2633,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
uint32_t section_id;
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -2596,12 +2662,23 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trace_downtime) {
|
||||||
|
start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
|
||||||
|
}
|
||||||
|
|
||||||
ret = vmstate_load(f, se);
|
ret = vmstate_load(f, se);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error while loading state section id %d(%s)",
|
error_report("error while loading state section id %d(%s)",
|
||||||
section_id, se->idstr);
|
section_id, se->idstr);
|
||||||
return ret;
|
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)) {
|
if (!check_section_footer(f, se)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -2790,14 +2867,14 @@ retry:
|
||||||
switch (section_type) {
|
switch (section_type) {
|
||||||
case QEMU_VM_SECTION_START:
|
case QEMU_VM_SECTION_START:
|
||||||
case QEMU_VM_SECTION_FULL:
|
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) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEMU_VM_SECTION_PART:
|
case QEMU_VM_SECTION_PART:
|
||||||
case QEMU_VM_SECTION_END:
|
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) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "postcopy-ram.h"
|
#include "postcopy-ram.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "qapi/clone-visitor.h"
|
||||||
|
#include "qapi/qapi-visit-sockets.h"
|
||||||
|
|
||||||
struct SocketOutgoingArgs {
|
struct SocketOutgoingArgs {
|
||||||
SocketAddress *saddr;
|
SocketAddress *saddr;
|
||||||
|
@ -108,19 +110,19 @@ out:
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void socket_start_outgoing_migration(MigrationState *s,
|
||||||
socket_start_outgoing_migration_internal(MigrationState *s,
|
SocketAddress *saddr,
|
||||||
SocketAddress *saddr,
|
Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
QIOChannelSocket *sioc = qio_channel_socket_new();
|
QIOChannelSocket *sioc = qio_channel_socket_new();
|
||||||
struct SocketConnectData *data = g_new0(struct SocketConnectData, 1);
|
struct SocketConnectData *data = g_new0(struct SocketConnectData, 1);
|
||||||
|
SocketAddress *addr = QAPI_CLONE(SocketAddress, saddr);
|
||||||
|
|
||||||
data->s = s;
|
data->s = s;
|
||||||
|
|
||||||
/* in case previous migration leaked it */
|
/* in case previous migration leaked it */
|
||||||
qapi_free_SocketAddress(outgoing_args.saddr);
|
qapi_free_SocketAddress(outgoing_args.saddr);
|
||||||
outgoing_args.saddr = saddr;
|
outgoing_args.saddr = addr;
|
||||||
|
|
||||||
if (saddr->type == SOCKET_ADDRESS_TYPE_INET) {
|
if (saddr->type == SOCKET_ADDRESS_TYPE_INET) {
|
||||||
data->hostname = g_strdup(saddr->u.inet.host);
|
data->hostname = g_strdup(saddr->u.inet.host);
|
||||||
|
@ -135,18 +137,6 @@ socket_start_outgoing_migration_internal(MigrationState *s,
|
||||||
NULL);
|
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,
|
static void socket_accept_incoming_migration(QIONetListener *listener,
|
||||||
QIOChannelSocket *cioc,
|
QIOChannelSocket *cioc,
|
||||||
gpointer opaque)
|
gpointer opaque)
|
||||||
|
@ -172,9 +162,8 @@ socket_incoming_migration_end(void *opaque)
|
||||||
object_unref(OBJECT(listener));
|
object_unref(OBJECT(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void socket_start_incoming_migration(SocketAddress *saddr,
|
||||||
socket_start_incoming_migration_internal(SocketAddress *saddr,
|
Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
QIONetListener *listener = qio_net_listener_new();
|
QIONetListener *listener = qio_net_listener_new();
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,13 +19,14 @@
|
||||||
|
|
||||||
#include "io/channel.h"
|
#include "io/channel.h"
|
||||||
#include "io/task.h"
|
#include "io/task.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
|
||||||
void socket_send_channel_create(QIOTaskFunc f, void *data);
|
void socket_send_channel_create(QIOTaskFunc f, void *data);
|
||||||
QIOChannel *socket_send_channel_create_sync(Error **errp);
|
QIOChannel *socket_send_channel_create_sync(Error **errp);
|
||||||
int socket_send_channel_destroy(QIOChannel *send);
|
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,
|
void socket_start_outgoing_migration(MigrationState *s,
|
||||||
Error **errp);
|
SocketAddress *saddr, Error **errp);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,7 +17,6 @@ loadvm_handle_recv_bitmap(char *s) "%s"
|
||||||
loadvm_postcopy_handle_advise(void) ""
|
loadvm_postcopy_handle_advise(void) ""
|
||||||
loadvm_postcopy_handle_listen(const char *str) "%s"
|
loadvm_postcopy_handle_listen(const char *str) "%s"
|
||||||
loadvm_postcopy_handle_run(void) ""
|
loadvm_postcopy_handle_run(void) ""
|
||||||
loadvm_postcopy_handle_run_bh(const char *str) "%s"
|
|
||||||
loadvm_postcopy_handle_resume(void) ""
|
loadvm_postcopy_handle_resume(void) ""
|
||||||
loadvm_postcopy_ram_handle_discard(void) ""
|
loadvm_postcopy_ram_handle_discard(void) ""
|
||||||
loadvm_postcopy_ram_handle_discard_end(void) ""
|
loadvm_postcopy_ram_handle_discard_end(void) ""
|
||||||
|
@ -48,6 +47,9 @@ savevm_state_cleanup(void) ""
|
||||||
savevm_state_complete_precopy(void) ""
|
savevm_state_complete_precopy(void) ""
|
||||||
vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s"
|
vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s"
|
||||||
vmstate_load(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(void) ""
|
||||||
postcopy_pause_incoming_continued(void) ""
|
postcopy_pause_incoming_continued(void) ""
|
||||||
postcopy_page_req_sync(void *host_addr) "sync page req %p"
|
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"
|
multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s"
|
||||||
|
|
||||||
# migration.c
|
# 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_set_state(const char *new_state) "new state %s"
|
||||||
migrate_fd_cleanup(void) ""
|
migrate_fd_cleanup(void) ""
|
||||||
migrate_fd_error(const char *error_desc) "error=%s"
|
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_pre(int ms) "%d ms"
|
||||||
migration_rate_limit_post(int urgent) "urgent: %d"
|
migration_rate_limit_post(int urgent) "urgent: %d"
|
||||||
migration_return_path_end_before(void) ""
|
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_after_loop(void) ""
|
||||||
migration_thread_file_err(void) ""
|
migration_thread_file_err(void) ""
|
||||||
migration_thread_setup_complete(void) ""
|
migration_thread_setup_complete(void) ""
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "migration/register.h"
|
#include "migration/register.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
#include "migration/qemu-file-types.h"
|
#include "migration/qemu-file-types.h"
|
||||||
|
|
||||||
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
|
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?
|
* specific version?
|
||||||
*/
|
*/
|
||||||
g_assert(slirp_state_version() == 4);
|
g_assert(slirp_state_version() == 4);
|
||||||
register_savevm_live("slirp", 0, slirp_state_version(),
|
register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY,
|
||||||
&savevm_slirp_state, s->slirp);
|
slirp_state_version(), &savevm_slirp_state, s->slirp);
|
||||||
|
|
||||||
s->poll_notifier.notify = net_slirp_poll_notify;
|
s->poll_notifier.notify = net_slirp_poll_notify;
|
||||||
main_loop_poll_add_notifier(&s->poll_notifier);
|
main_loop_poll_add_notifier(&s->poll_notifier);
|
||||||
|
|
|
@ -631,6 +631,28 @@
|
||||||
'data': [ 'none', 'zlib',
|
'data': [ 'none', 'zlib',
|
||||||
{ 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] }
|
{ '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:
|
# @BitmapMigrationBitmapAliasTransform:
|
||||||
#
|
#
|
||||||
|
@ -849,6 +871,9 @@
|
||||||
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
|
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
|
||||||
# Defaults to 1. (Since 8.1)
|
# Defaults to 1. (Since 8.1)
|
||||||
#
|
#
|
||||||
|
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
|
||||||
|
# (Since 8.2)
|
||||||
|
#
|
||||||
# Features:
|
# Features:
|
||||||
#
|
#
|
||||||
# @deprecated: Member @block-incremental is deprecated. Use
|
# @deprecated: Member @block-incremental is deprecated. Use
|
||||||
|
@ -881,7 +906,8 @@
|
||||||
'multifd-zlib-level', 'multifd-zstd-level',
|
'multifd-zlib-level', 'multifd-zstd-level',
|
||||||
'block-bitmap-mapping',
|
'block-bitmap-mapping',
|
||||||
{ 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
|
{ 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
|
||||||
'vcpu-dirty-limit'] }
|
'vcpu-dirty-limit',
|
||||||
|
'mode'] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @MigrateSetParameters:
|
# @MigrateSetParameters:
|
||||||
|
@ -1033,6 +1059,9 @@
|
||||||
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
|
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
|
||||||
# Defaults to 1. (Since 8.1)
|
# Defaults to 1. (Since 8.1)
|
||||||
#
|
#
|
||||||
|
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
|
||||||
|
# (Since 8.2)
|
||||||
|
#
|
||||||
# Features:
|
# Features:
|
||||||
#
|
#
|
||||||
# @deprecated: Member @block-incremental is deprecated. Use
|
# @deprecated: Member @block-incremental is deprecated. Use
|
||||||
|
@ -1085,7 +1114,8 @@
|
||||||
'*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
|
'*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
|
||||||
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
|
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
|
||||||
'features': [ 'unstable' ] },
|
'features': [ 'unstable' ] },
|
||||||
'*vcpu-dirty-limit': 'uint64'} }
|
'*vcpu-dirty-limit': 'uint64',
|
||||||
|
'*mode': 'MigMode'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @migrate-set-parameters:
|
# @migrate-set-parameters:
|
||||||
|
@ -1257,6 +1287,9 @@
|
||||||
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
|
# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration.
|
||||||
# Defaults to 1. (Since 8.1)
|
# Defaults to 1. (Since 8.1)
|
||||||
#
|
#
|
||||||
|
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
|
||||||
|
# (Since 8.2)
|
||||||
|
#
|
||||||
# Features:
|
# Features:
|
||||||
#
|
#
|
||||||
# @deprecated: Member @block-incremental is deprecated. Use
|
# @deprecated: Member @block-incremental is deprecated. Use
|
||||||
|
@ -1306,7 +1339,8 @@
|
||||||
'*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
|
'*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ],
|
||||||
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
|
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
|
||||||
'features': [ 'unstable' ] },
|
'features': [ 'unstable' ] },
|
||||||
'*vcpu-dirty-limit': 'uint64'} }
|
'*vcpu-dirty-limit': 'uint64',
|
||||||
|
'*mode': 'MigMode'} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-migrate-parameters:
|
# @query-migrate-parameters:
|
||||||
|
@ -1550,6 +1584,91 @@
|
||||||
##
|
##
|
||||||
{ 'command': 'migrate-continue', 'data': {'state': 'MigrationStatus'} }
|
{ '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:
|
# @migrate:
|
||||||
#
|
#
|
||||||
|
@ -1557,6 +1676,9 @@
|
||||||
#
|
#
|
||||||
# @uri: the Uniform Resource Identifier of the destination VM
|
# @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)
|
# @blk: do block migration (full disk copy)
|
||||||
#
|
#
|
||||||
# @inc: incremental disk copy migration
|
# @inc: incremental disk copy migration
|
||||||
|
@ -1586,13 +1708,57 @@
|
||||||
# 3. The user Monitor's "detach" argument is invalid in QMP and should
|
# 3. The user Monitor's "detach" argument is invalid in QMP and should
|
||||||
# not be used
|
# 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:
|
# Example:
|
||||||
#
|
#
|
||||||
# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
|
# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
|
||||||
# <- { "return": {} }
|
# <- { "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',
|
{ 'command': 'migrate',
|
||||||
'data': {'uri': 'str',
|
'data': {'uri': 'str',
|
||||||
|
'*channels': [ 'MigrationChannel' ],
|
||||||
'*blk': { 'type': 'bool', 'features': [ 'deprecated' ] },
|
'*blk': { 'type': 'bool', 'features': [ 'deprecated' ] },
|
||||||
'*inc': { 'type': 'bool', 'features': [ 'deprecated' ] },
|
'*inc': { 'type': 'bool', 'features': [ 'deprecated' ] },
|
||||||
'*detach': 'bool', '*resume': 'bool' } }
|
'*detach': 'bool', '*resume': 'bool' } }
|
||||||
|
@ -1606,6 +1772,9 @@
|
||||||
# @uri: The Uniform Resource Identifier identifying the source or
|
# @uri: The Uniform Resource Identifier identifying the source or
|
||||||
# address to listen on
|
# 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
|
# Returns: nothing on success
|
||||||
#
|
#
|
||||||
# Since: 2.3
|
# Since: 2.3
|
||||||
|
@ -1621,13 +1790,46 @@
|
||||||
#
|
#
|
||||||
# 3. The uri format is the same as for -incoming
|
# 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:
|
# Example:
|
||||||
#
|
#
|
||||||
# -> { "execute": "migrate-incoming",
|
# -> { "execute": "migrate-incoming",
|
||||||
# "arguments": { "uri": "tcp::4446" } }
|
# "arguments": { "uri": "tcp::4446" } }
|
||||||
# <- { "return": {} }
|
# <- { "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:
|
# @xen-save-devices-state:
|
||||||
|
|
|
@ -6,6 +6,16 @@ int migrate_add_blocker(Error **reasonp, Error **errp)
|
||||||
return 0;
|
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)
|
void migrate_del_blocker(Error **reasonp)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -2702,7 +2702,7 @@ void qmp_x_exit_preconfig(Error **errp)
|
||||||
if (incoming) {
|
if (incoming) {
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
if (strcmp(incoming, "defer") != 0) {
|
if (strcmp(incoming, "defer") != 0) {
|
||||||
qmp_migrate_incoming(incoming, &local_err);
|
qmp_migrate_incoming(incoming, false, NULL, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_reportf_err(local_err, "-incoming %s: ", incoming);
|
error_reportf_err(local_err, "-incoming %s: ", incoming);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -728,6 +728,7 @@ typedef struct {
|
||||||
/* Postcopy specific fields */
|
/* Postcopy specific fields */
|
||||||
void *postcopy_data;
|
void *postcopy_data;
|
||||||
bool postcopy_preempt;
|
bool postcopy_preempt;
|
||||||
|
bool postcopy_recovery_test_fail;
|
||||||
} MigrateCommon;
|
} MigrateCommon;
|
||||||
|
|
||||||
static int test_migrate_start(QTestState **from, QTestState **to,
|
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);
|
migrate_prepare_for_dirty_mem(from);
|
||||||
qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming',"
|
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 the first serial output from the source */
|
||||||
wait_for_serial("src_serial");
|
wait_for_serial("src_serial");
|
||||||
|
@ -1404,6 +1410,80 @@ static void test_postcopy_preempt_tls_psk(void)
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
static void test_postcopy_recovery_common(MigrateCommon *args)
|
||||||
{
|
{
|
||||||
QTestState *from, *to;
|
QTestState *from, *to;
|
||||||
|
@ -1439,9 +1519,19 @@ static void test_postcopy_recovery_common(MigrateCommon *args)
|
||||||
* migrate-recover command can only succeed if destination machine
|
* migrate-recover command can only succeed if destination machine
|
||||||
* is in the paused state
|
* is in the paused state
|
||||||
*/
|
*/
|
||||||
wait_for_migration_status(to, "postcopy-paused",
|
wait_for_postcopy_status(to, "postcopy-paused");
|
||||||
(const char * []) { "failed", "active",
|
wait_for_postcopy_status(from, "postcopy-paused");
|
||||||
"completed", NULL });
|
|
||||||
|
#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
|
* 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
|
* Try to rebuild the migration channel using the resume flag and
|
||||||
* the newly created channel
|
* the newly created channel
|
||||||
*/
|
*/
|
||||||
wait_for_migration_status(from, "postcopy-paused",
|
|
||||||
(const char * []) { "failed", "active",
|
|
||||||
"completed", NULL });
|
|
||||||
migrate_qmp(from, uri, "{'resume': true}");
|
migrate_qmp(from, uri, "{'resume': true}");
|
||||||
|
|
||||||
/* Restore the postcopy bandwidth to unlimited */
|
/* Restore the postcopy bandwidth to unlimited */
|
||||||
|
@ -1482,6 +1569,17 @@ static void test_postcopy_recovery_compress(void)
|
||||||
test_postcopy_recovery_common(&args);
|
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
|
#ifdef CONFIG_GNUTLS
|
||||||
static void test_postcopy_recovery_tls_psk(void)
|
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);
|
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)
|
static void test_precopy_tcp_plain(void)
|
||||||
{
|
{
|
||||||
MigrateCommon args = {
|
MigrateCommon args = {
|
||||||
|
@ -3068,6 +3191,11 @@ int main(int argc, char **argv)
|
||||||
qtest_add_func("/migration/postcopy/recovery/compress/plain",
|
qtest_add_func("/migration/postcopy/recovery/compress/plain",
|
||||||
test_postcopy_recovery_compress);
|
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);
|
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",
|
qtest_add_func("/migration/precopy/file/offset/bad",
|
||||||
test_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
|
#ifdef CONFIG_GNUTLS
|
||||||
qtest_add_func("/migration/precopy/unix/tls/psk",
|
qtest_add_func("/migration/precopy/unix/tls/psk",
|
||||||
test_precopy_unix_tls_psk);
|
test_precopy_unix_tls_psk);
|
||||||
|
|
Loading…
Reference in New Issue