mirror of https://github.com/proxmox/mirror_qemu
Migration Pull request
In this try - rebase to latest upstream - same than previous patch - fix compilation on non linux (userfaultfd.h) (me) - query-migrationthreads (jiang) - fix race on reading MultiFDPages_t.block (zhenzhong) - fix flush of zero copy page send reuest (zhenzhong) Please apply. Previous try: It includes: - David Hildenbrand fixes for virtio-men - David Gilbert canary to detect problems - Fix for rdma return values (Fiona) - Peter Xu uffd_open fixes - Peter Xu show right downtime for postcopy - manish.mishra msg fix fixes - my vfio changes. Please apply. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEGJn/jt6/WMzuA0uC9IfvGFhy1yMFAmPhobYACgkQ9IfvGFhy 1yMNaA/9EHDPqrI1HL/VkJG4nNOOsQR7RbburXEberZOzvLjnqpjUD3Ls9qV6rx+ ieHa5T4imYJFk72Wa5vx4r1/dCjtJD2W6jg5+/0nTvYAHrs1U1VRqpuTr0HiXdbJ ZLLCnW5eDyO3eMaOX0MUkgHgL0FNkc/Lq5ViCTFsMu9O9xMuDLLdAC3cdvslKuOu X1gKByr9jT817Y9e36amYmRaJKC6Cr/PIekNVFu12HBW79pPusLX8KWEf4RBw4HR sPwTvMCR/BwZ0+2Lppan60G5rt/ZxDu40oU7y+RHlfWqevl4hDM84/nhjMvEgzc5 a4Ahe2ERGLwwnC8z3l7v9+pEzSGzDoPcnRGvZcpUpk68wTDtxd5Bdq8CwmNUfL07 VzWcYpH0yvmwjBba9jfn9fAVgnG5rVp558XcYLIII3wEToty3UDtm43wSdj2CGr6 cu+IPAp+n/I5G9SRYBTU9ozJz45ttnEe0hxUtZ4I3MuhzHi1VEDAqTWM/X0LyS41 TB3Y5B2KKpJYbPyZEH4nyTeetR2k7alTFzahCgKqVfOgL0nJx54petjS1K+B1P72 g6lhP9WnQ33W+M8S7J/aGEaDJd1lFyFB2Rdjn2ZZnASH/fR9j0mFmXWvulXtjFNp Sfim3887+Iv4Uzw4VWEe3mM5Ypi/Ba2CmuTjy/pM08Ey8X1Qs5o= =ZQbR -----END PGP SIGNATURE----- Merge tag 'migration-20230206-pull-request' of https://gitlab.com/juan.quintela/qemu into staging Migration Pull request In this try - rebase to latest upstream - same than previous patch - fix compilation on non linux (userfaultfd.h) (me) - query-migrationthreads (jiang) - fix race on reading MultiFDPages_t.block (zhenzhong) - fix flush of zero copy page send reuest (zhenzhong) Please apply. Previous try: It includes: - David Hildenbrand fixes for virtio-men - David Gilbert canary to detect problems - Fix for rdma return values (Fiona) - Peter Xu uffd_open fixes - Peter Xu show right downtime for postcopy - manish.mishra msg fix fixes - my vfio changes. Please apply. # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEGJn/jt6/WMzuA0uC9IfvGFhy1yMFAmPhobYACgkQ9IfvGFhy # 1yMNaA/9EHDPqrI1HL/VkJG4nNOOsQR7RbburXEberZOzvLjnqpjUD3Ls9qV6rx+ # ieHa5T4imYJFk72Wa5vx4r1/dCjtJD2W6jg5+/0nTvYAHrs1U1VRqpuTr0HiXdbJ # ZLLCnW5eDyO3eMaOX0MUkgHgL0FNkc/Lq5ViCTFsMu9O9xMuDLLdAC3cdvslKuOu # X1gKByr9jT817Y9e36amYmRaJKC6Cr/PIekNVFu12HBW79pPusLX8KWEf4RBw4HR # sPwTvMCR/BwZ0+2Lppan60G5rt/ZxDu40oU7y+RHlfWqevl4hDM84/nhjMvEgzc5 # a4Ahe2ERGLwwnC8z3l7v9+pEzSGzDoPcnRGvZcpUpk68wTDtxd5Bdq8CwmNUfL07 # VzWcYpH0yvmwjBba9jfn9fAVgnG5rVp558XcYLIII3wEToty3UDtm43wSdj2CGr6 # cu+IPAp+n/I5G9SRYBTU9ozJz45ttnEe0hxUtZ4I3MuhzHi1VEDAqTWM/X0LyS41 # TB3Y5B2KKpJYbPyZEH4nyTeetR2k7alTFzahCgKqVfOgL0nJx54petjS1K+B1P72 # g6lhP9WnQ33W+M8S7J/aGEaDJd1lFyFB2Rdjn2ZZnASH/fR9j0mFmXWvulXtjFNp # Sfim3887+Iv4Uzw4VWEe3mM5Ypi/Ba2CmuTjy/pM08Ey8X1Qs5o= # =ZQbR # -----END PGP SIGNATURE----- # gpg: Signature made Tue 07 Feb 2023 00:56:22 GMT # 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-20230206-pull-request' of https://gitlab.com/juan.quintela/qemu: (30 commits) migration: save/delete migration thread info migration: Introduce interface query-migrationthreads multifd: Fix flush of zero copy page send request multifd: Fix a race on reading MultiFDPages_t.block migration: check magic value for deciding the mapping of channels io: Add support for MSG_PEEK for socket channel migration/dirtyrate: Show sample pages only in page-sampling mode migration: Perform vmsd structure check during tests migration: Add canary to VMSTATE_END_OF_LIST migration/rdma: fix return value for qio_channel_rdma_{readv,writev} migration: Show downtime during postcopy phase virtio-mem: Proper support for preallocation with migration virtio-mem: Migrate immutable properties early virtio-mem: Fail if a memory backend with "prealloc=on" is specified migration/ram: Factor out check for advised postcopy migration/vmstate: Introduce VMSTATE_WITH_TMP_TEST() and VMSTATE_BITMAP_TEST() migration/savevm: Allow immutable device state to be migrated early (i.e., before RAM) migration/savevm: Prepare vmdesc json writer in qemu_savevm_state_setup() migration/savevm: Move more savevm handling into vmstate_save() migration/ram: Optimize ram_write_tracking_start() for RamDiscardManager ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>master
commit
b86307ecef
|
@ -283,11 +283,11 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
|
||||||
if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
|
if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
|
||||||
ret = qio_channel_readv_full(s->ioc, &iov, 1,
|
ret = qio_channel_readv_full(s->ioc, &iov, 1,
|
||||||
&msgfds, &msgfds_num,
|
&msgfds, &msgfds_num,
|
||||||
NULL);
|
0, NULL);
|
||||||
} else {
|
} else {
|
||||||
ret = qio_channel_readv_full(s->ioc, &iov, 1,
|
ret = qio_channel_readv_full(s->ioc, &iov, 1,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL);
|
0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msgfds_num) {
|
if (msgfds_num) {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Boards:
|
||||||
|
#
|
||||||
|
CONFIG_ISAPC=n
|
||||||
|
CONFIG_I440FX=n
|
||||||
|
CONFIG_Q35=n
|
||||||
|
CONFIG_MICROVM=y
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Boards:
|
||||||
|
#
|
||||||
|
CONFIG_ISAPC=y
|
||||||
|
CONFIG_I440FX=y
|
||||||
|
CONFIG_Q35=y
|
||||||
|
CONFIG_MICROVM=y
|
|
@ -482,15 +482,17 @@ An iterative device must provide:
|
||||||
- A ``load_setup`` function that initialises the data structures on the
|
- A ``load_setup`` function that initialises the data structures on the
|
||||||
destination.
|
destination.
|
||||||
|
|
||||||
- A ``save_live_pending`` function that is called repeatedly and must
|
- A ``state_pending_exact`` function that indicates how much more
|
||||||
indicate how much more data the iterative data must save. The core
|
data we must save. The core migration code will use this to
|
||||||
migration code will use this to determine when to pause the CPUs
|
determine when to pause the CPUs and complete the migration.
|
||||||
and complete the migration.
|
|
||||||
|
|
||||||
- A ``save_live_iterate`` function (called after ``save_live_pending``
|
- A ``state_pending_estimate`` function that indicates how much more
|
||||||
when there is significant data still to be sent). It should send
|
data we must save. When the estimated amount is smaller than the
|
||||||
a chunk of data until the point that stream bandwidth limits tell it
|
threshold, we call ``state_pending_exact``.
|
||||||
to stop. Each call generates one section.
|
|
||||||
|
- A ``save_live_iterate`` function should send a chunk of data until
|
||||||
|
the point that stream bandwidth limits tell it to stop. Each call
|
||||||
|
generates one section.
|
||||||
|
|
||||||
- A ``save_live_complete_precopy`` function that must transmit the
|
- A ``save_live_complete_precopy`` function that must transmit the
|
||||||
last section for the device containing any remaining data.
|
last section for the device containing any remaining data.
|
||||||
|
|
|
@ -28,7 +28,7 @@ VFIO implements the device hooks for the iterative approach as follows:
|
||||||
* A ``load_setup`` function that sets up the migration region on the
|
* A ``load_setup`` function that sets up the migration region on the
|
||||||
destination and sets _RESUMING flag in the VFIO device state.
|
destination and sets _RESUMING flag in the VFIO device state.
|
||||||
|
|
||||||
* A ``save_live_pending`` function that reads pending_bytes from the vendor
|
* A ``state_pending_exact`` function that reads pending_bytes from the vendor
|
||||||
driver, which indicates the amount of data that the vendor driver has yet to
|
driver, which indicates the amount of data that the vendor driver has yet to
|
||||||
save for the VFIO device.
|
save for the VFIO device.
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ Live migration save path
|
||||||
(RUNNING, _SETUP, _RUNNING|_SAVING)
|
(RUNNING, _SETUP, _RUNNING|_SAVING)
|
||||||
|
|
|
|
||||||
(RUNNING, _ACTIVE, _RUNNING|_SAVING)
|
(RUNNING, _ACTIVE, _RUNNING|_SAVING)
|
||||||
If device is active, get pending_bytes by .save_live_pending()
|
If device is active, get pending_bytes by .state_pending_exact()
|
||||||
If total pending_bytes >= threshold_size, call .save_live_iterate()
|
If total pending_bytes >= threshold_size, call .save_live_iterate()
|
||||||
Data of VFIO device for pre-copy phase is copied
|
Data of VFIO device for pre-copy phase is copied
|
||||||
Iterate till total pending bytes converge and are less than threshold
|
Iterate till total pending bytes converge and are less than threshold
|
||||||
|
|
|
@ -41,7 +41,9 @@
|
||||||
#include "hw/virtio/virtio-pci.h"
|
#include "hw/virtio/virtio-pci.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
|
|
||||||
GlobalProperty hw_compat_7_2[] = {};
|
GlobalProperty hw_compat_7_2[] = {
|
||||||
|
{ "virtio-mem", "x-early-migration", "false" },
|
||||||
|
};
|
||||||
const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2);
|
const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2);
|
||||||
|
|
||||||
GlobalProperty hw_compat_7_1[] = {
|
GlobalProperty hw_compat_7_1[] = {
|
||||||
|
|
|
@ -182,10 +182,10 @@ static int cmma_save_setup(QEMUFile *f, void *opaque)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmma_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
|
static void cmma_state_pending(void *opaque,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_precopy_only,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_postcopy_only)
|
uint64_t *res_postcopy_only)
|
||||||
{
|
{
|
||||||
S390StAttribState *sas = S390_STATTRIB(opaque);
|
S390StAttribState *sas = S390_STATTRIB(opaque);
|
||||||
S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas);
|
S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas);
|
||||||
|
@ -371,7 +371,8 @@ 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,
|
||||||
.save_live_complete_precopy = cmma_save_complete,
|
.save_live_complete_precopy = cmma_save_complete,
|
||||||
.save_live_pending = cmma_save_pending,
|
.state_pending_exact = cmma_state_pending,
|
||||||
|
.state_pending_estimate = cmma_state_pending,
|
||||||
.save_cleanup = cmma_save_cleanup,
|
.save_cleanup = cmma_save_cleanup,
|
||||||
.load_state = cmma_load,
|
.load_state = cmma_load,
|
||||||
.is_active = cmma_active,
|
.is_active = cmma_active,
|
||||||
|
|
|
@ -456,11 +456,10 @@ static void vfio_save_cleanup(void *opaque)
|
||||||
trace_vfio_save_cleanup(vbasedev->name);
|
trace_vfio_save_cleanup(vbasedev->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_save_pending(QEMUFile *f, void *opaque,
|
static void vfio_state_pending(void *opaque,
|
||||||
uint64_t threshold_size,
|
uint64_t *res_precopy_only,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_postcopy_only)
|
||||||
uint64_t *res_postcopy_only)
|
|
||||||
{
|
{
|
||||||
VFIODevice *vbasedev = opaque;
|
VFIODevice *vbasedev = opaque;
|
||||||
VFIOMigration *migration = vbasedev->migration;
|
VFIOMigration *migration = vbasedev->migration;
|
||||||
|
@ -473,7 +472,7 @@ static void vfio_save_pending(QEMUFile *f, void *opaque,
|
||||||
|
|
||||||
*res_precopy_only += migration->pending_bytes;
|
*res_precopy_only += migration->pending_bytes;
|
||||||
|
|
||||||
trace_vfio_save_pending(vbasedev->name, *res_precopy_only,
|
trace_vfio_state_pending(vbasedev->name, *res_precopy_only,
|
||||||
*res_postcopy_only, *res_compatible);
|
*res_postcopy_only, *res_compatible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,9 +514,9 @@ static int vfio_save_iterate(QEMUFile *f, void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset pending_bytes as .save_live_pending is not called during savevm or
|
* Reset pending_bytes as state_pending* are not called during
|
||||||
* snapshot case, in such case vfio_update_pending() at the start of this
|
* savevm or snapshot case, in such case vfio_update_pending() at
|
||||||
* function updates pending_bytes.
|
* the start of this function updates pending_bytes.
|
||||||
*/
|
*/
|
||||||
migration->pending_bytes = 0;
|
migration->pending_bytes = 0;
|
||||||
trace_vfio_save_iterate(vbasedev->name, data_size);
|
trace_vfio_save_iterate(vbasedev->name, data_size);
|
||||||
|
@ -685,7 +684,8 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id)
|
||||||
static SaveVMHandlers savevm_vfio_handlers = {
|
static SaveVMHandlers savevm_vfio_handlers = {
|
||||||
.save_setup = vfio_save_setup,
|
.save_setup = vfio_save_setup,
|
||||||
.save_cleanup = vfio_save_cleanup,
|
.save_cleanup = vfio_save_cleanup,
|
||||||
.save_live_pending = vfio_save_pending,
|
.state_pending_exact = vfio_state_pending,
|
||||||
|
.state_pending_estimate = vfio_state_pending,
|
||||||
.save_live_iterate = vfio_save_iterate,
|
.save_live_iterate = vfio_save_iterate,
|
||||||
.save_live_complete_precopy = vfio_save_complete_precopy,
|
.save_live_complete_precopy = vfio_save_complete_precopy,
|
||||||
.save_state = vfio_save_state,
|
.save_state = vfio_save_state,
|
||||||
|
|
|
@ -157,7 +157,7 @@ vfio_save_cleanup(const char *name) " (%s)"
|
||||||
vfio_save_buffer(const char *name, uint64_t data_offset, uint64_t data_size, uint64_t pending) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64" pending 0x%"PRIx64
|
vfio_save_buffer(const char *name, uint64_t data_offset, uint64_t data_size, uint64_t pending) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64" pending 0x%"PRIx64
|
||||||
vfio_update_pending(const char *name, uint64_t pending) " (%s) pending 0x%"PRIx64
|
vfio_update_pending(const char *name, uint64_t pending) " (%s) pending 0x%"PRIx64
|
||||||
vfio_save_device_config_state(const char *name) " (%s)"
|
vfio_save_device_config_state(const char *name) " (%s)"
|
||||||
vfio_save_pending(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t compatible) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" compatible 0x%"PRIx64
|
vfio_state_pending(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t compatible) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" compatible 0x%"PRIx64
|
||||||
vfio_save_iterate(const char *name, int data_size) " (%s) data_size %d"
|
vfio_save_iterate(const char *name, int data_size) " (%s) data_size %d"
|
||||||
vfio_save_complete_precopy(const char *name) " (%s)"
|
vfio_save_complete_precopy(const char *name) " (%s)"
|
||||||
vfio_load_device_config_state(const char *name) " (%s)"
|
vfio_load_device_config_state(const char *name) " (%s)"
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include CONFIG_DEVICES
|
#include CONFIG_DEVICES
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_mem_device_early;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only had legacy x86 guests that did not support
|
* We only had legacy x86 guests that did not support
|
||||||
* VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE. Other targets don't have legacy guests.
|
* VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE. Other targets don't have legacy guests.
|
||||||
|
@ -202,6 +204,30 @@ static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int virtio_mem_for_each_plugged_range(const VirtIOMEM *vmem, void *arg,
|
||||||
|
virtio_mem_range_cb cb)
|
||||||
|
{
|
||||||
|
unsigned long first_bit, last_bit;
|
||||||
|
uint64_t offset, size;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
first_bit = find_first_bit(vmem->bitmap, vmem->bitmap_size);
|
||||||
|
while (first_bit < vmem->bitmap_size) {
|
||||||
|
offset = first_bit * vmem->block_size;
|
||||||
|
last_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size,
|
||||||
|
first_bit + 1) - 1;
|
||||||
|
size = (last_bit - first_bit + 1) * vmem->block_size;
|
||||||
|
|
||||||
|
ret = cb(vmem, arg, offset, size);
|
||||||
|
if (ret) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
first_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size,
|
||||||
|
last_bit + 2);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust the memory section to cover the intersection with the given range.
|
* Adjust the memory section to cover the intersection with the given range.
|
||||||
*
|
*
|
||||||
|
@ -772,6 +798,12 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
|
||||||
error_setg(errp, "'%s' property specifies an unsupported memdev",
|
error_setg(errp, "'%s' property specifies an unsupported memdev",
|
||||||
VIRTIO_MEM_MEMDEV_PROP);
|
VIRTIO_MEM_MEMDEV_PROP);
|
||||||
return;
|
return;
|
||||||
|
} else if (vmem->memdev->prealloc) {
|
||||||
|
error_setg(errp, "'%s' property specifies a memdev with preallocation"
|
||||||
|
" enabled: %s. Instead, specify 'prealloc=on' for the"
|
||||||
|
" virtio-mem device. ", VIRTIO_MEM_MEMDEV_PROP,
|
||||||
|
object_get_canonical_path_component(OBJECT(vmem->memdev)));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((nb_numa_nodes && vmem->node >= nb_numa_nodes) ||
|
if ((nb_numa_nodes && vmem->node >= nb_numa_nodes) ||
|
||||||
|
@ -872,6 +904,10 @@ 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) {
|
||||||
|
vmstate_register(VMSTATE_IF(vmem), VMSTATE_INSTANCE_ID_ANY,
|
||||||
|
&vmstate_virtio_mem_device_early, vmem);
|
||||||
|
}
|
||||||
qemu_register_reset(virtio_mem_system_reset, vmem);
|
qemu_register_reset(virtio_mem_system_reset, vmem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -893,6 +929,10 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
|
||||||
*/
|
*/
|
||||||
memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
|
memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL);
|
||||||
qemu_unregister_reset(virtio_mem_system_reset, vmem);
|
qemu_unregister_reset(virtio_mem_system_reset, vmem);
|
||||||
|
if (vmem->early_migration) {
|
||||||
|
vmstate_unregister(VMSTATE_IF(vmem), &vmstate_virtio_mem_device_early,
|
||||||
|
vmem);
|
||||||
|
}
|
||||||
vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem));
|
vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem));
|
||||||
host_memory_backend_set_mapped(vmem->memdev, false);
|
host_memory_backend_set_mapped(vmem->memdev, false);
|
||||||
virtio_del_queue(vdev, 0);
|
virtio_del_queue(vdev, 0);
|
||||||
|
@ -922,6 +962,10 @@ static int virtio_mem_post_load(void *opaque, int version_id)
|
||||||
RamDiscardListener *rdl;
|
RamDiscardListener *rdl;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (vmem->prealloc && !vmem->early_migration) {
|
||||||
|
warn_report("Proper preallocation with migration requires a newer QEMU machine");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We started out with all memory discarded and our memory region is mapped
|
* We started out with all memory discarded and our memory region is mapped
|
||||||
* into an address space. Replay, now that we updated the bitmap.
|
* into an address space. Replay, now that we updated the bitmap.
|
||||||
|
@ -941,6 +985,64 @@ static int virtio_mem_post_load(void *opaque, int version_id)
|
||||||
return virtio_mem_restore_unplugged(vmem);
|
return virtio_mem_restore_unplugged(vmem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int virtio_mem_prealloc_range_cb(const VirtIOMEM *vmem, void *arg,
|
||||||
|
uint64_t offset, uint64_t size)
|
||||||
|
{
|
||||||
|
void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset;
|
||||||
|
int fd = memory_region_get_fd(&vmem->memdev->mr);
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
qemu_prealloc_mem(fd, area, size, 1, NULL, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_mem_post_load_early(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
VirtIOMEM *vmem = VIRTIO_MEM(opaque);
|
||||||
|
RAMBlock *rb = vmem->memdev->mr.ram_block;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!vmem->prealloc) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We restored the bitmap and verified that the basic properties
|
||||||
|
* match on source and destination, so we can go ahead and preallocate
|
||||||
|
* memory for all plugged memory blocks, before actual RAM migration starts
|
||||||
|
* touching this memory.
|
||||||
|
*/
|
||||||
|
ret = virtio_mem_for_each_plugged_range(vmem, NULL,
|
||||||
|
virtio_mem_prealloc_range_cb);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is tricky: postcopy wants to start with a clean slate. On
|
||||||
|
* POSTCOPY_INCOMING_ADVISE, postcopy code discards all (ordinarily
|
||||||
|
* preallocated) RAM such that postcopy will work as expected later.
|
||||||
|
*
|
||||||
|
* However, we run after POSTCOPY_INCOMING_ADVISE -- but before actual
|
||||||
|
* RAM migration. So let's discard all memory again. This looks like an
|
||||||
|
* expensive NOP, but actually serves a purpose: we made sure that we
|
||||||
|
* were able to allocate all required backend memory once. We cannot
|
||||||
|
* guarantee that the backend memory we will free will remain free
|
||||||
|
* until we need it during postcopy, but at least we can catch the
|
||||||
|
* obvious setup issues this way.
|
||||||
|
*/
|
||||||
|
if (migration_incoming_postcopy_advised()) {
|
||||||
|
if (ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb))) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct VirtIOMEMMigSanityChecks {
|
typedef struct VirtIOMEMMigSanityChecks {
|
||||||
VirtIOMEM *parent;
|
VirtIOMEM *parent;
|
||||||
uint64_t addr;
|
uint64_t addr;
|
||||||
|
@ -1009,18 +1111,54 @@ static const VMStateDescription vmstate_virtio_mem_sanity_checks = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool virtio_mem_vmstate_field_exists(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
const VirtIOMEM *vmem = VIRTIO_MEM(opaque);
|
||||||
|
|
||||||
|
/* With early migration, these fields were already migrated. */
|
||||||
|
return !vmem->early_migration;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_virtio_mem_device = {
|
static const VMStateDescription vmstate_virtio_mem_device = {
|
||||||
.name = "virtio-mem-device",
|
.name = "virtio-mem-device",
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.priority = MIG_PRI_VIRTIO_MEM,
|
.priority = MIG_PRI_VIRTIO_MEM,
|
||||||
.post_load = virtio_mem_post_load,
|
.post_load = virtio_mem_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_WITH_TMP_TEST(VirtIOMEM, virtio_mem_vmstate_field_exists,
|
||||||
|
VirtIOMEMMigSanityChecks,
|
||||||
|
vmstate_virtio_mem_sanity_checks),
|
||||||
|
VMSTATE_UINT64(usable_region_size, VirtIOMEM),
|
||||||
|
VMSTATE_UINT64_TEST(size, VirtIOMEM, virtio_mem_vmstate_field_exists),
|
||||||
|
VMSTATE_UINT64(requested_size, VirtIOMEM),
|
||||||
|
VMSTATE_BITMAP_TEST(bitmap, VirtIOMEM, virtio_mem_vmstate_field_exists,
|
||||||
|
0, bitmap_size),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transfer properties that are immutable while migration is active early,
|
||||||
|
* such that we have have this information around before migrating any RAM
|
||||||
|
* content.
|
||||||
|
*
|
||||||
|
* Note that virtio_mem_is_busy() makes sure these properties can no longer
|
||||||
|
* change on the migration source until migration completed.
|
||||||
|
*
|
||||||
|
* With QEMU compat machines, we transmit these properties later, via
|
||||||
|
* vmstate_virtio_mem_device instead -- see virtio_mem_vmstate_field_exists().
|
||||||
|
*/
|
||||||
|
static const VMStateDescription vmstate_virtio_mem_device_early = {
|
||||||
|
.name = "virtio-mem-device-early",
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.version_id = 1,
|
||||||
|
.early_setup = true,
|
||||||
|
.post_load = virtio_mem_post_load_early,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks,
|
VMSTATE_WITH_TMP(VirtIOMEM, VirtIOMEMMigSanityChecks,
|
||||||
vmstate_virtio_mem_sanity_checks),
|
vmstate_virtio_mem_sanity_checks),
|
||||||
VMSTATE_UINT64(usable_region_size, VirtIOMEM),
|
|
||||||
VMSTATE_UINT64(size, VirtIOMEM),
|
VMSTATE_UINT64(size, VirtIOMEM),
|
||||||
VMSTATE_UINT64(requested_size, VirtIOMEM),
|
|
||||||
VMSTATE_BITMAP(bitmap, VirtIOMEM, 0, bitmap_size),
|
VMSTATE_BITMAP(bitmap, VirtIOMEM, 0, bitmap_size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
},
|
},
|
||||||
|
@ -1205,6 +1343,8 @@ static Property virtio_mem_properties[] = {
|
||||||
DEFINE_PROP_ON_OFF_AUTO(VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP, VirtIOMEM,
|
DEFINE_PROP_ON_OFF_AUTO(VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP, VirtIOMEM,
|
||||||
unplugged_inaccessible, ON_OFF_AUTO_AUTO),
|
unplugged_inaccessible, ON_OFF_AUTO_AUTO),
|
||||||
#endif
|
#endif
|
||||||
|
DEFINE_PROP_BOOL(VIRTIO_MEM_EARLY_MIGRATION_PROP, VirtIOMEM,
|
||||||
|
early_migration, true),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass,
|
||||||
#define VIRTIO_MEM_BLOCK_SIZE_PROP "block-size"
|
#define VIRTIO_MEM_BLOCK_SIZE_PROP "block-size"
|
||||||
#define VIRTIO_MEM_ADDR_PROP "memaddr"
|
#define VIRTIO_MEM_ADDR_PROP "memaddr"
|
||||||
#define VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP "unplugged-inaccessible"
|
#define VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP "unplugged-inaccessible"
|
||||||
|
#define VIRTIO_MEM_EARLY_MIGRATION_PROP "x-early-migration"
|
||||||
#define VIRTIO_MEM_PREALLOC_PROP "prealloc"
|
#define VIRTIO_MEM_PREALLOC_PROP "prealloc"
|
||||||
|
|
||||||
struct VirtIOMEM {
|
struct VirtIOMEM {
|
||||||
|
@ -74,6 +75,13 @@ struct VirtIOMEM {
|
||||||
/* whether to prealloc memory when plugging new blocks */
|
/* whether to prealloc memory when plugging new blocks */
|
||||||
bool prealloc;
|
bool prealloc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whether we migrate properties that are immutable while migration is
|
||||||
|
* active early, before state of other devices and especially, before
|
||||||
|
* migrating any RAM content.
|
||||||
|
*/
|
||||||
|
bool early_migration;
|
||||||
|
|
||||||
/* notifiers to notify when "size" changes */
|
/* notifiers to notify when "size" changes */
|
||||||
NotifierList size_change_notifiers;
|
NotifierList size_change_notifiers;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass,
|
||||||
|
|
||||||
#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1
|
#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1
|
||||||
|
|
||||||
|
#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1
|
||||||
|
|
||||||
typedef enum QIOChannelFeature QIOChannelFeature;
|
typedef enum QIOChannelFeature QIOChannelFeature;
|
||||||
|
|
||||||
enum QIOChannelFeature {
|
enum QIOChannelFeature {
|
||||||
|
@ -41,6 +43,7 @@ enum QIOChannelFeature {
|
||||||
QIO_CHANNEL_FEATURE_SHUTDOWN,
|
QIO_CHANNEL_FEATURE_SHUTDOWN,
|
||||||
QIO_CHANNEL_FEATURE_LISTEN,
|
QIO_CHANNEL_FEATURE_LISTEN,
|
||||||
QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY,
|
QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY,
|
||||||
|
QIO_CHANNEL_FEATURE_READ_MSG_PEEK,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,6 +117,7 @@ struct QIOChannelClass {
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
int (*io_close)(QIOChannel *ioc,
|
int (*io_close)(QIOChannel *ioc,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
@ -188,6 +192,7 @@ void qio_channel_set_name(QIOChannel *ioc,
|
||||||
* @niov: the length of the @iov array
|
* @niov: the length of the @iov array
|
||||||
* @fds: pointer to an array that will received file handles
|
* @fds: pointer to an array that will received file handles
|
||||||
* @nfds: pointer filled with number of elements in @fds on return
|
* @nfds: pointer filled with number of elements in @fds on return
|
||||||
|
* @flags: read flags (QIO_CHANNEL_READ_FLAG_*)
|
||||||
* @errp: pointer to a NULL-initialized error object
|
* @errp: pointer to a NULL-initialized error object
|
||||||
*
|
*
|
||||||
* Read data from the IO channel, storing it in the
|
* Read data from the IO channel, storing it in the
|
||||||
|
@ -224,6 +229,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,10 @@ bool migration_has_failed(MigrationState *);
|
||||||
/* ...and after the device transmission */
|
/* ...and after the device transmission */
|
||||||
bool migration_in_postcopy_after_devices(MigrationState *);
|
bool migration_in_postcopy_after_devices(MigrationState *);
|
||||||
void migration_global_dump(Monitor *mon);
|
void migration_global_dump(Monitor *mon);
|
||||||
/* True if incomming migration entered POSTCOPY_INCOMING_DISCARD */
|
/* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */
|
||||||
bool migration_in_incoming_postcopy(void);
|
bool migration_in_incoming_postcopy(void);
|
||||||
|
/* True if incoming migration entered POSTCOPY_INCOMING_ADVISE */
|
||||||
|
bool migration_incoming_postcopy_advised(void);
|
||||||
/* True if background snapshot is active */
|
/* True if background snapshot is active */
|
||||||
bool migration_in_bg_snapshot(void);
|
bool migration_in_bg_snapshot(void);
|
||||||
|
|
||||||
|
|
|
@ -46,11 +46,6 @@ typedef struct SaveVMHandlers {
|
||||||
|
|
||||||
/* This runs outside the iothread lock! */
|
/* This runs outside the iothread lock! */
|
||||||
int (*save_setup)(QEMUFile *f, void *opaque);
|
int (*save_setup)(QEMUFile *f, void *opaque);
|
||||||
void (*save_live_pending)(QEMUFile *f, void *opaque,
|
|
||||||
uint64_t threshold_size,
|
|
||||||
uint64_t *res_precopy_only,
|
|
||||||
uint64_t *res_compatible,
|
|
||||||
uint64_t *res_postcopy_only);
|
|
||||||
/* Note for save_live_pending:
|
/* Note for save_live_pending:
|
||||||
* - res_precopy_only is for data which must be migrated in precopy phase
|
* - res_precopy_only is for data which must be migrated in precopy phase
|
||||||
* or in stopped state, in other words - before target vm start
|
* or in stopped state, in other words - before target vm start
|
||||||
|
@ -61,8 +56,16 @@ typedef struct SaveVMHandlers {
|
||||||
* Sum of res_postcopy_only, res_compatible and res_postcopy_only is the
|
* Sum of res_postcopy_only, res_compatible and res_postcopy_only is the
|
||||||
* whole amount of pending data.
|
* whole amount of pending data.
|
||||||
*/
|
*/
|
||||||
|
/* This estimates the remaining data to transfer */
|
||||||
|
void (*state_pending_estimate)(void *opaque,
|
||||||
|
uint64_t *res_precopy_only,
|
||||||
|
uint64_t *res_compatible,
|
||||||
|
uint64_t *res_postcopy_only);
|
||||||
|
/* This calculate the exact remaining data to transfer */
|
||||||
|
void (*state_pending_exact)(void *opaque,
|
||||||
|
uint64_t *res_precopy_only,
|
||||||
|
uint64_t *res_compatible,
|
||||||
|
uint64_t *res_postcopy_only);
|
||||||
LoadStateHandler *load_state;
|
LoadStateHandler *load_state;
|
||||||
int (*load_setup)(QEMUFile *f, void *opaque);
|
int (*load_setup)(QEMUFile *f, void *opaque);
|
||||||
int (*load_cleanup)(void *opaque);
|
int (*load_cleanup)(void *opaque);
|
||||||
|
|
|
@ -147,6 +147,9 @@ enum VMStateFlags {
|
||||||
* VMStateField.struct_version_id to tell which version of the
|
* VMStateField.struct_version_id to tell which version of the
|
||||||
* structure we are referencing to use. */
|
* structure we are referencing to use. */
|
||||||
VMS_VSTRUCT = 0x8000,
|
VMS_VSTRUCT = 0x8000,
|
||||||
|
|
||||||
|
/* Marker for end of list */
|
||||||
|
VMS_END = 0x10000
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -178,7 +181,21 @@ struct VMStateField {
|
||||||
|
|
||||||
struct VMStateDescription {
|
struct VMStateDescription {
|
||||||
const char *name;
|
const char *name;
|
||||||
int unmigratable;
|
bool unmigratable;
|
||||||
|
/*
|
||||||
|
* This VMSD describes something that should be sent during setup phase
|
||||||
|
* of migration. It plays similar role as save_setup() for explicitly
|
||||||
|
* registered vmstate entries, so it can be seen as a way to describe
|
||||||
|
* save_setup() in VMSD structures.
|
||||||
|
*
|
||||||
|
* Note that for now, a SaveStateEntry cannot have a VMSD and
|
||||||
|
* operations (e.g., save_setup()) set at the same time. Consequently,
|
||||||
|
* save_setup() and a VMSD with early_setup set to true are mutually
|
||||||
|
* exclusive. For this reason, also early_setup VMSDs are migrated in a
|
||||||
|
* QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in
|
||||||
|
* a QEMU_VM_SECTION_START section.
|
||||||
|
*/
|
||||||
|
bool early_setup;
|
||||||
int version_id;
|
int version_id;
|
||||||
int minimum_version_id;
|
int minimum_version_id;
|
||||||
MigrationPriority priority;
|
MigrationPriority priority;
|
||||||
|
@ -705,8 +722,9 @@ extern const VMStateInfo vmstate_info_qlist;
|
||||||
* '_state' type
|
* '_state' type
|
||||||
* That the pointer is right at the start of _tmp_type.
|
* That the pointer is right at the start of _tmp_type.
|
||||||
*/
|
*/
|
||||||
#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) { \
|
#define VMSTATE_WITH_TMP_TEST(_state, _test, _tmp_type, _vmsd) { \
|
||||||
.name = "tmp", \
|
.name = "tmp", \
|
||||||
|
.field_exists = (_test), \
|
||||||
.size = sizeof(_tmp_type) + \
|
.size = sizeof(_tmp_type) + \
|
||||||
QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \
|
QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \
|
||||||
type_check_pointer(_state, \
|
type_check_pointer(_state, \
|
||||||
|
@ -715,6 +733,9 @@ extern const VMStateInfo vmstate_info_qlist;
|
||||||
.info = &vmstate_info_tmp, \
|
.info = &vmstate_info_tmp, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) \
|
||||||
|
VMSTATE_WITH_TMP_TEST(_state, NULL, _tmp_type, _vmsd)
|
||||||
|
|
||||||
#define VMSTATE_UNUSED_BUFFER(_test, _version, _size) { \
|
#define VMSTATE_UNUSED_BUFFER(_test, _version, _size) { \
|
||||||
.name = "unused", \
|
.name = "unused", \
|
||||||
.field_exists = (_test), \
|
.field_exists = (_test), \
|
||||||
|
@ -738,8 +759,9 @@ extern const VMStateInfo vmstate_info_qlist;
|
||||||
/* _field_size should be a int32_t field in the _state struct giving the
|
/* _field_size should be a int32_t field in the _state struct giving the
|
||||||
* size of the bitmap _field in bits.
|
* size of the bitmap _field in bits.
|
||||||
*/
|
*/
|
||||||
#define VMSTATE_BITMAP(_field, _state, _version, _field_size) { \
|
#define VMSTATE_BITMAP_TEST(_field, _state, _test, _version, _field_size) { \
|
||||||
.name = (stringify(_field)), \
|
.name = (stringify(_field)), \
|
||||||
|
.field_exists = (_test), \
|
||||||
.version_id = (_version), \
|
.version_id = (_version), \
|
||||||
.size_offset = vmstate_offset_value(_state, _field_size, int32_t),\
|
.size_offset = vmstate_offset_value(_state, _field_size, int32_t),\
|
||||||
.info = &vmstate_info_bitmap, \
|
.info = &vmstate_info_bitmap, \
|
||||||
|
@ -747,6 +769,9 @@ extern const VMStateInfo vmstate_info_qlist;
|
||||||
.offset = offsetof(_state, _field), \
|
.offset = offsetof(_state, _field), \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VMSTATE_BITMAP(_field, _state, _version, _field_size) \
|
||||||
|
VMSTATE_BITMAP_TEST(_field, _state, NULL, _version, _field_size)
|
||||||
|
|
||||||
/* For migrating a QTAILQ.
|
/* For migrating a QTAILQ.
|
||||||
* Target QTAILQ needs be properly initialized.
|
* Target QTAILQ needs be properly initialized.
|
||||||
* _type: type of QTAILQ element
|
* _type: type of QTAILQ element
|
||||||
|
@ -1161,7 +1186,9 @@ extern const VMStateInfo vmstate_info_qlist;
|
||||||
VMSTATE_UNUSED_BUFFER(_test, 0, _size)
|
VMSTATE_UNUSED_BUFFER(_test, 0, _size)
|
||||||
|
|
||||||
#define VMSTATE_END_OF_LIST() \
|
#define VMSTATE_END_OF_LIST() \
|
||||||
{}
|
{ \
|
||||||
|
.flags = VMS_END, \
|
||||||
|
}
|
||||||
|
|
||||||
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
void *opaque, int version_id);
|
void *opaque, int version_id);
|
||||||
|
|
|
@ -13,10 +13,20 @@
|
||||||
#ifndef USERFAULTFD_H
|
#ifndef USERFAULTFD_H
|
||||||
#define USERFAULTFD_H
|
#define USERFAULTFD_H
|
||||||
|
|
||||||
|
#ifdef CONFIG_LINUX
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "exec/hwaddr.h"
|
#include "exec/hwaddr.h"
|
||||||
#include <linux/userfaultfd.h>
|
#include <linux/userfaultfd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* uffd_open(): Open an userfaultfd handle for current context.
|
||||||
|
*
|
||||||
|
* @flags: The flags we want to pass in when creating the handle.
|
||||||
|
*
|
||||||
|
* Returns: the uffd handle if >=0, or <0 if error happens.
|
||||||
|
*/
|
||||||
|
int uffd_open(int flags);
|
||||||
int uffd_query_features(uint64_t *features);
|
int uffd_query_features(uint64_t *features);
|
||||||
int uffd_create_fd(uint64_t features, bool non_blocking);
|
int uffd_create_fd(uint64_t features, bool non_blocking);
|
||||||
void uffd_close_fd(int uffd_fd);
|
void uffd_close_fd(int uffd_fd);
|
||||||
|
@ -32,4 +42,6 @@ int uffd_wakeup(int uffd_fd, void *addr, uint64_t length);
|
||||||
int uffd_read_events(int uffd_fd, struct uffd_msg *msgs, int count);
|
int uffd_read_events(int uffd_fd, struct uffd_msg *msgs, int count);
|
||||||
bool uffd_poll_events(int uffd_fd, int tmo);
|
bool uffd_poll_events(int uffd_fd, int tmo);
|
||||||
|
|
||||||
|
#endif /* CONFIG_LINUX */
|
||||||
|
|
||||||
#endif /* USERFAULTFD_H */
|
#endif /* USERFAULTFD_H */
|
||||||
|
|
|
@ -54,6 +54,7 @@ static ssize_t qio_channel_buffer_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
|
||||||
|
|
|
@ -203,6 +203,7 @@ static ssize_t qio_channel_command_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
|
||||||
|
|
|
@ -86,6 +86,7 @@ static ssize_t qio_channel_file_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
|
||||||
|
|
|
@ -60,6 +60,7 @@ qio_channel_null_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds G_GNUC_UNUSED,
|
int **fds G_GNUC_UNUSED,
|
||||||
size_t *nfds G_GNUC_UNUSED,
|
size_t *nfds G_GNUC_UNUSED,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc);
|
QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc);
|
||||||
|
|
|
@ -173,6 +173,9 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
qio_channel_set_feature(QIO_CHANNEL(ioc),
|
||||||
|
QIO_CHANNEL_FEATURE_READ_MSG_PEEK);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +409,9 @@ qio_channel_socket_accept(QIOChannelSocket *ioc,
|
||||||
}
|
}
|
||||||
#endif /* WIN32 */
|
#endif /* WIN32 */
|
||||||
|
|
||||||
|
qio_channel_set_feature(QIO_CHANNEL(cioc),
|
||||||
|
QIO_CHANNEL_FEATURE_READ_MSG_PEEK);
|
||||||
|
|
||||||
trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd);
|
trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd);
|
||||||
return cioc;
|
return cioc;
|
||||||
|
|
||||||
|
@ -496,6 +502,7 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
|
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
|
||||||
|
@ -517,6 +524,10 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) {
|
||||||
|
sflags |= MSG_PEEK;
|
||||||
|
}
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
ret = recvmsg(sioc->fd, &msg, sflags);
|
ret = recvmsg(sioc->fd, &msg, sflags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -624,11 +635,17 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
|
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
|
||||||
ssize_t done = 0;
|
ssize_t done = 0;
|
||||||
ssize_t i;
|
ssize_t i;
|
||||||
|
int sflags = 0;
|
||||||
|
|
||||||
|
if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) {
|
||||||
|
sflags |= MSG_PEEK;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < niov; i++) {
|
for (i = 0; i < niov; i++) {
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
@ -636,7 +653,7 @@ static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
|
||||||
ret = recv(sioc->fd,
|
ret = recv(sioc->fd,
|
||||||
iov[i].iov_base,
|
iov[i].iov_base,
|
||||||
iov[i].iov_len,
|
iov[i].iov_len,
|
||||||
0);
|
sflags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
if (done) {
|
if (done) {
|
||||||
|
|
|
@ -260,6 +260,7 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
|
||||||
|
|
|
@ -1081,6 +1081,7 @@ static ssize_t qio_channel_websock_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
|
QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
|
||||||
|
|
16
io/channel.c
16
io/channel.c
|
@ -52,6 +52,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
|
||||||
|
@ -63,7 +64,14 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return klass->io_readv(ioc, iov, niov, fds, nfds, errp);
|
if ((flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) &&
|
||||||
|
!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) {
|
||||||
|
error_setg_errno(errp, EINVAL,
|
||||||
|
"Channel does not support peek read");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return klass->io_readv(ioc, iov, niov, fds, nfds, flags, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,7 +154,7 @@ int qio_channel_readv_full_all_eof(QIOChannel *ioc,
|
||||||
while ((nlocal_iov > 0) || local_fds) {
|
while ((nlocal_iov > 0) || local_fds) {
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
|
len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds,
|
||||||
local_nfds, errp);
|
local_nfds, 0, errp);
|
||||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
if (qemu_in_coroutine()) {
|
if (qemu_in_coroutine()) {
|
||||||
qio_channel_yield(ioc, G_IO_IN);
|
qio_channel_yield(ioc, G_IO_IN);
|
||||||
|
@ -284,7 +292,7 @@ ssize_t qio_channel_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, errp);
|
return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, 0, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,7 +311,7 @@ ssize_t qio_channel_read(QIOChannel *ioc,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
struct iovec iov = { .iov_base = buf, .iov_len = buflen };
|
||||||
return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, errp);
|
return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, 0, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -762,11 +762,10 @@ static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque,
|
static void dirty_bitmap_state_pending(void *opaque,
|
||||||
uint64_t max_size,
|
uint64_t *res_precopy_only,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_postcopy_only)
|
||||||
uint64_t *res_postcopy_only)
|
|
||||||
{
|
{
|
||||||
DBMSaveState *s = &((DBMState *)opaque)->save;
|
DBMSaveState *s = &((DBMState *)opaque)->save;
|
||||||
SaveBitmapState *dbms;
|
SaveBitmapState *dbms;
|
||||||
|
@ -784,7 +783,7 @@ static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque,
|
||||||
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
|
||||||
trace_dirty_bitmap_save_pending(pending, max_size);
|
trace_dirty_bitmap_state_pending(pending);
|
||||||
|
|
||||||
*res_postcopy_only += pending;
|
*res_postcopy_only += pending;
|
||||||
}
|
}
|
||||||
|
@ -1253,7 +1252,8 @@ static SaveVMHandlers savevm_dirty_bitmap_handlers = {
|
||||||
.save_live_complete_postcopy = dirty_bitmap_save_complete,
|
.save_live_complete_postcopy = dirty_bitmap_save_complete,
|
||||||
.save_live_complete_precopy = dirty_bitmap_save_complete,
|
.save_live_complete_precopy = dirty_bitmap_save_complete,
|
||||||
.has_postcopy = dirty_bitmap_has_postcopy,
|
.has_postcopy = dirty_bitmap_has_postcopy,
|
||||||
.save_live_pending = dirty_bitmap_save_pending,
|
.state_pending_exact = dirty_bitmap_state_pending,
|
||||||
|
.state_pending_estimate = dirty_bitmap_state_pending,
|
||||||
.save_live_iterate = dirty_bitmap_save_iterate,
|
.save_live_iterate = dirty_bitmap_save_iterate,
|
||||||
.is_active_iterate = dirty_bitmap_is_active_iterate,
|
.is_active_iterate = dirty_bitmap_is_active_iterate,
|
||||||
.load_state = dirty_bitmap_load,
|
.load_state = dirty_bitmap_load,
|
||||||
|
|
|
@ -863,10 +863,10 @@ static int block_save_complete(QEMUFile *f, void *opaque)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
|
static void block_state_pending(void *opaque,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_precopy_only,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_postcopy_only)
|
uint64_t *res_postcopy_only)
|
||||||
{
|
{
|
||||||
/* Estimate pending number of bytes to send */
|
/* Estimate pending number of bytes to send */
|
||||||
uint64_t pending;
|
uint64_t pending;
|
||||||
|
@ -885,7 +885,7 @@ static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
|
||||||
pending = BLK_MIG_BLOCK_SIZE;
|
pending = BLK_MIG_BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_migration_block_save_pending(pending);
|
trace_migration_block_state_pending(pending);
|
||||||
/* We don't do postcopy */
|
/* We don't do postcopy */
|
||||||
*res_precopy_only += pending;
|
*res_precopy_only += pending;
|
||||||
}
|
}
|
||||||
|
@ -1020,7 +1020,8 @@ static SaveVMHandlers savevm_block_handlers = {
|
||||||
.save_setup = block_save_setup,
|
.save_setup = block_save_setup,
|
||||||
.save_live_iterate = block_save_iterate,
|
.save_live_iterate = block_save_iterate,
|
||||||
.save_live_complete_precopy = block_save_complete,
|
.save_live_complete_precopy = block_save_complete,
|
||||||
.save_live_pending = block_save_pending,
|
.state_pending_exact = block_state_pending,
|
||||||
|
.state_pending_estimate = block_state_pending,
|
||||||
.load_state = block_load,
|
.load_state = block_load,
|
||||||
.save_cleanup = block_migration_cleanup,
|
.save_cleanup = block_migration_cleanup,
|
||||||
.is_active = block_is_active,
|
.is_active = block_is_active,
|
||||||
|
|
|
@ -53,6 +53,7 @@ qio_channel_block_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc);
|
QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc);
|
||||||
|
|
|
@ -92,3 +92,48 @@ void migration_channel_connect(MigrationState *s,
|
||||||
migrate_fd_connect(s, error);
|
migrate_fd_connect(s, error);
|
||||||
error_free(error);
|
error_free(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @migration_channel_read_peek - Peek at migration channel, without
|
||||||
|
* actually removing it from channel buffer.
|
||||||
|
*
|
||||||
|
* @ioc: the channel object
|
||||||
|
* @buf: the memory region to read data into
|
||||||
|
* @buflen: the number of bytes to read in @buf
|
||||||
|
* @errp: pointer to a NULL-initialized error object
|
||||||
|
*
|
||||||
|
* Returns 0 if successful, returns -1 and sets @errp if fails.
|
||||||
|
*/
|
||||||
|
int migration_channel_read_peek(QIOChannel *ioc,
|
||||||
|
const char *buf,
|
||||||
|
const size_t buflen,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
ssize_t len = 0;
|
||||||
|
struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
len = qio_channel_readv_full(ioc, &iov, 1, NULL, NULL,
|
||||||
|
QIO_CHANNEL_READ_FLAG_MSG_PEEK, errp);
|
||||||
|
|
||||||
|
if (len <= 0 && len != QIO_CHANNEL_ERR_BLOCK) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Failed to peek at channel");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == buflen) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1ms sleep. */
|
||||||
|
if (qemu_in_coroutine()) {
|
||||||
|
qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000);
|
||||||
|
} else {
|
||||||
|
g_usleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -24,4 +24,9 @@ void migration_channel_connect(MigrationState *s,
|
||||||
QIOChannel *ioc,
|
QIOChannel *ioc,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
Error *error_in);
|
Error *error_in);
|
||||||
|
|
||||||
|
int migration_channel_read_peek(QIOChannel *ioc,
|
||||||
|
const char *buf,
|
||||||
|
const size_t buflen,
|
||||||
|
Error **errp);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -714,8 +714,8 @@ void qmp_calc_dirty_rate(int64_t calc_time,
|
||||||
mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
|
mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_sample_pages && mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
|
if (has_sample_pages && mode != DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
|
||||||
error_setg(errp, "either sample-pages or dirty-ring can be specified.");
|
error_setg(errp, "sample-pages is used only in page-sampling mode");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,8 +785,10 @@ void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
|
||||||
DirtyRateStatus_str(info->status));
|
DirtyRateStatus_str(info->status));
|
||||||
monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
|
monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
|
||||||
info->start_time);
|
info->start_time);
|
||||||
monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
|
if (info->mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
|
||||||
info->sample_pages);
|
monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
|
||||||
|
info->sample_pages);
|
||||||
|
}
|
||||||
monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
|
monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
|
||||||
info->calc_time);
|
info->calc_time);
|
||||||
monitor_printf(mon, "Mode: %s\n",
|
monitor_printf(mon, "Mode: %s\n",
|
||||||
|
|
|
@ -26,6 +26,7 @@ softmmu_ss.add(files(
|
||||||
'savevm.c',
|
'savevm.c',
|
||||||
'socket.c',
|
'socket.c',
|
||||||
'tls.c',
|
'tls.c',
|
||||||
|
'threadinfo.c',
|
||||||
), gnutls)
|
), gnutls)
|
||||||
|
|
||||||
softmmu_ss.add(when: rdma, if_true: files('rdma.c'))
|
softmmu_ss.add(when: rdma, if_true: files('rdma.c'))
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "migration.h"
|
#include "migration.h"
|
||||||
#include "savevm.h"
|
#include "savevm.h"
|
||||||
#include "qemu-file.h"
|
#include "qemu-file.h"
|
||||||
|
#include "channel.h"
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
#include "net/announce.h"
|
#include "net/announce.h"
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
#include "multifd.h"
|
#include "multifd.h"
|
||||||
|
#include "threadinfo.h"
|
||||||
#include "qemu/yank.h"
|
#include "qemu/yank.h"
|
||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
#include "yank_functions.h"
|
#include "yank_functions.h"
|
||||||
|
@ -664,10 +666,6 @@ static bool migration_incoming_setup(QEMUFile *f, Error **errp)
|
||||||
{
|
{
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
|
|
||||||
if (multifd_load_setup(errp) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mis->from_src_file) {
|
if (!mis->from_src_file) {
|
||||||
mis->from_src_file = f;
|
mis->from_src_file = f;
|
||||||
}
|
}
|
||||||
|
@ -734,31 +732,56 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
|
||||||
{
|
{
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
bool start_migration;
|
|
||||||
QEMUFile *f;
|
QEMUFile *f;
|
||||||
|
bool default_channel = true;
|
||||||
|
uint32_t channel_magic = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (!mis->from_src_file) {
|
if (migrate_use_multifd() && !migrate_postcopy_ram() &&
|
||||||
/* The first connection (multifd may have multiple) */
|
qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) {
|
||||||
|
/*
|
||||||
|
* With multiple channels, it is possible that we receive channels
|
||||||
|
* out of order on destination side, causing incorrect mapping of
|
||||||
|
* source channels on destination side. Check channel MAGIC to
|
||||||
|
* decide type of channel. Please note this is best effort, postcopy
|
||||||
|
* preempt channel does not send any magic number so avoid it for
|
||||||
|
* postcopy live migration. Also tls live migration already does
|
||||||
|
* tls handshake while initializing main channel so with tls this
|
||||||
|
* issue is not possible.
|
||||||
|
*/
|
||||||
|
ret = migration_channel_read_peek(ioc, (void *)&channel_magic,
|
||||||
|
sizeof(channel_magic), &local_err);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default_channel = (channel_magic == cpu_to_be32(QEMU_VM_FILE_MAGIC));
|
||||||
|
} else {
|
||||||
|
default_channel = !mis->from_src_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multifd_load_setup(errp) != 0) {
|
||||||
|
error_setg(errp, "Failed to setup multifd channels");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default_channel) {
|
||||||
f = qemu_file_new_input(ioc);
|
f = qemu_file_new_input(ioc);
|
||||||
|
|
||||||
if (!migration_incoming_setup(f, errp)) {
|
if (!migration_incoming_setup(f, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Common migration only needs one channel, so we can start
|
|
||||||
* right now. Some features need more than one channel, we wait.
|
|
||||||
*/
|
|
||||||
start_migration = !migration_needs_multiple_sockets();
|
|
||||||
} else {
|
} else {
|
||||||
/* Multiple connections */
|
/* Multiple connections */
|
||||||
assert(migration_needs_multiple_sockets());
|
assert(migration_needs_multiple_sockets());
|
||||||
if (migrate_use_multifd()) {
|
if (migrate_use_multifd()) {
|
||||||
start_migration = multifd_recv_new_channel(ioc, &local_err);
|
multifd_recv_new_channel(ioc, &local_err);
|
||||||
} else {
|
} else {
|
||||||
assert(migrate_postcopy_preempt());
|
assert(migrate_postcopy_preempt());
|
||||||
f = qemu_file_new_input(ioc);
|
f = qemu_file_new_input(ioc);
|
||||||
start_migration = postcopy_preempt_new_channel(mis, f);
|
postcopy_preempt_new_channel(mis, f);
|
||||||
}
|
}
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
@ -766,7 +789,7 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_migration) {
|
if (migration_has_all_channels()) {
|
||||||
/* If it's a recovery, we're done */
|
/* If it's a recovery, we're done */
|
||||||
if (postcopy_try_recover()) {
|
if (postcopy_try_recover()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1051,20 +1074,30 @@ bool migration_is_running(int state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool migrate_show_downtime(MigrationState *s)
|
||||||
|
{
|
||||||
|
return (s->state == MIGRATION_STATUS_COMPLETED) || migration_in_postcopy();
|
||||||
|
}
|
||||||
|
|
||||||
static void populate_time_info(MigrationInfo *info, MigrationState *s)
|
static void populate_time_info(MigrationInfo *info, MigrationState *s)
|
||||||
{
|
{
|
||||||
info->has_status = true;
|
info->has_status = true;
|
||||||
info->has_setup_time = true;
|
info->has_setup_time = true;
|
||||||
info->setup_time = s->setup_time;
|
info->setup_time = s->setup_time;
|
||||||
|
|
||||||
if (s->state == MIGRATION_STATUS_COMPLETED) {
|
if (s->state == MIGRATION_STATUS_COMPLETED) {
|
||||||
info->has_total_time = true;
|
info->has_total_time = true;
|
||||||
info->total_time = s->total_time;
|
info->total_time = s->total_time;
|
||||||
info->has_downtime = true;
|
|
||||||
info->downtime = s->downtime;
|
|
||||||
} else {
|
} else {
|
||||||
info->has_total_time = true;
|
info->has_total_time = true;
|
||||||
info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
|
info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
|
||||||
s->start_time;
|
s->start_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (migrate_show_downtime(s)) {
|
||||||
|
info->has_downtime = true;
|
||||||
|
info->downtime = s->downtime;
|
||||||
|
} else {
|
||||||
info->has_expected_downtime = true;
|
info->has_expected_downtime = true;
|
||||||
info->expected_downtime = s->expected_downtime;
|
info->expected_downtime = s->expected_downtime;
|
||||||
}
|
}
|
||||||
|
@ -1933,6 +1966,8 @@ static void migrate_fd_cleanup(MigrationState *s)
|
||||||
|
|
||||||
g_free(s->hostname);
|
g_free(s->hostname);
|
||||||
s->hostname = NULL;
|
s->hostname = NULL;
|
||||||
|
json_writer_free(s->vmdesc);
|
||||||
|
s->vmdesc = NULL;
|
||||||
|
|
||||||
qemu_savevm_state_cleanup();
|
qemu_savevm_state_cleanup();
|
||||||
|
|
||||||
|
@ -2124,6 +2159,13 @@ bool migration_in_incoming_postcopy(void)
|
||||||
return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END;
|
return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool migration_incoming_postcopy_advised(void)
|
||||||
|
{
|
||||||
|
PostcopyState ps = postcopy_state_get();
|
||||||
|
|
||||||
|
return ps >= POSTCOPY_INCOMING_ADVISE && ps < POSTCOPY_INCOMING_END;
|
||||||
|
}
|
||||||
|
|
||||||
bool migration_in_bg_snapshot(void)
|
bool migration_in_bg_snapshot(void)
|
||||||
{
|
{
|
||||||
MigrationState *s = migrate_get_current();
|
MigrationState *s = migrate_get_current();
|
||||||
|
@ -3778,33 +3820,39 @@ typedef enum {
|
||||||
*/
|
*/
|
||||||
static MigIterateState migration_iteration_run(MigrationState *s)
|
static MigIterateState migration_iteration_run(MigrationState *s)
|
||||||
{
|
{
|
||||||
uint64_t pending_size, pend_pre, pend_compat, pend_post;
|
uint64_t pend_pre, pend_compat, pend_post;
|
||||||
bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE;
|
bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE;
|
||||||
|
|
||||||
qemu_savevm_state_pending(s->to_dst_file, s->threshold_size, &pend_pre,
|
qemu_savevm_state_pending_estimate(&pend_pre, &pend_compat, &pend_post);
|
||||||
&pend_compat, &pend_post);
|
uint64_t pending_size = pend_pre + pend_compat + pend_post;
|
||||||
pending_size = pend_pre + pend_compat + pend_post;
|
|
||||||
|
|
||||||
trace_migrate_pending(pending_size, s->threshold_size,
|
trace_migrate_pending_estimate(pending_size,
|
||||||
pend_pre, pend_compat, pend_post);
|
pend_pre, pend_compat, pend_post);
|
||||||
|
|
||||||
if (pending_size && pending_size >= s->threshold_size) {
|
if (pend_pre + pend_compat <= s->threshold_size) {
|
||||||
/* Still a significant amount to transfer */
|
qemu_savevm_state_pending_exact(&pend_pre, &pend_compat, &pend_post);
|
||||||
if (!in_postcopy && pend_pre <= s->threshold_size &&
|
pending_size = pend_pre + pend_compat + pend_post;
|
||||||
qatomic_read(&s->start_postcopy)) {
|
trace_migrate_pending_exact(pending_size,
|
||||||
if (postcopy_start(s)) {
|
pend_pre, pend_compat, pend_post);
|
||||||
error_report("%s: postcopy failed to start", __func__);
|
}
|
||||||
}
|
|
||||||
return MIG_ITERATE_SKIP;
|
if (!pending_size || pending_size < s->threshold_size) {
|
||||||
}
|
|
||||||
/* Just another iteration step */
|
|
||||||
qemu_savevm_state_iterate(s->to_dst_file, in_postcopy);
|
|
||||||
} else {
|
|
||||||
trace_migration_thread_low_pending(pending_size);
|
trace_migration_thread_low_pending(pending_size);
|
||||||
migration_completion(s);
|
migration_completion(s);
|
||||||
return MIG_ITERATE_BREAK;
|
return MIG_ITERATE_BREAK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Still a significant amount to transfer */
|
||||||
|
if (!in_postcopy && pend_pre <= s->threshold_size &&
|
||||||
|
qatomic_read(&s->start_postcopy)) {
|
||||||
|
if (postcopy_start(s)) {
|
||||||
|
error_report("%s: postcopy failed to start", __func__);
|
||||||
|
}
|
||||||
|
return MIG_ITERATE_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Just another iteration step */
|
||||||
|
qemu_savevm_state_iterate(s->to_dst_file, in_postcopy);
|
||||||
return MIG_ITERATE_RESUME;
|
return MIG_ITERATE_RESUME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3981,10 +4029,13 @@ static void qemu_savevm_wait_unplug(MigrationState *s, int old_state,
|
||||||
static void *migration_thread(void *opaque)
|
static void *migration_thread(void *opaque)
|
||||||
{
|
{
|
||||||
MigrationState *s = opaque;
|
MigrationState *s = opaque;
|
||||||
|
MigrationThread *thread = NULL;
|
||||||
int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
||||||
MigThrError thr_error;
|
MigThrError thr_error;
|
||||||
bool urgent = false;
|
bool urgent = false;
|
||||||
|
|
||||||
|
thread = MigrationThreadAdd("live_migration", qemu_get_thread_id());
|
||||||
|
|
||||||
rcu_register_thread();
|
rcu_register_thread();
|
||||||
|
|
||||||
object_ref(OBJECT(s));
|
object_ref(OBJECT(s));
|
||||||
|
@ -4061,6 +4112,7 @@ static void *migration_thread(void *opaque)
|
||||||
migration_iteration_finish(s);
|
migration_iteration_finish(s);
|
||||||
object_unref(OBJECT(s));
|
object_unref(OBJECT(s));
|
||||||
rcu_unregister_thread();
|
rcu_unregister_thread();
|
||||||
|
MigrationThreadDel(thread);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "exec/cpu-common.h"
|
#include "exec/cpu-common.h"
|
||||||
#include "hw/qdev-core.h"
|
#include "hw/qdev-core.h"
|
||||||
#include "qapi/qapi-types-migration.h"
|
#include "qapi/qapi-types-migration.h"
|
||||||
|
#include "qapi/qmp/json-writer.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/coroutine_int.h"
|
#include "qemu/coroutine_int.h"
|
||||||
#include "io/channel.h"
|
#include "io/channel.h"
|
||||||
|
@ -366,6 +367,9 @@ struct MigrationState {
|
||||||
* This save hostname when out-going migration starts
|
* This save hostname when out-going migration starts
|
||||||
*/
|
*/
|
||||||
char *hostname;
|
char *hostname;
|
||||||
|
|
||||||
|
/* QEMU_VM_VMDESCRIPTION content filled for all non-iterable devices. */
|
||||||
|
JSONWriter *vmdesc;
|
||||||
};
|
};
|
||||||
|
|
||||||
void migrate_set_state(int *state, int old_state, int new_state);
|
void migrate_set_state(int *state, int old_state, int new_state);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "qemu-file.h"
|
#include "qemu-file.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "multifd.h"
|
#include "multifd.h"
|
||||||
|
#include "threadinfo.h"
|
||||||
|
|
||||||
#include "qemu/yank.h"
|
#include "qemu/yank.h"
|
||||||
#include "io/channel-socket.h"
|
#include "io/channel-socket.h"
|
||||||
|
@ -442,6 +443,7 @@ static int multifd_send_pages(QEMUFile *f)
|
||||||
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
|
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
|
||||||
{
|
{
|
||||||
MultiFDPages_t *pages = multifd_send_state->pages;
|
MultiFDPages_t *pages = multifd_send_state->pages;
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
if (!pages->block) {
|
if (!pages->block) {
|
||||||
pages->block = block;
|
pages->block = block;
|
||||||
|
@ -454,14 +456,16 @@ int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
|
||||||
if (pages->num < pages->allocated) {
|
if (pages->num < pages->allocated) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multifd_send_pages(f) < 0) {
|
if (multifd_send_pages(f) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pages->block != block) {
|
if (changed) {
|
||||||
return multifd_queue_page(f, block, offset);
|
return multifd_queue_page(f, block, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -627,16 +631,16 @@ int multifd_send_sync_main(QEMUFile *f)
|
||||||
stat64_add(&ram_atomic_counters.transferred, p->packet_len);
|
stat64_add(&ram_atomic_counters.transferred, p->packet_len);
|
||||||
qemu_mutex_unlock(&p->mutex);
|
qemu_mutex_unlock(&p->mutex);
|
||||||
qemu_sem_post(&p->sem);
|
qemu_sem_post(&p->sem);
|
||||||
|
|
||||||
if (flush_zero_copy && p->c && (multifd_zero_copy_flush(p->c) < 0)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (i = 0; i < migrate_multifd_channels(); i++) {
|
for (i = 0; i < migrate_multifd_channels(); i++) {
|
||||||
MultiFDSendParams *p = &multifd_send_state->params[i];
|
MultiFDSendParams *p = &multifd_send_state->params[i];
|
||||||
|
|
||||||
trace_multifd_send_sync_main_wait(p->id);
|
trace_multifd_send_sync_main_wait(p->id);
|
||||||
qemu_sem_wait(&p->sem_sync);
|
qemu_sem_wait(&p->sem_sync);
|
||||||
|
|
||||||
|
if (flush_zero_copy && p->c && (multifd_zero_copy_flush(p->c) < 0)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
trace_multifd_send_sync_main(multifd_send_state->packet_num);
|
trace_multifd_send_sync_main(multifd_send_state->packet_num);
|
||||||
|
|
||||||
|
@ -646,10 +650,13 @@ int multifd_send_sync_main(QEMUFile *f)
|
||||||
static void *multifd_send_thread(void *opaque)
|
static void *multifd_send_thread(void *opaque)
|
||||||
{
|
{
|
||||||
MultiFDSendParams *p = opaque;
|
MultiFDSendParams *p = opaque;
|
||||||
|
MigrationThread *thread = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
bool use_zero_copy_send = migrate_use_zero_copy_send();
|
bool use_zero_copy_send = migrate_use_zero_copy_send();
|
||||||
|
|
||||||
|
thread = MigrationThreadAdd(p->name, qemu_get_thread_id());
|
||||||
|
|
||||||
trace_multifd_send_thread_start(p->id);
|
trace_multifd_send_thread_start(p->id);
|
||||||
rcu_register_thread();
|
rcu_register_thread();
|
||||||
|
|
||||||
|
@ -759,6 +766,7 @@ out:
|
||||||
qemu_mutex_unlock(&p->mutex);
|
qemu_mutex_unlock(&p->mutex);
|
||||||
|
|
||||||
rcu_unregister_thread();
|
rcu_unregister_thread();
|
||||||
|
MigrationThreadDel(thread);
|
||||||
trace_multifd_send_thread_end(p->id, p->num_packets, p->total_normal_pages);
|
trace_multifd_send_thread_end(p->id, p->num_packets, p->total_normal_pages);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1164,9 +1172,14 @@ int multifd_load_setup(Error **errp)
|
||||||
uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
|
uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
if (!migrate_use_multifd()) {
|
/*
|
||||||
|
* Return successfully if multiFD recv state is already initialised
|
||||||
|
* or multiFD is not enabled.
|
||||||
|
*/
|
||||||
|
if (multifd_recv_state || !migrate_use_multifd()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!migrate_multi_channels_is_allowed()) {
|
if (!migrate_multi_channels_is_allowed()) {
|
||||||
error_setg(errp, "multifd is not supported by current protocol");
|
error_setg(errp, "multifd is not supported by current protocol");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1227,11 +1240,9 @@ bool multifd_recv_all_channels_created(void)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to receive all multifd channels to get ready for the migration.
|
* Try to receive all multifd channels to get ready for the migration.
|
||||||
* - Return true and do not set @errp when correctly receiving all channels;
|
* Sets @errp when failing to receive the current channel.
|
||||||
* - Return false and do not set @errp when correctly receiving the current one;
|
|
||||||
* - Return false and set @errp when failing to receive the current channel.
|
|
||||||
*/
|
*/
|
||||||
bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||||||
{
|
{
|
||||||
MultiFDRecvParams *p;
|
MultiFDRecvParams *p;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
@ -1244,7 +1255,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||||||
"failed to receive packet"
|
"failed to receive packet"
|
||||||
" via multifd channel %d: ",
|
" via multifd channel %d: ",
|
||||||
qatomic_read(&multifd_recv_state->count));
|
qatomic_read(&multifd_recv_state->count));
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
trace_multifd_recv_new_channel(id);
|
trace_multifd_recv_new_channel(id);
|
||||||
|
|
||||||
|
@ -1254,7 +1265,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||||||
id);
|
id);
|
||||||
multifd_recv_terminate_threads(local_err);
|
multifd_recv_terminate_threads(local_err);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
p->c = ioc;
|
p->c = ioc;
|
||||||
object_ref(OBJECT(ioc));
|
object_ref(OBJECT(ioc));
|
||||||
|
@ -1265,6 +1276,4 @@ bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
|
||||||
qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
|
qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
|
||||||
QEMU_THREAD_JOINABLE);
|
QEMU_THREAD_JOINABLE);
|
||||||
qatomic_inc(&multifd_recv_state->count);
|
qatomic_inc(&multifd_recv_state->count);
|
||||||
return qatomic_read(&multifd_recv_state->count) ==
|
|
||||||
migrate_multifd_channels();
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,7 +18,7 @@ void multifd_save_cleanup(void);
|
||||||
int multifd_load_setup(Error **errp);
|
int multifd_load_setup(Error **errp);
|
||||||
int multifd_load_cleanup(Error **errp);
|
int multifd_load_cleanup(Error **errp);
|
||||||
bool multifd_recv_all_channels_created(void);
|
bool multifd_recv_all_channels_created(void);
|
||||||
bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
|
void multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
|
||||||
void multifd_recv_sync_main(void);
|
void multifd_recv_sync_main(void);
|
||||||
int multifd_send_sync_main(QEMUFile *f);
|
int multifd_send_sync_main(QEMUFile *f);
|
||||||
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset);
|
int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset);
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "qemu-file.h"
|
#include "qemu-file.h"
|
||||||
#include "yank_functions.h"
|
#include "yank_functions.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
|
#include "qemu/userfaultfd.h"
|
||||||
|
|
||||||
/* Arbitrary limit on size of each discard command,
|
/* Arbitrary limit on size of each discard command,
|
||||||
* keeps them around ~200 bytes
|
* keeps them around ~200 bytes
|
||||||
|
@ -226,11 +227,9 @@ static bool receive_ufd_features(uint64_t *features)
|
||||||
int ufd;
|
int ufd;
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
/* if we are here __NR_userfaultfd should exists */
|
ufd = uffd_open(O_CLOEXEC);
|
||||||
ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
|
|
||||||
if (ufd == -1) {
|
if (ufd == -1) {
|
||||||
error_report("%s: syscall __NR_userfaultfd failed: %s", __func__,
|
error_report("%s: uffd_open() failed: %s", __func__, strerror(errno));
|
||||||
strerror(errno));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +374,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
|
ufd = uffd_open(O_CLOEXEC);
|
||||||
if (ufd == -1) {
|
if (ufd == -1) {
|
||||||
error_report("%s: userfaultfd not available: %s", __func__,
|
error_report("%s: userfaultfd not available: %s", __func__,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
@ -1160,7 +1159,7 @@ static int postcopy_temp_pages_setup(MigrationIncomingState *mis)
|
||||||
int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
|
int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
|
||||||
{
|
{
|
||||||
/* Open the fd for the kernel to give us userfaults */
|
/* Open the fd for the kernel to give us userfaults */
|
||||||
mis->userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
mis->userfault_fd = uffd_open(O_CLOEXEC | O_NONBLOCK);
|
||||||
if (mis->userfault_fd == -1) {
|
if (mis->userfault_fd == -1) {
|
||||||
error_report("%s: Failed to open userfault fd: %s", __func__,
|
error_report("%s: Failed to open userfault fd: %s", __func__,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
@ -1539,7 +1538,7 @@ void postcopy_unregister_shared_ufd(struct PostCopyFD *pcfd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file)
|
void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The new loading channel has its own threads, so it needs to be
|
* The new loading channel has its own threads, so it needs to be
|
||||||
|
@ -1548,9 +1547,6 @@ bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file)
|
||||||
qemu_file_set_blocking(file, true);
|
qemu_file_set_blocking(file, true);
|
||||||
mis->postcopy_qemufile_dst = file;
|
mis->postcopy_qemufile_dst = file;
|
||||||
trace_postcopy_preempt_new_channel();
|
trace_postcopy_preempt_new_channel();
|
||||||
|
|
||||||
/* Start the migration immediately */
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -190,7 +190,7 @@ enum PostcopyChannels {
|
||||||
RAM_CHANNEL_MAX,
|
RAM_CHANNEL_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file);
|
void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file);
|
||||||
int postcopy_preempt_setup(MigrationState *s, Error **errp);
|
int postcopy_preempt_setup(MigrationState *s, Error **errp);
|
||||||
int postcopy_preempt_wait_channel(MigrationState *s);
|
int postcopy_preempt_wait_channel(MigrationState *s);
|
||||||
|
|
||||||
|
|
120
migration/ram.c
120
migration/ram.c
|
@ -1774,13 +1774,15 @@ out:
|
||||||
static inline void populate_read_range(RAMBlock *block, ram_addr_t offset,
|
static inline void populate_read_range(RAMBlock *block, ram_addr_t offset,
|
||||||
ram_addr_t size)
|
ram_addr_t size)
|
||||||
{
|
{
|
||||||
|
const ram_addr_t end = offset + size;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We read one byte of each page; this will preallocate page tables if
|
* We read one byte of each page; this will preallocate page tables if
|
||||||
* required and populate the shared zeropage on MAP_PRIVATE anonymous memory
|
* required and populate the shared zeropage on MAP_PRIVATE anonymous memory
|
||||||
* where no page was populated yet. This might require adaption when
|
* where no page was populated yet. This might require adaption when
|
||||||
* supporting other mappings, like shmem.
|
* supporting other mappings, like shmem.
|
||||||
*/
|
*/
|
||||||
for (; offset < size; offset += block->page_size) {
|
for (; offset < end; offset += block->page_size) {
|
||||||
char tmp = *((char *)block->host + offset);
|
char tmp = *((char *)block->host + offset);
|
||||||
|
|
||||||
/* Don't optimize the read out */
|
/* Don't optimize the read out */
|
||||||
|
@ -1863,6 +1865,39 @@ void ram_write_tracking_prepare(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int uffd_protect_section(MemoryRegionSection *section,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
const hwaddr size = int128_get64(section->size);
|
||||||
|
const hwaddr offset = section->offset_within_region;
|
||||||
|
RAMBlock *rb = section->mr->ram_block;
|
||||||
|
int uffd_fd = (uintptr_t)opaque;
|
||||||
|
|
||||||
|
return uffd_change_protection(uffd_fd, rb->host + offset, size, true,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ram_block_uffd_protect(RAMBlock *rb, int uffd_fd)
|
||||||
|
{
|
||||||
|
assert(rb->flags & RAM_UF_WRITEPROTECT);
|
||||||
|
|
||||||
|
/* See ram_block_populate_read() */
|
||||||
|
if (rb->mr && memory_region_has_ram_discard_manager(rb->mr)) {
|
||||||
|
RamDiscardManager *rdm = memory_region_get_ram_discard_manager(rb->mr);
|
||||||
|
MemoryRegionSection section = {
|
||||||
|
.mr = rb->mr,
|
||||||
|
.offset_within_region = 0,
|
||||||
|
.size = rb->mr->size,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ram_discard_manager_replay_populated(rdm, §ion,
|
||||||
|
uffd_protect_section,
|
||||||
|
(void *)(uintptr_t)uffd_fd);
|
||||||
|
}
|
||||||
|
return uffd_change_protection(uffd_fd, rb->host,
|
||||||
|
rb->used_length, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ram_write_tracking_start: start UFFD-WP memory tracking
|
* ram_write_tracking_start: start UFFD-WP memory tracking
|
||||||
*
|
*
|
||||||
|
@ -1894,14 +1929,14 @@ int ram_write_tracking_start(void)
|
||||||
block->max_length, UFFDIO_REGISTER_MODE_WP, NULL)) {
|
block->max_length, UFFDIO_REGISTER_MODE_WP, NULL)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
/* Apply UFFD write protection to the block memory range */
|
|
||||||
if (uffd_change_protection(rs->uffdio_fd, block->host,
|
|
||||||
block->max_length, true, false)) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
block->flags |= RAM_UF_WRITEPROTECT;
|
block->flags |= RAM_UF_WRITEPROTECT;
|
||||||
memory_region_ref(block->mr);
|
memory_region_ref(block->mr);
|
||||||
|
|
||||||
|
/* Apply UFFD write protection to the block memory range */
|
||||||
|
if (ram_block_uffd_protect(block, uffd_fd)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
trace_ram_write_tracking_ramblock_start(block->idstr, block->page_size,
|
trace_ram_write_tracking_ramblock_start(block->idstr, block->page_size,
|
||||||
block->host, block->max_length);
|
block->host, block->max_length);
|
||||||
}
|
}
|
||||||
|
@ -1915,12 +1950,6 @@ fail:
|
||||||
if ((block->flags & RAM_UF_WRITEPROTECT) == 0) {
|
if ((block->flags & RAM_UF_WRITEPROTECT) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* In case some memory block failed to be write-protected
|
|
||||||
* remove protection and unregister all succeeded RAM blocks
|
|
||||||
*/
|
|
||||||
uffd_change_protection(rs->uffdio_fd, block->host, block->max_length,
|
|
||||||
false, false);
|
|
||||||
uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length);
|
uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length);
|
||||||
/* Cleanup flags and remove reference */
|
/* Cleanup flags and remove reference */
|
||||||
block->flags &= ~RAM_UF_WRITEPROTECT;
|
block->flags &= ~RAM_UF_WRITEPROTECT;
|
||||||
|
@ -1946,9 +1975,6 @@ void ram_write_tracking_stop(void)
|
||||||
if ((block->flags & RAM_UF_WRITEPROTECT) == 0) {
|
if ((block->flags & RAM_UF_WRITEPROTECT) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Remove protection and unregister all affected RAM blocks */
|
|
||||||
uffd_change_protection(rs->uffdio_fd, block->host, block->max_length,
|
|
||||||
false, false);
|
|
||||||
uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length);
|
uffd_unregister_memory(rs->uffdio_fd, block->host, block->max_length);
|
||||||
|
|
||||||
trace_ram_write_tracking_ramblock_stop(block->idstr, block->page_size,
|
trace_ram_write_tracking_ramblock_stop(block->idstr, block->page_size,
|
||||||
|
@ -2319,8 +2345,25 @@ static void pss_host_page_prepare(PageSearchStatus *pss)
|
||||||
size_t guest_pfns = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS;
|
size_t guest_pfns = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS;
|
||||||
|
|
||||||
pss->host_page_sending = true;
|
pss->host_page_sending = true;
|
||||||
pss->host_page_start = ROUND_DOWN(pss->page, guest_pfns);
|
if (guest_pfns <= 1) {
|
||||||
pss->host_page_end = ROUND_UP(pss->page + 1, guest_pfns);
|
/*
|
||||||
|
* This covers both when guest psize == host psize, or when guest
|
||||||
|
* has larger psize than the host (guest_pfns==0).
|
||||||
|
*
|
||||||
|
* For the latter, we always send one whole guest page per
|
||||||
|
* iteration of the host page (example: an Alpha VM on x86 host
|
||||||
|
* will have guest psize 8K while host psize 4K).
|
||||||
|
*/
|
||||||
|
pss->host_page_start = pss->page;
|
||||||
|
pss->host_page_end = pss->page + 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The host page spans over multiple guest pages, we send them
|
||||||
|
* within the same host page iteration.
|
||||||
|
*/
|
||||||
|
pss->host_page_start = ROUND_DOWN(pss->page, guest_pfns);
|
||||||
|
pss->host_page_end = ROUND_UP(pss->page + 1, guest_pfns);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3392,19 +3435,35 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
|
static void ram_state_pending_estimate(void *opaque,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_precopy_only,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_postcopy_only)
|
uint64_t *res_postcopy_only)
|
||||||
{
|
{
|
||||||
RAMState **temp = opaque;
|
RAMState **temp = opaque;
|
||||||
RAMState *rs = *temp;
|
RAMState *rs = *temp;
|
||||||
uint64_t remaining_size;
|
|
||||||
|
|
||||||
remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE;
|
uint64_t remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE;
|
||||||
|
|
||||||
if (!migration_in_postcopy() &&
|
if (migrate_postcopy_ram()) {
|
||||||
remaining_size < max_size) {
|
/* We can do postcopy, and all the data is postcopiable */
|
||||||
|
*res_postcopy_only += remaining_size;
|
||||||
|
} else {
|
||||||
|
*res_precopy_only += remaining_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ram_state_pending_exact(void *opaque,
|
||||||
|
uint64_t *res_precopy_only,
|
||||||
|
uint64_t *res_compatible,
|
||||||
|
uint64_t *res_postcopy_only)
|
||||||
|
{
|
||||||
|
RAMState **temp = opaque;
|
||||||
|
RAMState *rs = *temp;
|
||||||
|
|
||||||
|
uint64_t remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE;
|
||||||
|
|
||||||
|
if (!migration_in_postcopy()) {
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
WITH_RCU_READ_LOCK_GUARD() {
|
WITH_RCU_READ_LOCK_GUARD() {
|
||||||
migration_bitmap_sync_precopy(rs);
|
migration_bitmap_sync_precopy(rs);
|
||||||
|
@ -4091,12 +4150,6 @@ int ram_load_postcopy(QEMUFile *f, int channel)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool postcopy_is_advised(void)
|
|
||||||
{
|
|
||||||
PostcopyState ps = postcopy_state_get();
|
|
||||||
return ps >= POSTCOPY_INCOMING_ADVISE && ps < POSTCOPY_INCOMING_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool postcopy_is_running(void)
|
static bool postcopy_is_running(void)
|
||||||
{
|
{
|
||||||
PostcopyState ps = postcopy_state_get();
|
PostcopyState ps = postcopy_state_get();
|
||||||
|
@ -4167,7 +4220,7 @@ static int ram_load_precopy(QEMUFile *f)
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0;
|
int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0;
|
||||||
/* ADVISE is earlier, it shows the source has the postcopy capability on */
|
/* ADVISE is earlier, it shows the source has the postcopy capability on */
|
||||||
bool postcopy_advised = postcopy_is_advised();
|
bool postcopy_advised = migration_incoming_postcopy_advised();
|
||||||
if (!migrate_use_compression()) {
|
if (!migrate_use_compression()) {
|
||||||
invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE;
|
invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE;
|
||||||
}
|
}
|
||||||
|
@ -4560,7 +4613,8 @@ static SaveVMHandlers savevm_ram_handlers = {
|
||||||
.save_live_complete_postcopy = ram_save_complete,
|
.save_live_complete_postcopy = ram_save_complete,
|
||||||
.save_live_complete_precopy = ram_save_complete,
|
.save_live_complete_precopy = ram_save_complete,
|
||||||
.has_postcopy = ram_has_postcopy,
|
.has_postcopy = ram_has_postcopy,
|
||||||
.save_live_pending = ram_save_pending,
|
.state_pending_exact = ram_state_pending_exact,
|
||||||
|
.state_pending_estimate = ram_state_pending_estimate,
|
||||||
.load_state = ram_load,
|
.load_state = ram_load,
|
||||||
.save_cleanup = ram_save_cleanup,
|
.save_cleanup = ram_save_cleanup,
|
||||||
.load_setup = ram_load_setup,
|
.load_setup = ram_load_setup,
|
||||||
|
|
|
@ -2785,7 +2785,8 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
rdma = qatomic_rcu_read(&rioc->rdmaout);
|
rdma = qatomic_rcu_read(&rioc->rdmaout);
|
||||||
|
|
||||||
if (!rdma) {
|
if (!rdma) {
|
||||||
return -EIO;
|
error_setg(errp, "RDMA control channel output is not set");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
@ -2797,7 +2798,8 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
ret = qemu_rdma_write_flush(f, rdma);
|
ret = qemu_rdma_write_flush(f, rdma);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
return ret;
|
error_setg(errp, "qemu_rdma_write_flush returned %d", ret);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < niov; i++) {
|
for (i = 0; i < niov; i++) {
|
||||||
|
@ -2816,7 +2818,8 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
return ret;
|
error_setg(errp, "qemu_rdma_exchange_send returned %d", ret);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data += len;
|
data += len;
|
||||||
|
@ -2854,6 +2857,7 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
|
||||||
size_t niov,
|
size_t niov,
|
||||||
int **fds,
|
int **fds,
|
||||||
size_t *nfds,
|
size_t *nfds,
|
||||||
|
int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
|
||||||
|
@ -2867,7 +2871,8 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
|
||||||
rdma = qatomic_rcu_read(&rioc->rdmain);
|
rdma = qatomic_rcu_read(&rioc->rdmain);
|
||||||
|
|
||||||
if (!rdma) {
|
if (!rdma) {
|
||||||
return -EIO;
|
error_setg(errp, "RDMA control channel input is not set");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_ERROR_STATE();
|
CHECK_ERROR_STATE();
|
||||||
|
@ -2903,7 +2908,8 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdma->error_state = ret;
|
rdma->error_state = ret;
|
||||||
return ret;
|
error_setg(errp, "qemu_rdma_exchange_recv returned %d", ret);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
#include "postcopy-ram.h"
|
#include "postcopy-ram.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-commands-migration.h"
|
#include "qapi/qapi-commands-migration.h"
|
||||||
#include "qapi/qmp/json-writer.h"
|
|
||||||
#include "qapi/clone-visitor.h"
|
#include "qapi/clone-visitor.h"
|
||||||
#include "qapi/qapi-builtin-visit.h"
|
#include "qapi/qapi-builtin-visit.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
|
@ -67,6 +66,7 @@
|
||||||
#include "net/announce.h"
|
#include "net/announce.h"
|
||||||
#include "qemu/yank.h"
|
#include "qemu/yank.h"
|
||||||
#include "yank_functions.h"
|
#include "yank_functions.h"
|
||||||
|
#include "sysemu/qtest.h"
|
||||||
|
|
||||||
const unsigned int postcopy_ram_discard_version;
|
const unsigned int postcopy_ram_discard_version;
|
||||||
|
|
||||||
|
@ -586,6 +586,7 @@ static void dump_vmstate_vmsd(FILE *out_file,
|
||||||
field++;
|
field++;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
assert(field->flags == VMS_END);
|
||||||
fprintf(out_file, "\n%*s]", indent, "");
|
fprintf(out_file, "\n%*s]", indent, "");
|
||||||
}
|
}
|
||||||
if (vmsd->subsections != NULL) {
|
if (vmsd->subsections != NULL) {
|
||||||
|
@ -804,6 +805,42 @@ void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform some basic checks on vmsd's at registration
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
static void vmstate_check(const VMStateDescription *vmsd)
|
||||||
|
{
|
||||||
|
const VMStateField *field = vmsd->fields;
|
||||||
|
const VMStateDescription **subsection = vmsd->subsections;
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
while (field->name) {
|
||||||
|
if (field->flags & (VMS_STRUCT | VMS_VSTRUCT)) {
|
||||||
|
/* Recurse to sub structures */
|
||||||
|
vmstate_check(field->vmsd);
|
||||||
|
}
|
||||||
|
/* Carry on */
|
||||||
|
field++;
|
||||||
|
}
|
||||||
|
/* Check for the end of field list canary */
|
||||||
|
if (field->flags != VMS_END) {
|
||||||
|
error_report("VMSTATE not ending with VMS_END: %s", vmsd->name);
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (subsection && *subsection) {
|
||||||
|
/*
|
||||||
|
* The name of a subsection should start with the name of the
|
||||||
|
* current object.
|
||||||
|
*/
|
||||||
|
assert(!strncmp(vmsd->name, (*subsection)->name, strlen(vmsd->name)));
|
||||||
|
vmstate_check(*subsection);
|
||||||
|
subsection++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
@ -849,6 +886,11 @@ int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id,
|
||||||
} else {
|
} else {
|
||||||
se->instance_id = instance_id;
|
se->instance_id = instance_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Perform a recursive sanity check during the test runs */
|
||||||
|
if (qtest_enabled()) {
|
||||||
|
vmstate_check(vmsd);
|
||||||
|
}
|
||||||
assert(!se->compat || se->instance_id == 0);
|
assert(!se->compat || se->instance_id == 0);
|
||||||
savevm_state_handler_insert(se);
|
savevm_state_handler_insert(se);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -898,17 +940,6 @@ static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmstate_save(QEMUFile *f, SaveStateEntry *se,
|
|
||||||
JSONWriter *vmdesc)
|
|
||||||
{
|
|
||||||
trace_vmstate_save(se->idstr, se->vmsd ? se->vmsd->name : "(old)");
|
|
||||||
if (!se->vmsd) {
|
|
||||||
vmstate_save_old_style(f, se, vmdesc);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return vmstate_save_state(f, se->vmsd, se->opaque, vmdesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write the header for device section (QEMU_VM_SECTION START/END/PART/FULL)
|
* Write the header for device section (QEMU_VM_SECTION START/END/PART/FULL)
|
||||||
*/
|
*/
|
||||||
|
@ -942,6 +973,43 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (se->vmsd && !vmstate_save_needed(se->vmsd, se->opaque)) {
|
||||||
|
trace_savevm_section_skip(se->idstr, se->section_id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_savevm_section_start(se->idstr, se->section_id);
|
||||||
|
save_section_header(f, se, QEMU_VM_SECTION_FULL);
|
||||||
|
if (vmdesc) {
|
||||||
|
json_writer_start_object(vmdesc, NULL);
|
||||||
|
json_writer_str(vmdesc, "name", se->idstr);
|
||||||
|
json_writer_int64(vmdesc, "instance_id", se->instance_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_vmstate_save(se->idstr, se->vmsd ? se->vmsd->name : "(old)");
|
||||||
|
if (!se->vmsd) {
|
||||||
|
vmstate_save_old_style(f, se, vmdesc);
|
||||||
|
} else {
|
||||||
|
ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_savevm_section_end(se->idstr, se->section_id, 0);
|
||||||
|
save_section_footer(f, se);
|
||||||
|
if (vmdesc) {
|
||||||
|
json_writer_end_object(vmdesc);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* qemu_savevm_command_send: Send a 'QEMU_VM_COMMAND' type element with the
|
* qemu_savevm_command_send: Send a 'QEMU_VM_COMMAND' type element with the
|
||||||
* command and associated data.
|
* command and associated data.
|
||||||
|
@ -1164,12 +1232,27 @@ bool qemu_savevm_state_guest_unplug_pending(void)
|
||||||
|
|
||||||
void qemu_savevm_state_setup(QEMUFile *f)
|
void qemu_savevm_state_setup(QEMUFile *f)
|
||||||
{
|
{
|
||||||
|
MigrationState *ms = migrate_get_current();
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ms->vmdesc = json_writer_new(false);
|
||||||
|
json_writer_start_object(ms->vmdesc, NULL);
|
||||||
|
json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size());
|
||||||
|
json_writer_start_array(ms->vmdesc, "devices");
|
||||||
|
|
||||||
trace_savevm_state_setup();
|
trace_savevm_state_setup();
|
||||||
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
||||||
|
if (se->vmsd && se->vmsd->early_setup) {
|
||||||
|
ret = vmstate_save(f, se, ms->vmdesc);
|
||||||
|
if (ret) {
|
||||||
|
qemu_file_set_error(f, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!se->ops || !se->ops->save_setup) {
|
if (!se->ops || !se->ops->save_setup) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1365,41 +1448,23 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
|
||||||
bool in_postcopy,
|
bool in_postcopy,
|
||||||
bool inactivate_disks)
|
bool inactivate_disks)
|
||||||
{
|
{
|
||||||
g_autoptr(JSONWriter) vmdesc = NULL;
|
MigrationState *ms = migrate_get_current();
|
||||||
|
JSONWriter *vmdesc = ms->vmdesc;
|
||||||
int vmdesc_len;
|
int vmdesc_len;
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
vmdesc = json_writer_new(false);
|
|
||||||
json_writer_start_object(vmdesc, NULL);
|
|
||||||
json_writer_int64(vmdesc, "page_size", qemu_target_page_size());
|
|
||||||
json_writer_start_array(vmdesc, "devices");
|
|
||||||
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
||||||
|
if (se->vmsd && se->vmsd->early_setup) {
|
||||||
if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
|
/* Already saved during qemu_savevm_state_setup(). */
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (se->vmsd && !vmstate_save_needed(se->vmsd, se->opaque)) {
|
|
||||||
trace_savevm_section_skip(se->idstr, se->section_id);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_savevm_section_start(se->idstr, se->section_id);
|
|
||||||
|
|
||||||
json_writer_start_object(vmdesc, NULL);
|
|
||||||
json_writer_str(vmdesc, "name", se->idstr);
|
|
||||||
json_writer_int64(vmdesc, "instance_id", se->instance_id);
|
|
||||||
|
|
||||||
save_section_header(f, se, QEMU_VM_SECTION_FULL);
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
trace_savevm_section_end(se->idstr, se->section_id, 0);
|
|
||||||
save_section_footer(f, se);
|
|
||||||
|
|
||||||
json_writer_end_object(vmdesc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inactivate_disks) {
|
if (inactivate_disks) {
|
||||||
|
@ -1428,6 +1493,10 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
|
||||||
qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len);
|
qemu_put_buffer(f, (uint8_t *)json_writer_get(vmdesc), vmdesc_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free it now to detect any inconsistencies. */
|
||||||
|
json_writer_free(vmdesc);
|
||||||
|
ms->vmdesc = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1472,10 +1541,9 @@ flush:
|
||||||
* the result is split into the amount for units that can and
|
* the result is split into the amount for units that can and
|
||||||
* for units that can't do postcopy.
|
* for units that can't do postcopy.
|
||||||
*/
|
*/
|
||||||
void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
|
void qemu_savevm_state_pending_estimate(uint64_t *res_precopy_only,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_postcopy_only)
|
||||||
uint64_t *res_postcopy_only)
|
|
||||||
{
|
{
|
||||||
SaveStateEntry *se;
|
SaveStateEntry *se;
|
||||||
|
|
||||||
|
@ -1483,9 +1551,8 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
|
||||||
*res_compatible = 0;
|
*res_compatible = 0;
|
||||||
*res_postcopy_only = 0;
|
*res_postcopy_only = 0;
|
||||||
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
||||||
if (!se->ops || !se->ops->save_live_pending) {
|
if (!se->ops || !se->ops->state_pending_exact) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (se->ops->is_active) {
|
if (se->ops->is_active) {
|
||||||
|
@ -1493,9 +1560,34 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
se->ops->save_live_pending(f, se->opaque, threshold_size,
|
se->ops->state_pending_exact(se->opaque,
|
||||||
res_precopy_only, res_compatible,
|
res_precopy_only, res_compatible,
|
||||||
res_postcopy_only);
|
res_postcopy_only);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_savevm_state_pending_exact(uint64_t *res_precopy_only,
|
||||||
|
uint64_t *res_compatible,
|
||||||
|
uint64_t *res_postcopy_only)
|
||||||
|
{
|
||||||
|
SaveStateEntry *se;
|
||||||
|
|
||||||
|
*res_precopy_only = 0;
|
||||||
|
*res_compatible = 0;
|
||||||
|
*res_postcopy_only = 0;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
|
||||||
|
if (!se->ops || !se->ops->state_pending_estimate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (se->ops->is_active) {
|
||||||
|
if (!se->ops->is_active(se->opaque)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
se->ops->state_pending_estimate(se->opaque,
|
||||||
|
res_precopy_only, res_compatible,
|
||||||
|
res_postcopy_only);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1595,21 +1687,10 @@ int qemu_save_device_state(QEMUFile *f)
|
||||||
if (se->is_ram) {
|
if (se->is_ram) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (se->vmsd && !vmstate_save_needed(se->vmsd, se->opaque)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
save_section_header(f, se, QEMU_VM_SECTION_FULL);
|
|
||||||
|
|
||||||
ret = vmstate_save(f, se, NULL);
|
ret = vmstate_save(f, se, NULL);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
save_section_footer(f, se);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_put_byte(f, QEMU_VM_EOF);
|
qemu_put_byte(f, QEMU_VM_EOF);
|
||||||
|
|
|
@ -40,10 +40,12 @@ void qemu_savevm_state_cleanup(void);
|
||||||
void qemu_savevm_state_complete_postcopy(QEMUFile *f);
|
void qemu_savevm_state_complete_postcopy(QEMUFile *f);
|
||||||
int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
|
int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
|
||||||
bool inactivate_disks);
|
bool inactivate_disks);
|
||||||
void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size,
|
void qemu_savevm_state_pending_exact(uint64_t *res_precopy_only,
|
||||||
uint64_t *res_precopy_only,
|
uint64_t *res_compatible,
|
||||||
uint64_t *res_compatible,
|
uint64_t *res_postcopy_only);
|
||||||
uint64_t *res_postcopy_only);
|
void qemu_savevm_state_pending_estimate(uint64_t *res_precopy_only,
|
||||||
|
uint64_t *res_compatible,
|
||||||
|
uint64_t *res_postcopy_only);
|
||||||
void qemu_savevm_send_ping(QEMUFile *f, uint32_t value);
|
void qemu_savevm_send_ping(QEMUFile *f, uint32_t value);
|
||||||
void qemu_savevm_send_open_return_path(QEMUFile *f);
|
void qemu_savevm_send_open_return_path(QEMUFile *f);
|
||||||
int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len);
|
int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len);
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Migration Threads info
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 HUAWEI TECHNOLOGIES CO., LTD.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Jiang Jiacheng <jiangjiacheng@huawei.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "threadinfo.h"
|
||||||
|
|
||||||
|
static QLIST_HEAD(, MigrationThread) migration_threads;
|
||||||
|
|
||||||
|
MigrationThread *MigrationThreadAdd(const char *name, int thread_id)
|
||||||
|
{
|
||||||
|
MigrationThread *thread = g_new0(MigrationThread, 1);
|
||||||
|
thread->name = name;
|
||||||
|
thread->thread_id = thread_id;
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&migration_threads, thread, node);
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MigrationThreadDel(MigrationThread *thread)
|
||||||
|
{
|
||||||
|
if (thread) {
|
||||||
|
QLIST_REMOVE(thread, node);
|
||||||
|
g_free(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MigrationThreadInfoList *qmp_query_migrationthreads(Error **errp)
|
||||||
|
{
|
||||||
|
MigrationThreadInfoList *head = NULL;
|
||||||
|
MigrationThreadInfoList **tail = &head;
|
||||||
|
MigrationThread *thread = NULL;
|
||||||
|
|
||||||
|
QLIST_FOREACH(thread, &migration_threads, node) {
|
||||||
|
MigrationThreadInfo *info = g_new0(MigrationThreadInfo, 1);
|
||||||
|
info->name = g_strdup(thread->name);
|
||||||
|
info->thread_id = thread->thread_id;
|
||||||
|
|
||||||
|
QAPI_LIST_APPEND(tail, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Migration Threads info
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 HUAWEI TECHNOLOGIES CO., LTD.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Jiang Jiacheng <jiangjiacheng@huawei.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/queue.h"
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qapi-commands-migration.h"
|
||||||
|
|
||||||
|
typedef struct MigrationThread MigrationThread;
|
||||||
|
|
||||||
|
struct MigrationThread {
|
||||||
|
const char *name; /* the name of migration thread */
|
||||||
|
int thread_id; /* ID of the underlying host thread */
|
||||||
|
QLIST_ENTRY(MigrationThread) node;
|
||||||
|
};
|
||||||
|
|
||||||
|
MigrationThread *MigrationThreadAdd(const char *name, int thread_id);
|
||||||
|
|
||||||
|
void MigrationThreadDel(MigrationThread *info);
|
|
@ -150,7 +150,8 @@ migrate_fd_cleanup(void) ""
|
||||||
migrate_fd_error(const char *error_desc) "error=%s"
|
migrate_fd_error(const char *error_desc) "error=%s"
|
||||||
migrate_fd_cancel(void) ""
|
migrate_fd_cancel(void) ""
|
||||||
migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx"
|
migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx"
|
||||||
migrate_pending(uint64_t size, uint64_t max, uint64_t pre, uint64_t compat, uint64_t post) "pending size %" PRIu64 " max %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")"
|
migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t compat, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")"
|
||||||
|
migrate_pending_estimate(uint64_t size, uint64_t pre, uint64_t compat, uint64_t post) "estimate pending size %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")"
|
||||||
migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d"
|
migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d"
|
||||||
migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi64
|
migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi64
|
||||||
migration_completion_file_err(void) ""
|
migration_completion_file_err(void) ""
|
||||||
|
@ -330,7 +331,7 @@ send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uin
|
||||||
dirty_bitmap_save_iterate(int in_postcopy) "in postcopy: %d"
|
dirty_bitmap_save_iterate(int in_postcopy) "in postcopy: %d"
|
||||||
dirty_bitmap_save_complete_enter(void) ""
|
dirty_bitmap_save_complete_enter(void) ""
|
||||||
dirty_bitmap_save_complete_finish(void) ""
|
dirty_bitmap_save_complete_finish(void) ""
|
||||||
dirty_bitmap_save_pending(uint64_t pending, uint64_t max_size) "pending %" PRIu64 " max: %" PRIu64
|
dirty_bitmap_state_pending(uint64_t pending) "pending %" PRIu64
|
||||||
dirty_bitmap_load_complete(void) ""
|
dirty_bitmap_load_complete(void) ""
|
||||||
dirty_bitmap_load_bits_enter(uint64_t first_sector, uint32_t nr_sectors) "chunk: %" PRIu64 " %" PRIu32
|
dirty_bitmap_load_bits_enter(uint64_t first_sector, uint32_t nr_sectors) "chunk: %" PRIu64 " %" PRIu32
|
||||||
dirty_bitmap_load_bits_zeroes(void) ""
|
dirty_bitmap_load_bits_zeroes(void) ""
|
||||||
|
@ -355,7 +356,7 @@ migration_block_save_device_dirty(int64_t sector) "Error reading sector %" PRId6
|
||||||
migration_block_flush_blks(const char *action, int submitted, int read_done, int transferred) "%s submitted %d read_done %d transferred %d"
|
migration_block_flush_blks(const char *action, int submitted, int read_done, int transferred) "%s submitted %d read_done %d transferred %d"
|
||||||
migration_block_save(const char *mig_stage, int submitted, int transferred) "Enter save live %s submitted %d transferred %d"
|
migration_block_save(const char *mig_stage, int submitted, int transferred) "Enter save live %s submitted %d transferred %d"
|
||||||
migration_block_save_complete(void) "Block migration completed"
|
migration_block_save_complete(void) "Block migration completed"
|
||||||
migration_block_save_pending(uint64_t pending) "Enter save live pending %" PRIu64
|
migration_block_state_pending(uint64_t pending) "Enter save live pending %" PRIu64
|
||||||
|
|
||||||
# page_cache.c
|
# page_cache.c
|
||||||
migration_pagecache_init(int64_t max_num_items) "Setting cache buckets to %" PRId64
|
migration_pagecache_init(int64_t max_num_items) "Setting cache buckets to %" PRId64
|
||||||
|
|
|
@ -154,6 +154,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
}
|
}
|
||||||
field++;
|
field++;
|
||||||
}
|
}
|
||||||
|
assert(field->flags == VMS_END);
|
||||||
ret = vmstate_subsection_load(f, vmsd, opaque);
|
ret = vmstate_subsection_load(f, vmsd, opaque);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -408,6 +409,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
}
|
}
|
||||||
field++;
|
field++;
|
||||||
}
|
}
|
||||||
|
assert(field->flags == VMS_END);
|
||||||
|
|
||||||
if (vmdesc) {
|
if (vmdesc) {
|
||||||
json_writer_end_array(vmdesc);
|
json_writer_end_array(vmdesc);
|
||||||
|
|
|
@ -1958,6 +1958,35 @@
|
||||||
{ 'command': 'query-vcpu-dirty-limit',
|
{ 'command': 'query-vcpu-dirty-limit',
|
||||||
'returns': [ 'DirtyLimitInfo' ] }
|
'returns': [ 'DirtyLimitInfo' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @MigrationThreadInfo:
|
||||||
|
#
|
||||||
|
# Information about migrationthreads
|
||||||
|
#
|
||||||
|
# @name: the name of migration thread
|
||||||
|
#
|
||||||
|
# @thread-id: ID of the underlying host thread
|
||||||
|
#
|
||||||
|
# Since: 7.2
|
||||||
|
##
|
||||||
|
{ 'struct': 'MigrationThreadInfo',
|
||||||
|
'data': {'name': 'str',
|
||||||
|
'thread-id': 'int'} }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @query-migrationthreads:
|
||||||
|
#
|
||||||
|
# Returns information of migration threads
|
||||||
|
#
|
||||||
|
# data: migration thread name
|
||||||
|
#
|
||||||
|
# returns: information about migration threads
|
||||||
|
#
|
||||||
|
# Since: 7.2
|
||||||
|
##
|
||||||
|
{ 'command': 'query-migrationthreads',
|
||||||
|
'returns': ['MigrationThreadInfo'] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @snapshot-save:
|
# @snapshot-save:
|
||||||
#
|
#
|
||||||
|
|
|
@ -614,7 +614,7 @@ static int coroutine_fn prh_read(PRHelperClient *client, void *buf, int sz,
|
||||||
iov.iov_base = buf;
|
iov.iov_base = buf;
|
||||||
iov.iov_len = sz;
|
iov.iov_len = sz;
|
||||||
n_read = qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, 1,
|
n_read = qio_channel_readv_full(QIO_CHANNEL(client->ioc), &iov, 1,
|
||||||
&fds, &nfds, errp);
|
&fds, &nfds, 0, errp);
|
||||||
|
|
||||||
if (n_read == QIO_CHANNEL_ERR_BLOCK) {
|
if (n_read == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN);
|
qio_channel_yield(QIO_CHANNEL(client->ioc), G_IO_IN);
|
||||||
|
|
|
@ -61,14 +61,14 @@ static bool uffd_feature_thread_id;
|
||||||
#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
|
#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD)
|
||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/userfaultfd.h>
|
#include "qemu/userfaultfd.h"
|
||||||
|
|
||||||
static bool ufd_version_check(void)
|
static bool ufd_version_check(void)
|
||||||
{
|
{
|
||||||
struct uffdio_api api_struct;
|
struct uffdio_api api_struct;
|
||||||
uint64_t ioctl_mask;
|
uint64_t ioctl_mask;
|
||||||
|
|
||||||
int ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
|
int ufd = uffd_open(O_CLOEXEC);
|
||||||
|
|
||||||
if (ufd == -1) {
|
if (ufd == -1) {
|
||||||
g_test_message("Skipping test: userfaultfd not available");
|
g_test_message("Skipping test: userfaultfd not available");
|
||||||
|
|
|
@ -115,7 +115,7 @@ void *tpm_emu_ctrl_thread(void *data)
|
||||||
int *pfd = NULL;
|
int *pfd = NULL;
|
||||||
size_t nfd = 0;
|
size_t nfd = 0;
|
||||||
|
|
||||||
qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort);
|
qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, 0, &error_abort);
|
||||||
cmd = be32_to_cpu(cmd);
|
cmd = be32_to_cpu(cmd);
|
||||||
g_assert_cmpint(cmd, ==, CMD_SET_DATAFD);
|
g_assert_cmpint(cmd, ==, CMD_SET_DATAFD);
|
||||||
g_assert_cmpint(nfd, ==, 1);
|
g_assert_cmpint(nfd, ==, 1);
|
||||||
|
|
|
@ -460,6 +460,7 @@ static void test_io_channel_unix_fd_pass(void)
|
||||||
G_N_ELEMENTS(iorecv),
|
G_N_ELEMENTS(iorecv),
|
||||||
&fdrecv,
|
&fdrecv,
|
||||||
&nfdrecv,
|
&nfdrecv,
|
||||||
|
0,
|
||||||
&error_abort);
|
&error_abort);
|
||||||
|
|
||||||
g_assert(nfdrecv == G_N_ELEMENTS(fdsend));
|
g_assert(nfdrecv == G_N_ELEMENTS(fdsend));
|
||||||
|
|
|
@ -19,6 +19,15 @@
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
int uffd_open(int flags)
|
||||||
|
{
|
||||||
|
#if defined(__NR_userfaultfd)
|
||||||
|
return syscall(__NR_userfaultfd, flags);
|
||||||
|
#else
|
||||||
|
return -EINVAL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* uffd_query_features: query UFFD features
|
* uffd_query_features: query UFFD features
|
||||||
*
|
*
|
||||||
|
@ -32,7 +41,7 @@ int uffd_query_features(uint64_t *features)
|
||||||
struct uffdio_api api_struct = { 0 };
|
struct uffdio_api api_struct = { 0 };
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
uffd_fd = syscall(__NR_userfaultfd, O_CLOEXEC);
|
uffd_fd = uffd_open(O_CLOEXEC);
|
||||||
if (uffd_fd < 0) {
|
if (uffd_fd < 0) {
|
||||||
trace_uffd_query_features_nosys(errno);
|
trace_uffd_query_features_nosys(errno);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -69,7 +78,7 @@ int uffd_create_fd(uint64_t features, bool non_blocking)
|
||||||
uint64_t ioctl_mask = BIT(_UFFDIO_REGISTER) | BIT(_UFFDIO_UNREGISTER);
|
uint64_t ioctl_mask = BIT(_UFFDIO_REGISTER) | BIT(_UFFDIO_UNREGISTER);
|
||||||
|
|
||||||
flags = O_CLOEXEC | (non_blocking ? O_NONBLOCK : 0);
|
flags = O_CLOEXEC | (non_blocking ? O_NONBLOCK : 0);
|
||||||
uffd_fd = syscall(__NR_userfaultfd, flags);
|
uffd_fd = uffd_open(flags);
|
||||||
if (uffd_fd < 0) {
|
if (uffd_fd < 0) {
|
||||||
trace_uffd_create_fd_nosys(errno);
|
trace_uffd_create_fd_nosys(errno);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -116,7 +116,7 @@ vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg)
|
||||||
* qio_channel_readv_full may have short reads, keeping calling it
|
* qio_channel_readv_full may have short reads, keeping calling it
|
||||||
* until getting VHOST_USER_HDR_SIZE or 0 bytes in total
|
* until getting VHOST_USER_HDR_SIZE or 0 bytes in total
|
||||||
*/
|
*/
|
||||||
rc = qio_channel_readv_full(ioc, &iov, 1, &fds, &nfds, &local_err);
|
rc = qio_channel_readv_full(ioc, &iov, 1, &fds, &nfds, 0, &local_err);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
if (rc == QIO_CHANNEL_ERR_BLOCK) {
|
if (rc == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
assert(local_err == NULL);
|
assert(local_err == NULL);
|
||||||
|
|
Loading…
Reference in New Issue