Migration
Amit: migration: remove myself as maintainer MAINTAINERS: update my email address Ashijeet: migrate: Introduce zero RAM checks to skip RAM migration Pavel: Postcopy release RAM Halil: consolidate VMStateField.start Hailiang: COLO: fix setting checkpoint-delay not working properly COLO: Shutdown related socket fd while do failover COLO: Don't process failover request while loading VM's state Me: migration: Add VMSTATE_UNUSED_VARRAY_UINT32 migration: Add VMSTATE_WITH_TMP tests/migration: Add test for VMSTATE_WITH_TMP virtio-net VMState conversion and new VMSTATE macros -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJYoe6nAAoJEAUWMx68W/3n98MP/2ZdTGPL7YYrDkHOt5cyOYAY vgnu9ewkJcF7aWv2YgYOvkSNABDZG1B84bIjT1bQrK4pVcoGTtGEFT3AAd2eAkWc kgV02xlW2NdCVD4Yeh2Y18YGH6bKrDie3pJtEhgkhkOgJUjWvtvT2BXoHIWjkJss nLQpMaHFsinap3Xp/3xxNhEFlimjsqU6sDyBOBYBfWxkhG4Y9W2GtF8Xm2vSrlJ9 H+7NfLDOTYa/vNiJiRme3Qi96SxBLklbZku3BvmZHymSVTBce22Bi6fMvLlgsPUa Ht14+VMk/JDeECmFojazl9Rk+2xRT60ha0ejvpqfRElaSywWEdKV3XRLH6alDDlc 5fPmeDGaBzN67noq0TZ8Kvn+Ou/un8n8T+V+dU3fDwpfLDuNvt+GP7+cQKhTYTn1 AoVpq/zpjTXTDoJtysmR6cRADAKTboNtvcCsk03f2BQbKLaVmcLrcvTQR+y2xRMO 96KLQY/nu/A+Be9EA09CS0m+M+LnRyAb9ImFq/nl66bhbnfJAJIQPsnsd6l1J/d1 s0A/E5g/8wulZiUc6OB2B/do9DYG2hkrR74q7oiwEfh7+UQoCVR9vMe3WsNsIhEw xSsB5MDg/gn4ON7wcprLVZKcqCVqCkhTN5m3wPnaoEUe3KykcIGTj7Z4UzmYpP2d QBPpbIFBGa74LmaQ3nWY =weIO -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgilbert/tags/pull-migration-20170213a' into staging Migration Amit: migration: remove myself as maintainer MAINTAINERS: update my email address Ashijeet: migrate: Introduce zero RAM checks to skip RAM migration Pavel: Postcopy release RAM Halil: consolidate VMStateField.start Hailiang: COLO: fix setting checkpoint-delay not working properly COLO: Shutdown related socket fd while do failover COLO: Don't process failover request while loading VM's state Me: migration: Add VMSTATE_UNUSED_VARRAY_UINT32 migration: Add VMSTATE_WITH_TMP tests/migration: Add test for VMSTATE_WITH_TMP virtio-net VMState conversion and new VMSTATE macros # gpg: Signature made Mon 13 Feb 2017 17:36:39 GMT # gpg: using RSA key 0x0516331EBC5BFDE7 # gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A 9FA9 0516 331E BC5B FDE7 * remotes/dgilbert/tags/pull-migration-20170213a: virtio/migration: Migrate virtio-net to VMState tests/migration: Add test for VMSTATE_WITH_TMP migration: Add VMSTATE_WITH_TMP migration: Add VMSTATE_UNUSED_VARRAY_UINT32 COLO: Don't process failover request while loading VM's state COLO: Shutdown related socket fd while do failover COLO: fix setting checkpoint-delay not working properly migration: consolidate VMStateField.start migrate: Introduce zero RAM checks to skip RAM migration migration: discard non-dirty ram pages after the start of postcopy add 'release-ram' migrate capability migration: add MigrationState arg for ram_save_/compressed_/page() MAINTAINERS: update my email address migration: remove myself as maintainer Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ec7a9bd5bb
|
@ -1034,7 +1034,7 @@ F: hw/input/virtio-input*.c
|
||||||
F: include/hw/virtio/virtio-input.h
|
F: include/hw/virtio/virtio-input.h
|
||||||
|
|
||||||
virtio-serial
|
virtio-serial
|
||||||
M: Amit Shah <amit.shah@redhat.com>
|
M: Amit Shah <amit@kernel.org>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/char/virtio-serial-bus.c
|
F: hw/char/virtio-serial-bus.c
|
||||||
F: hw/char/virtio-console.c
|
F: hw/char/virtio-console.c
|
||||||
|
@ -1043,7 +1043,7 @@ F: tests/virtio-console-test.c
|
||||||
F: tests/virtio-serial-test.c
|
F: tests/virtio-serial-test.c
|
||||||
|
|
||||||
virtio-rng
|
virtio-rng
|
||||||
M: Amit Shah <amit.shah@redhat.com>
|
M: Amit Shah <amit@kernel.org>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/virtio/virtio-rng.c
|
F: hw/virtio/virtio-rng.c
|
||||||
F: include/hw/virtio/virtio-rng.h
|
F: include/hw/virtio/virtio-rng.h
|
||||||
|
@ -1431,7 +1431,6 @@ F: scripts/checkpatch.pl
|
||||||
|
|
||||||
Migration
|
Migration
|
||||||
M: Juan Quintela <quintela@redhat.com>
|
M: Juan Quintela <quintela@redhat.com>
|
||||||
M: Amit Shah <amit.shah@redhat.com>
|
|
||||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: include/migration/
|
F: include/migration/
|
||||||
|
|
|
@ -561,7 +561,7 @@ static const VMStateDescription vmstate_exynos4210_uart_fifo = {
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(sp, Exynos4210UartFIFO),
|
VMSTATE_UINT32(sp, Exynos4210UartFIFO),
|
||||||
VMSTATE_UINT32(rp, Exynos4210UartFIFO),
|
VMSTATE_UINT32(rp, Exynos4210UartFIFO),
|
||||||
VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size),
|
VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -464,7 +464,7 @@ static const VMStateDescription vmstate_g364fb = {
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.post_load = g364fb_post_load,
|
.post_load = g364fb_post_load,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, 0, vram_size),
|
VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, vram_size),
|
||||||
VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
|
VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
|
||||||
VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
|
VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
|
||||||
VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
|
VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
|
||||||
|
|
|
@ -173,8 +173,8 @@ static const VMStateDescription vmstate_pl330_fifo = {
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size),
|
VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, buf_size),
|
||||||
VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size),
|
VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, buf_size),
|
||||||
VMSTATE_UINT32(head, PL330Fifo),
|
VMSTATE_UINT32(head, PL330Fifo),
|
||||||
VMSTATE_UINT32(num, PL330Fifo),
|
VMSTATE_UINT32(num, PL330Fifo),
|
||||||
VMSTATE_UINT32(buf_size, PL330Fifo),
|
VMSTATE_UINT32(buf_size, PL330Fifo),
|
||||||
|
@ -282,8 +282,8 @@ static const VMStateDescription vmstate_pl330 = {
|
||||||
VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan),
|
VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan),
|
||||||
VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0,
|
VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0,
|
||||||
vmstate_pl330_chan, PL330Chan),
|
vmstate_pl330_chan, PL330Chan),
|
||||||
VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls),
|
VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, num_chnls),
|
||||||
VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls),
|
VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, num_chnls),
|
||||||
VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo),
|
VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo),
|
||||||
VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue,
|
VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue,
|
||||||
PL330Queue),
|
PL330Queue),
|
||||||
|
|
|
@ -393,7 +393,7 @@ static const VMStateDescription vmstate_exynos4210_irq_gate = {
|
||||||
.version_id = 2,
|
.version_id = 2,
|
||||||
.minimum_version_id = 2,
|
.minimum_version_id = 2,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in),
|
VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, n_in),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -471,10 +471,8 @@ static const VMStateDescription vmstate_ISAIPMIBTDevice = {
|
||||||
VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
|
VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
|
||||||
VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
|
VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
|
||||||
VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
|
VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
|
||||||
VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0,
|
VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, bt.outlen),
|
||||||
bt.outlen),
|
VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, bt.inlen),
|
||||||
VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0,
|
|
||||||
bt.inlen),
|
|
||||||
VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
|
VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
|
||||||
VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
|
VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
|
||||||
VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
|
VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
|
||||||
|
|
|
@ -1557,119 +1557,22 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
|
||||||
virtio_net_set_queues(n);
|
virtio_net_set_queues(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f)
|
static int virtio_net_post_load_device(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
VirtIONet *n = VIRTIO_NET(vdev);
|
VirtIONet *n = opaque;
|
||||||
int i;
|
VirtIODevice *vdev = VIRTIO_DEVICE(n);
|
||||||
|
|
||||||
qemu_put_buffer(f, n->mac, ETH_ALEN);
|
|
||||||
qemu_put_be32(f, n->vqs[0].tx_waiting);
|
|
||||||
qemu_put_be32(f, n->mergeable_rx_bufs);
|
|
||||||
qemu_put_be16(f, n->status);
|
|
||||||
qemu_put_byte(f, n->promisc);
|
|
||||||
qemu_put_byte(f, n->allmulti);
|
|
||||||
qemu_put_be32(f, n->mac_table.in_use);
|
|
||||||
qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
|
|
||||||
qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
|
|
||||||
qemu_put_be32(f, n->has_vnet_hdr);
|
|
||||||
qemu_put_byte(f, n->mac_table.multi_overflow);
|
|
||||||
qemu_put_byte(f, n->mac_table.uni_overflow);
|
|
||||||
qemu_put_byte(f, n->alluni);
|
|
||||||
qemu_put_byte(f, n->nomulti);
|
|
||||||
qemu_put_byte(f, n->nouni);
|
|
||||||
qemu_put_byte(f, n->nobcast);
|
|
||||||
qemu_put_byte(f, n->has_ufo);
|
|
||||||
if (n->max_queues > 1) {
|
|
||||||
qemu_put_be16(f, n->max_queues);
|
|
||||||
qemu_put_be16(f, n->curr_queues);
|
|
||||||
for (i = 1; i < n->curr_queues; i++) {
|
|
||||||
qemu_put_be32(f, n->vqs[i].tx_waiting);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
|
|
||||||
qemu_put_be64(f, n->curr_guest_offloads);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
|
|
||||||
int version_id)
|
|
||||||
{
|
|
||||||
VirtIONet *n = VIRTIO_NET(vdev);
|
|
||||||
int i, link_down;
|
int i, link_down;
|
||||||
|
|
||||||
qemu_get_buffer(f, n->mac, ETH_ALEN);
|
virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
|
||||||
n->vqs[0].tx_waiting = qemu_get_be32(f);
|
|
||||||
|
|
||||||
virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f),
|
|
||||||
virtio_vdev_has_feature(vdev,
|
virtio_vdev_has_feature(vdev,
|
||||||
VIRTIO_F_VERSION_1));
|
VIRTIO_F_VERSION_1));
|
||||||
|
|
||||||
n->status = qemu_get_be16(f);
|
|
||||||
|
|
||||||
n->promisc = qemu_get_byte(f);
|
|
||||||
n->allmulti = qemu_get_byte(f);
|
|
||||||
|
|
||||||
n->mac_table.in_use = qemu_get_be32(f);
|
|
||||||
/* MAC_TABLE_ENTRIES may be different from the saved image */
|
/* MAC_TABLE_ENTRIES may be different from the saved image */
|
||||||
if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) {
|
if (n->mac_table.in_use > MAC_TABLE_ENTRIES) {
|
||||||
qemu_get_buffer(f, n->mac_table.macs,
|
|
||||||
n->mac_table.in_use * ETH_ALEN);
|
|
||||||
} else {
|
|
||||||
int64_t i;
|
|
||||||
|
|
||||||
/* Overflow detected - can happen if source has a larger MAC table.
|
|
||||||
* We simply set overflow flag so there's no need to maintain the
|
|
||||||
* table of addresses, discard them all.
|
|
||||||
* Note: 64 bit math to avoid integer overflow.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < (int64_t)n->mac_table.in_use * ETH_ALEN; ++i) {
|
|
||||||
qemu_get_byte(f);
|
|
||||||
}
|
|
||||||
n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1;
|
|
||||||
n->mac_table.in_use = 0;
|
n->mac_table.in_use = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
|
|
||||||
|
|
||||||
if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) {
|
if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
|
||||||
error_report("virtio-net: saved image requires vnet_hdr=on");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->mac_table.multi_overflow = qemu_get_byte(f);
|
|
||||||
n->mac_table.uni_overflow = qemu_get_byte(f);
|
|
||||||
|
|
||||||
n->alluni = qemu_get_byte(f);
|
|
||||||
n->nomulti = qemu_get_byte(f);
|
|
||||||
n->nouni = qemu_get_byte(f);
|
|
||||||
n->nobcast = qemu_get_byte(f);
|
|
||||||
|
|
||||||
if (qemu_get_byte(f) && !peer_has_ufo(n)) {
|
|
||||||
error_report("virtio-net: saved image requires TUN_F_UFO support");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n->max_queues > 1) {
|
|
||||||
if (n->max_queues != qemu_get_be16(f)) {
|
|
||||||
error_report("virtio-net: different max_queues ");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->curr_queues = qemu_get_be16(f);
|
|
||||||
if (n->curr_queues > n->max_queues) {
|
|
||||||
error_report("virtio-net: curr_queues %x > max_queues %x",
|
|
||||||
n->curr_queues, n->max_queues);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for (i = 1; i < n->curr_queues; i++) {
|
|
||||||
n->vqs[i].tx_waiting = qemu_get_be32(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
|
|
||||||
n->curr_guest_offloads = qemu_get_be64(f);
|
|
||||||
} else {
|
|
||||||
n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
|
n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1703,6 +1606,210 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* tx_waiting field of a VirtIONetQueue */
|
||||||
|
static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
|
||||||
|
.name = "virtio-net-queue-tx_waiting",
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(tx_waiting, VirtIONetQueue),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool max_queues_gt_1(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
return VIRTIO_NET(opaque)->max_queues > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_ctrl_guest_offloads(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
return virtio_vdev_has_feature(VIRTIO_DEVICE(opaque),
|
||||||
|
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mac_table_fits(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
return VIRTIO_NET(opaque)->mac_table.in_use <= MAC_TABLE_ENTRIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mac_table_doesnt_fit(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
return !mac_table_fits(opaque, version_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This temporary type is shared by all the WITH_TMP methods
|
||||||
|
* although only some fields are used by each.
|
||||||
|
*/
|
||||||
|
struct VirtIONetMigTmp {
|
||||||
|
VirtIONet *parent;
|
||||||
|
VirtIONetQueue *vqs_1;
|
||||||
|
uint16_t curr_queues_1;
|
||||||
|
uint8_t has_ufo;
|
||||||
|
uint32_t has_vnet_hdr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The 2nd and subsequent tx_waiting flags are loaded later than
|
||||||
|
* the 1st entry in the queues and only if there's more than one
|
||||||
|
* entry. We use the tmp mechanism to calculate a temporary
|
||||||
|
* pointer and count and also validate the count.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void virtio_net_tx_waiting_pre_save(void *opaque)
|
||||||
|
{
|
||||||
|
struct VirtIONetMigTmp *tmp = opaque;
|
||||||
|
|
||||||
|
tmp->vqs_1 = tmp->parent->vqs + 1;
|
||||||
|
tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
|
||||||
|
if (tmp->parent->curr_queues == 0) {
|
||||||
|
tmp->curr_queues_1 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_net_tx_waiting_pre_load(void *opaque)
|
||||||
|
{
|
||||||
|
struct VirtIONetMigTmp *tmp = opaque;
|
||||||
|
|
||||||
|
/* Reuse the pointer setup from save */
|
||||||
|
virtio_net_tx_waiting_pre_save(opaque);
|
||||||
|
|
||||||
|
if (tmp->parent->curr_queues > tmp->parent->max_queues) {
|
||||||
|
error_report("virtio-net: curr_queues %x > max_queues %x",
|
||||||
|
tmp->parent->curr_queues, tmp->parent->max_queues);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; /* all good */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_net_tx_waiting = {
|
||||||
|
.name = "virtio-net-tx_waiting",
|
||||||
|
.pre_load = virtio_net_tx_waiting_pre_load,
|
||||||
|
.pre_save = virtio_net_tx_waiting_pre_save,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
|
||||||
|
curr_queues_1,
|
||||||
|
vmstate_virtio_net_queue_tx_waiting,
|
||||||
|
struct VirtIONetQueue),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* the 'has_ufo' flag is just tested; if the incoming stream has the
|
||||||
|
* flag set we need to check that we have it
|
||||||
|
*/
|
||||||
|
static int virtio_net_ufo_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
struct VirtIONetMigTmp *tmp = opaque;
|
||||||
|
|
||||||
|
if (tmp->has_ufo && !peer_has_ufo(tmp->parent)) {
|
||||||
|
error_report("virtio-net: saved image requires TUN_F_UFO support");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_net_ufo_pre_save(void *opaque)
|
||||||
|
{
|
||||||
|
struct VirtIONetMigTmp *tmp = opaque;
|
||||||
|
|
||||||
|
tmp->has_ufo = tmp->parent->has_ufo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_net_has_ufo = {
|
||||||
|
.name = "virtio-net-ufo",
|
||||||
|
.post_load = virtio_net_ufo_post_load,
|
||||||
|
.pre_save = virtio_net_ufo_pre_save,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT8(has_ufo, struct VirtIONetMigTmp),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* the 'has_vnet_hdr' flag is just tested; if the incoming stream has the
|
||||||
|
* flag set we need to check that we have it
|
||||||
|
*/
|
||||||
|
static int virtio_net_vnet_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
struct VirtIONetMigTmp *tmp = opaque;
|
||||||
|
|
||||||
|
if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
|
||||||
|
error_report("virtio-net: saved image requires vnet_hdr=on");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_net_vnet_pre_save(void *opaque)
|
||||||
|
{
|
||||||
|
struct VirtIONetMigTmp *tmp = opaque;
|
||||||
|
|
||||||
|
tmp->has_vnet_hdr = tmp->parent->has_vnet_hdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_net_has_vnet = {
|
||||||
|
.name = "virtio-net-vnet",
|
||||||
|
.post_load = virtio_net_vnet_post_load,
|
||||||
|
.pre_save = virtio_net_vnet_pre_save,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(has_vnet_hdr, struct VirtIONetMigTmp),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_net_device = {
|
||||||
|
.name = "virtio-net-device",
|
||||||
|
.version_id = VIRTIO_NET_VM_VERSION,
|
||||||
|
.minimum_version_id = VIRTIO_NET_VM_VERSION,
|
||||||
|
.post_load = virtio_net_post_load_device,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT8_ARRAY(mac, VirtIONet, ETH_ALEN),
|
||||||
|
VMSTATE_STRUCT_POINTER(vqs, VirtIONet,
|
||||||
|
vmstate_virtio_net_queue_tx_waiting,
|
||||||
|
VirtIONetQueue),
|
||||||
|
VMSTATE_UINT32(mergeable_rx_bufs, VirtIONet),
|
||||||
|
VMSTATE_UINT16(status, VirtIONet),
|
||||||
|
VMSTATE_UINT8(promisc, VirtIONet),
|
||||||
|
VMSTATE_UINT8(allmulti, VirtIONet),
|
||||||
|
VMSTATE_UINT32(mac_table.in_use, VirtIONet),
|
||||||
|
|
||||||
|
/* Guarded pair: If it fits we load it, else we throw it away
|
||||||
|
* - can happen if source has a larger MAC table.; post-load
|
||||||
|
* sets flags in this case.
|
||||||
|
*/
|
||||||
|
VMSTATE_VBUFFER_MULTIPLY(mac_table.macs, VirtIONet,
|
||||||
|
0, mac_table_fits, mac_table.in_use,
|
||||||
|
ETH_ALEN),
|
||||||
|
VMSTATE_UNUSED_VARRAY_UINT32(VirtIONet, mac_table_doesnt_fit, 0,
|
||||||
|
mac_table.in_use, ETH_ALEN),
|
||||||
|
|
||||||
|
/* Note: This is an array of uint32's that's always been saved as a
|
||||||
|
* buffer; hold onto your endiannesses; it's actually used as a bitmap
|
||||||
|
* but based on the uint.
|
||||||
|
*/
|
||||||
|
VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
|
||||||
|
VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
|
||||||
|
vmstate_virtio_net_has_vnet),
|
||||||
|
VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
|
||||||
|
VMSTATE_UINT8(mac_table.uni_overflow, VirtIONet),
|
||||||
|
VMSTATE_UINT8(alluni, VirtIONet),
|
||||||
|
VMSTATE_UINT8(nomulti, VirtIONet),
|
||||||
|
VMSTATE_UINT8(nouni, VirtIONet),
|
||||||
|
VMSTATE_UINT8(nobcast, VirtIONet),
|
||||||
|
VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
|
||||||
|
vmstate_virtio_net_has_ufo),
|
||||||
|
VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
|
||||||
|
vmstate_info_uint16_equal, uint16_t),
|
||||||
|
VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
|
||||||
|
VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
|
||||||
|
vmstate_virtio_net_tx_waiting),
|
||||||
|
VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
|
||||||
|
has_ctrl_guest_offloads),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static NetClientInfo net_virtio_info = {
|
static NetClientInfo net_virtio_info = {
|
||||||
.type = NET_CLIENT_DRIVER_NIC,
|
.type = NET_CLIENT_DRIVER_NIC,
|
||||||
.size = sizeof(NICState),
|
.size = sizeof(NICState),
|
||||||
|
@ -1989,9 +2096,8 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
|
||||||
vdc->set_status = virtio_net_set_status;
|
vdc->set_status = virtio_net_set_status;
|
||||||
vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
|
vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
|
||||||
vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
|
vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
|
||||||
vdc->load = virtio_net_load_device;
|
|
||||||
vdc->save = virtio_net_save_device;
|
|
||||||
vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
|
vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO);
|
||||||
|
vdc->vmsd = &vmstate_virtio_net_device;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo virtio_net_info = {
|
static const TypeInfo virtio_net_info = {
|
||||||
|
|
|
@ -2397,7 +2397,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = {
|
||||||
.pre_load = vmxnet3_mcast_list_pre_load,
|
.pre_load = vmxnet3_mcast_list_pre_load,
|
||||||
.needed = vmxnet3_mc_list_needed,
|
.needed = vmxnet3_mc_list_needed,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0,
|
VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL,
|
||||||
mcast_list_buff_size),
|
mcast_list_buff_size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ static const VMStateDescription vmstate_macio_nvram = {
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, 0, size),
|
VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -224,7 +224,7 @@ static const VMStateDescription vmstate_spapr_nvram = {
|
||||||
.post_load = spapr_nvram_post_load,
|
.post_load = spapr_nvram_post_load,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(size, sPAPRNVRAM),
|
VMSTATE_UINT32(size, sPAPRNVRAM),
|
||||||
VMSTATE_VBUFFER_ALLOC_UINT32(buf, sPAPRNVRAM, 1, NULL, 0, size),
|
VMSTATE_VBUFFER_ALLOC_UINT32(buf, sPAPRNVRAM, 1, NULL, size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1253,7 +1253,7 @@ const VMStateDescription sdhci_vmstate = {
|
||||||
VMSTATE_UINT16(data_count, SDHCIState),
|
VMSTATE_UINT16(data_count, SDHCIState),
|
||||||
VMSTATE_UINT64(admasysaddr, SDHCIState),
|
VMSTATE_UINT64(admasysaddr, SDHCIState),
|
||||||
VMSTATE_UINT8(stopped_state, SDHCIState),
|
VMSTATE_UINT8(stopped_state, SDHCIState),
|
||||||
VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz),
|
VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, buf_maxsz),
|
||||||
VMSTATE_TIMER_PTR(insert_timer, SDHCIState),
|
VMSTATE_TIMER_PTR(insert_timer, SDHCIState),
|
||||||
VMSTATE_TIMER_PTR(transfer_timer, SDHCIState),
|
VMSTATE_TIMER_PTR(transfer_timer, SDHCIState),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
|
|
|
@ -563,7 +563,7 @@ static const VMStateDescription vmstate_m48t59 = {
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT8(lock, M48t59State),
|
VMSTATE_UINT8(lock, M48t59State),
|
||||||
VMSTATE_UINT16(addr, M48t59State),
|
VMSTATE_UINT16(addr, M48t59State),
|
||||||
VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, 0, size),
|
VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,7 +47,7 @@ typedef struct VirtIONetQueue {
|
||||||
VirtQueue *tx_vq;
|
VirtQueue *tx_vq;
|
||||||
QEMUTimer *tx_timer;
|
QEMUTimer *tx_timer;
|
||||||
QEMUBH *tx_bh;
|
QEMUBH *tx_bh;
|
||||||
int tx_waiting;
|
uint32_t tx_waiting;
|
||||||
struct {
|
struct {
|
||||||
VirtQueueElement *elem;
|
VirtQueueElement *elem;
|
||||||
} async_tx;
|
} async_tx;
|
||||||
|
@ -68,7 +68,7 @@ typedef struct VirtIONet {
|
||||||
size_t guest_hdr_len;
|
size_t guest_hdr_len;
|
||||||
uint32_t host_features;
|
uint32_t host_features;
|
||||||
uint8_t has_ufo;
|
uint8_t has_ufo;
|
||||||
int mergeable_rx_bufs;
|
uint32_t mergeable_rx_bufs;
|
||||||
uint8_t promisc;
|
uint8_t promisc;
|
||||||
uint8_t allmulti;
|
uint8_t allmulti;
|
||||||
uint8_t alluni;
|
uint8_t alluni;
|
||||||
|
|
|
@ -35,4 +35,6 @@ COLOMode get_colo_mode(void);
|
||||||
|
|
||||||
/* failover */
|
/* failover */
|
||||||
void colo_do_failover(MigrationState *s);
|
void colo_do_failover(MigrationState *s);
|
||||||
|
|
||||||
|
void colo_checkpoint_notify(void *opaque);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -116,6 +116,7 @@ struct MigrationIncomingState {
|
||||||
QemuThread colo_incoming_thread;
|
QemuThread colo_incoming_thread;
|
||||||
/* The coroutine we should enter (back) after failover */
|
/* The coroutine we should enter (back) after failover */
|
||||||
Coroutine *migration_incoming_co;
|
Coroutine *migration_incoming_co;
|
||||||
|
QemuSemaphore colo_incoming_sem;
|
||||||
|
|
||||||
/* See savevm.c */
|
/* See savevm.c */
|
||||||
LoadStateEntry_Head loadvm_handlers;
|
LoadStateEntry_Head loadvm_handlers;
|
||||||
|
@ -187,6 +188,13 @@ struct MigrationState
|
||||||
QSIMPLEQ_HEAD(src_page_requests, MigrationSrcPageRequest) src_page_requests;
|
QSIMPLEQ_HEAD(src_page_requests, MigrationSrcPageRequest) src_page_requests;
|
||||||
/* The RAMBlock used in the last src_page_request */
|
/* The RAMBlock used in the last src_page_request */
|
||||||
RAMBlock *last_req_rb;
|
RAMBlock *last_req_rb;
|
||||||
|
/* The semaphore is used to notify COLO thread that failover is finished */
|
||||||
|
QemuSemaphore colo_exit_sem;
|
||||||
|
|
||||||
|
/* The semaphore is used to notify COLO thread to do checkpoint */
|
||||||
|
QemuSemaphore colo_checkpoint_sem;
|
||||||
|
int64_t colo_checkpoint_time;
|
||||||
|
QEMUTimer *colo_delay_timer;
|
||||||
|
|
||||||
/* The last error that occurred */
|
/* The last error that occurred */
|
||||||
Error *error;
|
Error *error;
|
||||||
|
@ -285,6 +293,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms);
|
||||||
int ram_discard_range(MigrationIncomingState *mis, const char *block_name,
|
int ram_discard_range(MigrationIncomingState *mis, const char *block_name,
|
||||||
uint64_t start, size_t length);
|
uint64_t start, size_t length);
|
||||||
int ram_postcopy_incoming_init(MigrationIncomingState *mis);
|
int ram_postcopy_incoming_init(MigrationIncomingState *mis);
|
||||||
|
void ram_postcopy_migrated_memory_release(MigrationState *ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @migrate_add_blocker - prevent migration from proceeding
|
* @migrate_add_blocker - prevent migration from proceeding
|
||||||
|
@ -304,6 +313,7 @@ int migrate_add_blocker(Error *reason, Error **errp);
|
||||||
*/
|
*/
|
||||||
void migrate_del_blocker(Error *reason);
|
void migrate_del_blocker(Error *reason);
|
||||||
|
|
||||||
|
bool migrate_release_ram(void);
|
||||||
bool migrate_postcopy_ram(void);
|
bool migrate_postcopy_ram(void);
|
||||||
bool migrate_zero_blocks(void);
|
bool migrate_zero_blocks(void);
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,8 @@ void qemu_put_byte(QEMUFile *f, int v);
|
||||||
* put_buffer without copying the buffer.
|
* put_buffer without copying the buffer.
|
||||||
* The buffer should be available till it is sent asynchronously.
|
* The buffer should be available till it is sent asynchronously.
|
||||||
*/
|
*/
|
||||||
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size);
|
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size,
|
||||||
|
bool may_free);
|
||||||
bool qemu_file_mode_is_not_valid(const char *mode);
|
bool qemu_file_mode_is_not_valid(const char *mode);
|
||||||
bool qemu_file_is_writable(QEMUFile *f);
|
bool qemu_file_is_writable(QEMUFile *f);
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,7 @@ extern const VMStateInfo vmstate_info_cpudouble;
|
||||||
extern const VMStateInfo vmstate_info_timer;
|
extern const VMStateInfo vmstate_info_timer;
|
||||||
extern const VMStateInfo vmstate_info_buffer;
|
extern const VMStateInfo vmstate_info_buffer;
|
||||||
extern const VMStateInfo vmstate_info_unused_buffer;
|
extern const VMStateInfo vmstate_info_unused_buffer;
|
||||||
|
extern const VMStateInfo vmstate_info_tmp;
|
||||||
extern const VMStateInfo vmstate_info_bitmap;
|
extern const VMStateInfo vmstate_info_bitmap;
|
||||||
extern const VMStateInfo vmstate_info_qtailq;
|
extern const VMStateInfo vmstate_info_qtailq;
|
||||||
|
|
||||||
|
@ -587,7 +588,8 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.offset = vmstate_offset_buffer(_state, _field) + _start, \
|
.offset = vmstate_offset_buffer(_state, _field) + _start, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VMSTATE_VBUFFER_MULTIPLY(_field, _state, _version, _test, _start, _field_size, _multiply) { \
|
#define VMSTATE_VBUFFER_MULTIPLY(_field, _state, _version, _test, \
|
||||||
|
_field_size, _multiply) { \
|
||||||
.name = (stringify(_field)), \
|
.name = (stringify(_field)), \
|
||||||
.version_id = (_version), \
|
.version_id = (_version), \
|
||||||
.field_exists = (_test), \
|
.field_exists = (_test), \
|
||||||
|
@ -596,10 +598,9 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.info = &vmstate_info_buffer, \
|
.info = &vmstate_info_buffer, \
|
||||||
.flags = VMS_VBUFFER|VMS_POINTER|VMS_MULTIPLY, \
|
.flags = VMS_VBUFFER|VMS_POINTER|VMS_MULTIPLY, \
|
||||||
.offset = offsetof(_state, _field), \
|
.offset = offsetof(_state, _field), \
|
||||||
.start = (_start), \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VMSTATE_VBUFFER(_field, _state, _version, _test, _start, _field_size) { \
|
#define VMSTATE_VBUFFER(_field, _state, _version, _test, _field_size) { \
|
||||||
.name = (stringify(_field)), \
|
.name = (stringify(_field)), \
|
||||||
.version_id = (_version), \
|
.version_id = (_version), \
|
||||||
.field_exists = (_test), \
|
.field_exists = (_test), \
|
||||||
|
@ -607,10 +608,9 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.info = &vmstate_info_buffer, \
|
.info = &vmstate_info_buffer, \
|
||||||
.flags = VMS_VBUFFER|VMS_POINTER, \
|
.flags = VMS_VBUFFER|VMS_POINTER, \
|
||||||
.offset = offsetof(_state, _field), \
|
.offset = offsetof(_state, _field), \
|
||||||
.start = (_start), \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \
|
#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _field_size) { \
|
||||||
.name = (stringify(_field)), \
|
.name = (stringify(_field)), \
|
||||||
.version_id = (_version), \
|
.version_id = (_version), \
|
||||||
.field_exists = (_test), \
|
.field_exists = (_test), \
|
||||||
|
@ -618,10 +618,10 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.info = &vmstate_info_buffer, \
|
.info = &vmstate_info_buffer, \
|
||||||
.flags = VMS_VBUFFER|VMS_POINTER, \
|
.flags = VMS_VBUFFER|VMS_POINTER, \
|
||||||
.offset = offsetof(_state, _field), \
|
.offset = offsetof(_state, _field), \
|
||||||
.start = (_start), \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VMSTATE_VBUFFER_ALLOC_UINT32(_field, _state, _version, _test, _start, _field_size) { \
|
#define VMSTATE_VBUFFER_ALLOC_UINT32(_field, _state, _version, \
|
||||||
|
_test, _field_size) { \
|
||||||
.name = (stringify(_field)), \
|
.name = (stringify(_field)), \
|
||||||
.version_id = (_version), \
|
.version_id = (_version), \
|
||||||
.field_exists = (_test), \
|
.field_exists = (_test), \
|
||||||
|
@ -629,7 +629,6 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.info = &vmstate_info_buffer, \
|
.info = &vmstate_info_buffer, \
|
||||||
.flags = VMS_VBUFFER|VMS_POINTER|VMS_ALLOC, \
|
.flags = VMS_VBUFFER|VMS_POINTER|VMS_ALLOC, \
|
||||||
.offset = offsetof(_state, _field), \
|
.offset = offsetof(_state, _field), \
|
||||||
.start = (_start), \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VMSTATE_BUFFER_UNSAFE_INFO_TEST(_field, _state, _test, _version, _info, _size) { \
|
#define VMSTATE_BUFFER_UNSAFE_INFO_TEST(_field, _state, _test, _version, _info, _size) { \
|
||||||
|
@ -651,6 +650,24 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.offset = offsetof(_state, _field), \
|
.offset = offsetof(_state, _field), \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Allocate a temporary of type 'tmp_type', set tmp->parent to _state
|
||||||
|
* and execute the vmsd on the temporary. Note that we're working with
|
||||||
|
* the whole of _state here, not a field within it.
|
||||||
|
* We compile time check that:
|
||||||
|
* That _tmp_type contains a 'parent' member that's a pointer to the
|
||||||
|
* '_state' type
|
||||||
|
* That the pointer is right at the start of _tmp_type.
|
||||||
|
*/
|
||||||
|
#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) { \
|
||||||
|
.name = "tmp", \
|
||||||
|
.size = sizeof(_tmp_type) + \
|
||||||
|
QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \
|
||||||
|
type_check_pointer(_state, \
|
||||||
|
typeof_field(_tmp_type, parent)), \
|
||||||
|
.vmsd = &(_vmsd), \
|
||||||
|
.info = &vmstate_info_tmp, \
|
||||||
|
}
|
||||||
|
|
||||||
#define VMSTATE_UNUSED_BUFFER(_test, _version, _size) { \
|
#define VMSTATE_UNUSED_BUFFER(_test, _version, _size) { \
|
||||||
.name = "unused", \
|
.name = "unused", \
|
||||||
.field_exists = (_test), \
|
.field_exists = (_test), \
|
||||||
|
@ -660,6 +677,17 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
.flags = VMS_BUFFER, \
|
.flags = VMS_BUFFER, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Discard size * field_num bytes, where field_num is a uint32 member */
|
||||||
|
#define VMSTATE_UNUSED_VARRAY_UINT32(_state, _test, _version, _field_num, _size) {\
|
||||||
|
.name = "unused", \
|
||||||
|
.field_exists = (_test), \
|
||||||
|
.num_offset = vmstate_offset_value(_state, _field_num, uint32_t),\
|
||||||
|
.version_id = (_version), \
|
||||||
|
.size = (_size), \
|
||||||
|
.info = &vmstate_info_unused_buffer, \
|
||||||
|
.flags = VMS_VARRAY_UINT32 | VMS_BUFFER, \
|
||||||
|
}
|
||||||
|
|
||||||
/* _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.
|
||||||
*/
|
*/
|
||||||
|
@ -948,13 +976,10 @@ extern const VMStateInfo vmstate_info_qtailq;
|
||||||
VMSTATE_BUFFER_START_MIDDLE_V(_f, _s, _start, 0)
|
VMSTATE_BUFFER_START_MIDDLE_V(_f, _s, _start, 0)
|
||||||
|
|
||||||
#define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
|
#define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
|
||||||
VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
|
VMSTATE_VBUFFER(_f, _s, 0, NULL, _size)
|
||||||
|
|
||||||
#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \
|
#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \
|
||||||
VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size)
|
VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, _size)
|
||||||
|
|
||||||
#define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \
|
|
||||||
VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size)
|
|
||||||
|
|
||||||
#define VMSTATE_BUFFER_TEST(_f, _s, _test) \
|
#define VMSTATE_BUFFER_TEST(_f, _s, _test) \
|
||||||
VMSTATE_STATIC_BUFFER(_f, _s, 0, _test, 0, sizeof(typeof_field(_s, _f)))
|
VMSTATE_STATIC_BUFFER(_f, _s, 0, _test, 0, sizeof(typeof_field(_s, _f)))
|
||||||
|
|
102
migration/colo.c
102
migration/colo.c
|
@ -20,6 +20,8 @@
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "migration/failover.h"
|
#include "migration/failover.h"
|
||||||
|
|
||||||
|
static bool vmstate_loading;
|
||||||
|
|
||||||
#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024)
|
#define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024)
|
||||||
|
|
||||||
bool colo_supported(void)
|
bool colo_supported(void)
|
||||||
|
@ -51,6 +53,19 @@ static void secondary_vm_do_failover(void)
|
||||||
int old_state;
|
int old_state;
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
|
|
||||||
|
/* Can not do failover during the process of VM's loading VMstate, Or
|
||||||
|
* it will break the secondary VM.
|
||||||
|
*/
|
||||||
|
if (vmstate_loading) {
|
||||||
|
old_state = failover_set_state(FAILOVER_STATUS_ACTIVE,
|
||||||
|
FAILOVER_STATUS_RELAUNCH);
|
||||||
|
if (old_state != FAILOVER_STATUS_ACTIVE) {
|
||||||
|
error_report("Unknown error while do failover for secondary VM,"
|
||||||
|
"old_state: %s", FailoverStatus_lookup[old_state]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
migrate_set_state(&mis->state, MIGRATION_STATUS_COLO,
|
migrate_set_state(&mis->state, MIGRATION_STATUS_COLO,
|
||||||
MIGRATION_STATUS_COMPLETED);
|
MIGRATION_STATUS_COMPLETED);
|
||||||
|
|
||||||
|
@ -59,6 +74,18 @@ static void secondary_vm_do_failover(void)
|
||||||
/* recover runstate to normal migration finish state */
|
/* recover runstate to normal migration finish state */
|
||||||
autostart = true;
|
autostart = true;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Make sure COLO incoming thread not block in recv or send,
|
||||||
|
* If mis->from_src_file and mis->to_src_file use the same fd,
|
||||||
|
* The second shutdown() will return -1, we ignore this value,
|
||||||
|
* It is harmless.
|
||||||
|
*/
|
||||||
|
if (mis->from_src_file) {
|
||||||
|
qemu_file_shutdown(mis->from_src_file);
|
||||||
|
}
|
||||||
|
if (mis->to_src_file) {
|
||||||
|
qemu_file_shutdown(mis->to_src_file);
|
||||||
|
}
|
||||||
|
|
||||||
old_state = failover_set_state(FAILOVER_STATUS_ACTIVE,
|
old_state = failover_set_state(FAILOVER_STATUS_ACTIVE,
|
||||||
FAILOVER_STATUS_COMPLETED);
|
FAILOVER_STATUS_COMPLETED);
|
||||||
|
@ -67,6 +94,8 @@ static void secondary_vm_do_failover(void)
|
||||||
"secondary VM", FailoverStatus_lookup[old_state]);
|
"secondary VM", FailoverStatus_lookup[old_state]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* Notify COLO incoming thread that failover work is finished */
|
||||||
|
qemu_sem_post(&mis->colo_incoming_sem);
|
||||||
/* For Secondary VM, jump to incoming co */
|
/* For Secondary VM, jump to incoming co */
|
||||||
if (mis->migration_incoming_co) {
|
if (mis->migration_incoming_co) {
|
||||||
qemu_coroutine_enter(mis->migration_incoming_co);
|
qemu_coroutine_enter(mis->migration_incoming_co);
|
||||||
|
@ -81,6 +110,18 @@ static void primary_vm_do_failover(void)
|
||||||
migrate_set_state(&s->state, MIGRATION_STATUS_COLO,
|
migrate_set_state(&s->state, MIGRATION_STATUS_COLO,
|
||||||
MIGRATION_STATUS_COMPLETED);
|
MIGRATION_STATUS_COMPLETED);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wake up COLO thread which may blocked in recv() or send(),
|
||||||
|
* The s->rp_state.from_dst_file and s->to_dst_file may use the
|
||||||
|
* same fd, but we still shutdown the fd for twice, it is harmless.
|
||||||
|
*/
|
||||||
|
if (s->to_dst_file) {
|
||||||
|
qemu_file_shutdown(s->to_dst_file);
|
||||||
|
}
|
||||||
|
if (s->rp_state.from_dst_file) {
|
||||||
|
qemu_file_shutdown(s->rp_state.from_dst_file);
|
||||||
|
}
|
||||||
|
|
||||||
old_state = failover_set_state(FAILOVER_STATUS_ACTIVE,
|
old_state = failover_set_state(FAILOVER_STATUS_ACTIVE,
|
||||||
FAILOVER_STATUS_COMPLETED);
|
FAILOVER_STATUS_COMPLETED);
|
||||||
if (old_state != FAILOVER_STATUS_ACTIVE) {
|
if (old_state != FAILOVER_STATUS_ACTIVE) {
|
||||||
|
@ -88,6 +129,8 @@ static void primary_vm_do_failover(void)
|
||||||
FailoverStatus_lookup[old_state]);
|
FailoverStatus_lookup[old_state]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* Notify COLO thread that failover work is finished */
|
||||||
|
qemu_sem_post(&s->colo_exit_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void colo_do_failover(MigrationState *s)
|
void colo_do_failover(MigrationState *s)
|
||||||
|
@ -302,7 +345,7 @@ static void colo_process_checkpoint(MigrationState *s)
|
||||||
{
|
{
|
||||||
QIOChannelBuffer *bioc;
|
QIOChannelBuffer *bioc;
|
||||||
QEMUFile *fb = NULL;
|
QEMUFile *fb = NULL;
|
||||||
int64_t current_time, checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
int64_t current_time = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -332,26 +375,21 @@ static void colo_process_checkpoint(MigrationState *s)
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
trace_colo_vm_state_change("stop", "run");
|
trace_colo_vm_state_change("stop", "run");
|
||||||
|
|
||||||
|
timer_mod(s->colo_delay_timer,
|
||||||
|
current_time + s->parameters.x_checkpoint_delay);
|
||||||
|
|
||||||
while (s->state == MIGRATION_STATUS_COLO) {
|
while (s->state == MIGRATION_STATUS_COLO) {
|
||||||
if (failover_get_state() != FAILOVER_STATUS_NONE) {
|
if (failover_get_state() != FAILOVER_STATUS_NONE) {
|
||||||
error_report("failover request");
|
error_report("failover request");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_time = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
qemu_sem_wait(&s->colo_checkpoint_sem);
|
||||||
if (current_time - checkpoint_time <
|
|
||||||
s->parameters.x_checkpoint_delay) {
|
|
||||||
int64_t delay_ms;
|
|
||||||
|
|
||||||
delay_ms = s->parameters.x_checkpoint_delay -
|
|
||||||
(current_time - checkpoint_time);
|
|
||||||
g_usleep(delay_ms * 1000);
|
|
||||||
}
|
|
||||||
ret = colo_do_checkpoint_transaction(s, bioc, fb);
|
ret = colo_do_checkpoint_transaction(s, bioc, fb);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -364,14 +402,41 @@ out:
|
||||||
qemu_fclose(fb);
|
qemu_fclose(fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timer_del(s->colo_delay_timer);
|
||||||
|
|
||||||
|
/* Hope this not to be too long to wait here */
|
||||||
|
qemu_sem_wait(&s->colo_exit_sem);
|
||||||
|
qemu_sem_destroy(&s->colo_exit_sem);
|
||||||
|
/*
|
||||||
|
* Must be called after failover BH is completed,
|
||||||
|
* Or the failover BH may shutdown the wrong fd that
|
||||||
|
* re-used by other threads after we release here.
|
||||||
|
*/
|
||||||
if (s->rp_state.from_dst_file) {
|
if (s->rp_state.from_dst_file) {
|
||||||
qemu_fclose(s->rp_state.from_dst_file);
|
qemu_fclose(s->rp_state.from_dst_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void colo_checkpoint_notify(void *opaque)
|
||||||
|
{
|
||||||
|
MigrationState *s = opaque;
|
||||||
|
int64_t next_notify_time;
|
||||||
|
|
||||||
|
qemu_sem_post(&s->colo_checkpoint_sem);
|
||||||
|
s->colo_checkpoint_time = qemu_clock_get_ms(QEMU_CLOCK_HOST);
|
||||||
|
next_notify_time = s->colo_checkpoint_time +
|
||||||
|
s->parameters.x_checkpoint_delay;
|
||||||
|
timer_mod(s->colo_delay_timer, next_notify_time);
|
||||||
|
}
|
||||||
|
|
||||||
void migrate_start_colo_process(MigrationState *s)
|
void migrate_start_colo_process(MigrationState *s)
|
||||||
{
|
{
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
qemu_sem_init(&s->colo_checkpoint_sem, 0);
|
||||||
|
s->colo_delay_timer = timer_new_ms(QEMU_CLOCK_HOST,
|
||||||
|
colo_checkpoint_notify, s);
|
||||||
|
|
||||||
|
qemu_sem_init(&s->colo_exit_sem, 0);
|
||||||
migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
|
migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
|
||||||
MIGRATION_STATUS_COLO);
|
MIGRATION_STATUS_COLO);
|
||||||
colo_process_checkpoint(s);
|
colo_process_checkpoint(s);
|
||||||
|
@ -410,6 +475,8 @@ void *colo_process_incoming_thread(void *opaque)
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
qemu_sem_init(&mis->colo_incoming_sem, 0);
|
||||||
|
|
||||||
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
||||||
MIGRATION_STATUS_COLO);
|
MIGRATION_STATUS_COLO);
|
||||||
|
|
||||||
|
@ -496,13 +563,23 @@ void *colo_process_incoming_thread(void *opaque)
|
||||||
|
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
qemu_system_reset(VMRESET_SILENT);
|
qemu_system_reset(VMRESET_SILENT);
|
||||||
|
vmstate_loading = true;
|
||||||
if (qemu_loadvm_state(fb) < 0) {
|
if (qemu_loadvm_state(fb) < 0) {
|
||||||
error_report("COLO: loadvm failed");
|
error_report("COLO: loadvm failed");
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vmstate_loading = false;
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
|
|
||||||
|
if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) {
|
||||||
|
failover_set_state(FAILOVER_STATUS_RELAUNCH,
|
||||||
|
FAILOVER_STATUS_NONE);
|
||||||
|
failover_request_active(NULL);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_LOADED,
|
colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_LOADED,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
@ -511,6 +588,7 @@ void *colo_process_incoming_thread(void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
vmstate_loading = false;
|
||||||
/* Throw the unreported error message after exited from loop */
|
/* Throw the unreported error message after exited from loop */
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
|
@ -520,6 +598,10 @@ out:
|
||||||
qemu_fclose(fb);
|
qemu_fclose(fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hope this not to be too long to loop here */
|
||||||
|
qemu_sem_wait(&mis->colo_incoming_sem);
|
||||||
|
qemu_sem_destroy(&mis->colo_incoming_sem);
|
||||||
|
/* Must be called after failover BH is completed */
|
||||||
if (mis->to_src_file) {
|
if (mis->to_src_file) {
|
||||||
qemu_fclose(mis->to_src_file);
|
qemu_fclose(mis->to_src_file);
|
||||||
}
|
}
|
||||||
|
|
|
@ -891,6 +891,9 @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
|
||||||
|
|
||||||
if (params->has_x_checkpoint_delay) {
|
if (params->has_x_checkpoint_delay) {
|
||||||
s->parameters.x_checkpoint_delay = params->x_checkpoint_delay;
|
s->parameters.x_checkpoint_delay = params->x_checkpoint_delay;
|
||||||
|
if (migration_in_colo_state()) {
|
||||||
|
colo_checkpoint_notify(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1297,6 +1300,15 @@ void qmp_migrate_set_downtime(double value, Error **errp)
|
||||||
qmp_migrate_set_parameters(&p, errp);
|
qmp_migrate_set_parameters(&p, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool migrate_release_ram(void)
|
||||||
|
{
|
||||||
|
MigrationState *s;
|
||||||
|
|
||||||
|
s = migrate_get_current();
|
||||||
|
|
||||||
|
return s->enabled_capabilities[MIGRATION_CAPABILITY_RELEASE_RAM];
|
||||||
|
}
|
||||||
|
|
||||||
bool migrate_postcopy_ram(void)
|
bool migrate_postcopy_ram(void)
|
||||||
{
|
{
|
||||||
MigrationState *s;
|
MigrationState *s;
|
||||||
|
@ -1713,6 +1725,10 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
|
||||||
*/
|
*/
|
||||||
qemu_savevm_send_ping(ms->to_dst_file, 4);
|
qemu_savevm_send_ping(ms->to_dst_file, 4);
|
||||||
|
|
||||||
|
if (migrate_release_ram()) {
|
||||||
|
ram_postcopy_migrated_memory_release(ms);
|
||||||
|
}
|
||||||
|
|
||||||
ret = qemu_file_get_error(ms->to_dst_file);
|
ret = qemu_file_get_error(ms->to_dst_file);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("postcopy_start: Migration stream errored");
|
error_report("postcopy_start: Migration stream errored");
|
||||||
|
|
|
@ -49,6 +49,7 @@ struct QEMUFile {
|
||||||
int buf_size; /* 0 when writing */
|
int buf_size; /* 0 when writing */
|
||||||
uint8_t buf[IO_BUF_SIZE];
|
uint8_t buf[IO_BUF_SIZE];
|
||||||
|
|
||||||
|
DECLARE_BITMAP(may_free, MAX_IOV_SIZE);
|
||||||
struct iovec iov[MAX_IOV_SIZE];
|
struct iovec iov[MAX_IOV_SIZE];
|
||||||
unsigned int iovcnt;
|
unsigned int iovcnt;
|
||||||
|
|
||||||
|
@ -132,6 +133,41 @@ bool qemu_file_is_writable(QEMUFile *f)
|
||||||
return f->ops->writev_buffer;
|
return f->ops->writev_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qemu_iovec_release_ram(QEMUFile *f)
|
||||||
|
{
|
||||||
|
struct iovec iov;
|
||||||
|
unsigned long idx;
|
||||||
|
|
||||||
|
/* Find and release all the contiguous memory ranges marked as may_free. */
|
||||||
|
idx = find_next_bit(f->may_free, f->iovcnt, 0);
|
||||||
|
if (idx >= f->iovcnt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
iov = f->iov[idx];
|
||||||
|
|
||||||
|
/* The madvise() in the loop is called for iov within a continuous range and
|
||||||
|
* then reinitialize the iov. And in the end, madvise() is called for the
|
||||||
|
* last iov.
|
||||||
|
*/
|
||||||
|
while ((idx = find_next_bit(f->may_free, f->iovcnt, idx + 1)) < f->iovcnt) {
|
||||||
|
/* check for adjacent buffer and coalesce them */
|
||||||
|
if (iov.iov_base + iov.iov_len == f->iov[idx].iov_base) {
|
||||||
|
iov.iov_len += f->iov[idx].iov_len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (qemu_madvise(iov.iov_base, iov.iov_len, QEMU_MADV_DONTNEED) < 0) {
|
||||||
|
error_report("migrate: madvise DONTNEED failed %p %zd: %s",
|
||||||
|
iov.iov_base, iov.iov_len, strerror(errno));
|
||||||
|
}
|
||||||
|
iov = f->iov[idx];
|
||||||
|
}
|
||||||
|
if (qemu_madvise(iov.iov_base, iov.iov_len, QEMU_MADV_DONTNEED) < 0) {
|
||||||
|
error_report("migrate: madvise DONTNEED failed %p %zd: %s",
|
||||||
|
iov.iov_base, iov.iov_len, strerror(errno));
|
||||||
|
}
|
||||||
|
memset(f->may_free, 0, sizeof(f->may_free));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flushes QEMUFile buffer
|
* Flushes QEMUFile buffer
|
||||||
*
|
*
|
||||||
|
@ -151,6 +187,8 @@ void qemu_fflush(QEMUFile *f)
|
||||||
if (f->iovcnt > 0) {
|
if (f->iovcnt > 0) {
|
||||||
expect = iov_size(f->iov, f->iovcnt);
|
expect = iov_size(f->iov, f->iovcnt);
|
||||||
ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
|
ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
|
||||||
|
|
||||||
|
qemu_iovec_release_ram(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
|
@ -304,13 +342,19 @@ int qemu_fclose(QEMUFile *f)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size)
|
static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size,
|
||||||
|
bool may_free)
|
||||||
{
|
{
|
||||||
/* check for adjacent buffer and coalesce them */
|
/* check for adjacent buffer and coalesce them */
|
||||||
if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
|
if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
|
||||||
f->iov[f->iovcnt - 1].iov_len) {
|
f->iov[f->iovcnt - 1].iov_len &&
|
||||||
|
may_free == test_bit(f->iovcnt - 1, f->may_free))
|
||||||
|
{
|
||||||
f->iov[f->iovcnt - 1].iov_len += size;
|
f->iov[f->iovcnt - 1].iov_len += size;
|
||||||
} else {
|
} else {
|
||||||
|
if (may_free) {
|
||||||
|
set_bit(f->iovcnt, f->may_free);
|
||||||
|
}
|
||||||
f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
|
f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
|
||||||
f->iov[f->iovcnt++].iov_len = size;
|
f->iov[f->iovcnt++].iov_len = size;
|
||||||
}
|
}
|
||||||
|
@ -320,14 +364,15 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size)
|
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size,
|
||||||
|
bool may_free)
|
||||||
{
|
{
|
||||||
if (f->last_error) {
|
if (f->last_error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->bytes_xfer += size;
|
f->bytes_xfer += size;
|
||||||
add_to_iovec(f, buf, size);
|
add_to_iovec(f, buf, size, may_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size)
|
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size)
|
||||||
|
@ -345,7 +390,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size)
|
||||||
}
|
}
|
||||||
memcpy(f->buf + f->buf_index, buf, l);
|
memcpy(f->buf + f->buf_index, buf, l);
|
||||||
f->bytes_xfer += l;
|
f->bytes_xfer += l;
|
||||||
add_to_iovec(f, f->buf + f->buf_index, l);
|
add_to_iovec(f, f->buf + f->buf_index, l, false);
|
||||||
f->buf_index += l;
|
f->buf_index += l;
|
||||||
if (f->buf_index == IO_BUF_SIZE) {
|
if (f->buf_index == IO_BUF_SIZE) {
|
||||||
qemu_fflush(f);
|
qemu_fflush(f);
|
||||||
|
@ -366,7 +411,7 @@ void qemu_put_byte(QEMUFile *f, int v)
|
||||||
|
|
||||||
f->buf[f->buf_index] = v;
|
f->buf[f->buf_index] = v;
|
||||||
f->bytes_xfer++;
|
f->bytes_xfer++;
|
||||||
add_to_iovec(f, f->buf + f->buf_index, 1);
|
add_to_iovec(f, f->buf + f->buf_index, 1, false);
|
||||||
f->buf_index++;
|
f->buf_index++;
|
||||||
if (f->buf_index == IO_BUF_SIZE) {
|
if (f->buf_index == IO_BUF_SIZE) {
|
||||||
qemu_fflush(f);
|
qemu_fflush(f);
|
||||||
|
@ -647,7 +692,7 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
|
||||||
}
|
}
|
||||||
qemu_put_be32(f, blen);
|
qemu_put_be32(f, blen);
|
||||||
if (f->ops->writev_buffer) {
|
if (f->ops->writev_buffer) {
|
||||||
add_to_iovec(f, f->buf + f->buf_index, blen);
|
add_to_iovec(f, f->buf + f->buf_index, blen, false);
|
||||||
}
|
}
|
||||||
f->buf_index += blen;
|
f->buf_index += blen;
|
||||||
if (f->buf_index == IO_BUF_SIZE) {
|
if (f->buf_index == IO_BUF_SIZE) {
|
||||||
|
|
|
@ -705,6 +705,16 @@ static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ram_release_pages(MigrationState *ms, const char *block_name,
|
||||||
|
uint64_t offset, int pages)
|
||||||
|
{
|
||||||
|
if (!migrate_release_ram() || !migration_in_postcopy(ms)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ram_discard_range(NULL, block_name, offset, pages << TARGET_PAGE_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ram_save_page: Send the given page to the stream
|
* ram_save_page: Send the given page to the stream
|
||||||
*
|
*
|
||||||
|
@ -713,13 +723,14 @@ static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
|
||||||
* >=0 - Number of pages written - this might legally be 0
|
* >=0 - Number of pages written - this might legally be 0
|
||||||
* if xbzrle noticed the page was the same.
|
* if xbzrle noticed the page was the same.
|
||||||
*
|
*
|
||||||
|
* @ms: The current migration state.
|
||||||
* @f: QEMUFile where to send the data
|
* @f: QEMUFile where to send the data
|
||||||
* @block: block that contains the page we want to send
|
* @block: block that contains the page we want to send
|
||||||
* @offset: offset inside the block for the page
|
* @offset: offset inside the block for the page
|
||||||
* @last_stage: if we are at the completion stage
|
* @last_stage: if we are at the completion stage
|
||||||
* @bytes_transferred: increase it with the number of transferred bytes
|
* @bytes_transferred: increase it with the number of transferred bytes
|
||||||
*/
|
*/
|
||||||
static int ram_save_page(QEMUFile *f, PageSearchStatus *pss,
|
static int ram_save_page(MigrationState *ms, QEMUFile *f, PageSearchStatus *pss,
|
||||||
bool last_stage, uint64_t *bytes_transferred)
|
bool last_stage, uint64_t *bytes_transferred)
|
||||||
{
|
{
|
||||||
int pages = -1;
|
int pages = -1;
|
||||||
|
@ -764,9 +775,9 @@ static int ram_save_page(QEMUFile *f, PageSearchStatus *pss,
|
||||||
* page would be stale
|
* page would be stale
|
||||||
*/
|
*/
|
||||||
xbzrle_cache_zero_page(current_addr);
|
xbzrle_cache_zero_page(current_addr);
|
||||||
|
ram_release_pages(ms, block->idstr, pss->offset, pages);
|
||||||
} else if (!ram_bulk_stage &&
|
} else if (!ram_bulk_stage &&
|
||||||
!migration_in_postcopy(migrate_get_current()) &&
|
!migration_in_postcopy(ms) && migrate_use_xbzrle()) {
|
||||||
migrate_use_xbzrle()) {
|
|
||||||
pages = save_xbzrle_page(f, &p, current_addr, block,
|
pages = save_xbzrle_page(f, &p, current_addr, block,
|
||||||
offset, last_stage, bytes_transferred);
|
offset, last_stage, bytes_transferred);
|
||||||
if (!last_stage) {
|
if (!last_stage) {
|
||||||
|
@ -783,7 +794,9 @@ static int ram_save_page(QEMUFile *f, PageSearchStatus *pss,
|
||||||
*bytes_transferred += save_page_header(f, block,
|
*bytes_transferred += save_page_header(f, block,
|
||||||
offset | RAM_SAVE_FLAG_PAGE);
|
offset | RAM_SAVE_FLAG_PAGE);
|
||||||
if (send_async) {
|
if (send_async) {
|
||||||
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
|
qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE,
|
||||||
|
migrate_release_ram() &
|
||||||
|
migration_in_postcopy(ms));
|
||||||
} else {
|
} else {
|
||||||
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
|
qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
@ -813,6 +826,8 @@ static int do_compress_ram_page(QEMUFile *f, RAMBlock *block,
|
||||||
error_report("compressed data failed!");
|
error_report("compressed data failed!");
|
||||||
} else {
|
} else {
|
||||||
bytes_sent += blen;
|
bytes_sent += blen;
|
||||||
|
ram_release_pages(migrate_get_current(), block->idstr,
|
||||||
|
offset & TARGET_PAGE_MASK, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes_sent;
|
return bytes_sent;
|
||||||
|
@ -893,14 +908,15 @@ static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block,
|
||||||
*
|
*
|
||||||
* Returns: Number of pages written.
|
* Returns: Number of pages written.
|
||||||
*
|
*
|
||||||
|
* @ms: The current migration state.
|
||||||
* @f: QEMUFile where to send the data
|
* @f: QEMUFile where to send the data
|
||||||
* @block: block that contains the page we want to send
|
* @block: block that contains the page we want to send
|
||||||
* @offset: offset inside the block for the page
|
* @offset: offset inside the block for the page
|
||||||
* @last_stage: if we are at the completion stage
|
* @last_stage: if we are at the completion stage
|
||||||
* @bytes_transferred: increase it with the number of transferred bytes
|
* @bytes_transferred: increase it with the number of transferred bytes
|
||||||
*/
|
*/
|
||||||
static int ram_save_compressed_page(QEMUFile *f, PageSearchStatus *pss,
|
static int ram_save_compressed_page(MigrationState *ms, QEMUFile *f,
|
||||||
bool last_stage,
|
PageSearchStatus *pss, bool last_stage,
|
||||||
uint64_t *bytes_transferred)
|
uint64_t *bytes_transferred)
|
||||||
{
|
{
|
||||||
int pages = -1;
|
int pages = -1;
|
||||||
|
@ -951,12 +967,17 @@ static int ram_save_compressed_page(QEMUFile *f, PageSearchStatus *pss,
|
||||||
error_report("compressed data failed!");
|
error_report("compressed data failed!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pages > 0) {
|
||||||
|
ram_release_pages(ms, block->idstr, pss->offset, pages);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
offset |= RAM_SAVE_FLAG_CONTINUE;
|
offset |= RAM_SAVE_FLAG_CONTINUE;
|
||||||
pages = save_zero_page(f, block, offset, p, bytes_transferred);
|
pages = save_zero_page(f, block, offset, p, bytes_transferred);
|
||||||
if (pages == -1) {
|
if (pages == -1) {
|
||||||
pages = compress_page_with_multi_thread(f, block, offset,
|
pages = compress_page_with_multi_thread(f, block, offset,
|
||||||
bytes_transferred);
|
bytes_transferred);
|
||||||
|
} else {
|
||||||
|
ram_release_pages(ms, block->idstr, pss->offset, pages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1231,11 +1252,11 @@ static int ram_save_target_page(MigrationState *ms, QEMUFile *f,
|
||||||
if (migration_bitmap_clear_dirty(dirty_ram_abs)) {
|
if (migration_bitmap_clear_dirty(dirty_ram_abs)) {
|
||||||
unsigned long *unsentmap;
|
unsigned long *unsentmap;
|
||||||
if (compression_switch && migrate_use_compression()) {
|
if (compression_switch && migrate_use_compression()) {
|
||||||
res = ram_save_compressed_page(f, pss,
|
res = ram_save_compressed_page(ms, f, pss,
|
||||||
last_stage,
|
last_stage,
|
||||||
bytes_transferred);
|
bytes_transferred);
|
||||||
} else {
|
} else {
|
||||||
res = ram_save_page(f, pss, last_stage,
|
res = ram_save_page(ms, f, pss, last_stage,
|
||||||
bytes_transferred);
|
bytes_transferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1325,6 +1346,11 @@ static int ram_find_and_save_block(QEMUFile *f, bool last_stage,
|
||||||
ram_addr_t dirty_ram_abs; /* Address of the start of the dirty page in
|
ram_addr_t dirty_ram_abs; /* Address of the start of the dirty page in
|
||||||
ram_addr_t space */
|
ram_addr_t space */
|
||||||
|
|
||||||
|
/* No dirty page as there is zero RAM */
|
||||||
|
if (!ram_bytes_total()) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
pss.block = last_seen_block;
|
pss.block = last_seen_block;
|
||||||
pss.offset = last_offset;
|
pss.offset = last_offset;
|
||||||
pss.complete_round = false;
|
pss.complete_round = false;
|
||||||
|
@ -1516,6 +1542,25 @@ void ram_debug_dump_bitmap(unsigned long *todump, bool expected)
|
||||||
|
|
||||||
/* **** functions for postcopy ***** */
|
/* **** functions for postcopy ***** */
|
||||||
|
|
||||||
|
void ram_postcopy_migrated_memory_release(MigrationState *ms)
|
||||||
|
{
|
||||||
|
struct RAMBlock *block;
|
||||||
|
unsigned long *bitmap = atomic_rcu_read(&migration_bitmap_rcu)->bmap;
|
||||||
|
|
||||||
|
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
||||||
|
unsigned long first = block->offset >> TARGET_PAGE_BITS;
|
||||||
|
unsigned long range = first + (block->used_length >> TARGET_PAGE_BITS);
|
||||||
|
unsigned long run_start = find_next_zero_bit(bitmap, range, first);
|
||||||
|
|
||||||
|
while (run_start < range) {
|
||||||
|
unsigned long run_end = find_next_bit(bitmap, range, run_start + 1);
|
||||||
|
ram_discard_range(NULL, block->idstr, run_start << TARGET_PAGE_BITS,
|
||||||
|
(run_end - run_start) << TARGET_PAGE_BITS);
|
||||||
|
run_start = find_next_zero_bit(bitmap, range, run_end + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback from postcopy_each_ram_send_discard for each RAMBlock
|
* Callback from postcopy_each_ram_send_discard for each RAMBlock
|
||||||
* Note: At this point the 'unsentmap' is the processed bitmap combined
|
* Note: At this point the 'unsentmap' is the processed bitmap combined
|
||||||
|
@ -1912,14 +1957,17 @@ static int ram_save_init_globals(void)
|
||||||
bytes_transferred = 0;
|
bytes_transferred = 0;
|
||||||
reset_ram_globals();
|
reset_ram_globals();
|
||||||
|
|
||||||
ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
|
|
||||||
migration_bitmap_rcu = g_new0(struct BitmapRcu, 1);
|
migration_bitmap_rcu = g_new0(struct BitmapRcu, 1);
|
||||||
migration_bitmap_rcu->bmap = bitmap_new(ram_bitmap_pages);
|
/* Skip setting bitmap if there is no RAM */
|
||||||
bitmap_set(migration_bitmap_rcu->bmap, 0, ram_bitmap_pages);
|
if (ram_bytes_total()) {
|
||||||
|
ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
|
||||||
|
migration_bitmap_rcu->bmap = bitmap_new(ram_bitmap_pages);
|
||||||
|
bitmap_set(migration_bitmap_rcu->bmap, 0, ram_bitmap_pages);
|
||||||
|
|
||||||
if (migrate_postcopy_ram()) {
|
if (migrate_postcopy_ram()) {
|
||||||
migration_bitmap_rcu->unsentmap = bitmap_new(ram_bitmap_pages);
|
migration_bitmap_rcu->unsentmap = bitmap_new(ram_bitmap_pages);
|
||||||
bitmap_set(migration_bitmap_rcu->unsentmap, 0, ram_bitmap_pages);
|
bitmap_set(migration_bitmap_rcu->unsentmap, 0, ram_bitmap_pages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -356,7 +356,7 @@ static const VMStateDescription vmstate_configuration = {
|
||||||
.pre_save = configuration_pre_save,
|
.pre_save = configuration_pre_save,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(len, SaveState),
|
VMSTATE_UINT32(len, SaveState),
|
||||||
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len),
|
VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, len),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
},
|
},
|
||||||
.subsections = (const VMStateDescription*[]) {
|
.subsections = (const VMStateDescription*[]) {
|
||||||
|
|
|
@ -68,10 +68,10 @@ static void *vmstate_base_addr(void *opaque, VMStateField *field, bool alloc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (size) {
|
if (size) {
|
||||||
*((void **)base_addr + field->start) = g_malloc(size);
|
*(void **)base_addr = g_malloc(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
base_addr = *(void **)base_addr + field->start;
|
base_addr = *(void **)base_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base_addr;
|
return base_addr;
|
||||||
|
@ -935,6 +935,46 @@ const VMStateInfo vmstate_info_unused_buffer = {
|
||||||
.put = put_unused_buffer,
|
.put = put_unused_buffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate
|
||||||
|
* a temporary buffer and the pre_load/pre_save methods in the child vmsd
|
||||||
|
* copy stuff from the parent into the child and do calculations to fill
|
||||||
|
* in fields that don't really exist in the parent but need to be in the
|
||||||
|
* stream.
|
||||||
|
*/
|
||||||
|
static int get_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const VMStateDescription *vmsd = field->vmsd;
|
||||||
|
int version_id = field->version_id;
|
||||||
|
void *tmp = g_malloc(size);
|
||||||
|
|
||||||
|
/* Writes the parent field which is at the start of the tmp */
|
||||||
|
*(void **)tmp = pv;
|
||||||
|
ret = vmstate_load_state(f, vmsd, tmp, version_id);
|
||||||
|
g_free(tmp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int put_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field,
|
||||||
|
QJSON *vmdesc)
|
||||||
|
{
|
||||||
|
const VMStateDescription *vmsd = field->vmsd;
|
||||||
|
void *tmp = g_malloc(size);
|
||||||
|
|
||||||
|
/* Writes the parent field which is at the start of the tmp */
|
||||||
|
*(void **)tmp = pv;
|
||||||
|
vmstate_save_state(f, vmsd, tmp, vmdesc);
|
||||||
|
g_free(tmp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_tmp = {
|
||||||
|
.name = "tmp",
|
||||||
|
.get = get_tmp,
|
||||||
|
.put = put_tmp,
|
||||||
|
};
|
||||||
|
|
||||||
/* bitmaps (as defined by bitmap.h). Note that size here is the size
|
/* bitmaps (as defined by bitmap.h). Note that size here is the size
|
||||||
* of the bitmap in bits. The on-the-wire format of a bitmap is 64
|
* of the bitmap in bits. The on-the-wire format of a bitmap is 64
|
||||||
* bit words with the bits in big endian order. The in-memory format
|
* bit words with the bits in big endian order. The in-memory format
|
||||||
|
|
|
@ -865,11 +865,14 @@
|
||||||
# side, this process is called COarse-Grain LOck Stepping (COLO) for
|
# side, this process is called COarse-Grain LOck Stepping (COLO) for
|
||||||
# Non-stop Service. (since 2.8)
|
# Non-stop Service. (since 2.8)
|
||||||
#
|
#
|
||||||
|
# @release-ram: if enabled, qemu will free the migrated ram pages on the source
|
||||||
|
# during postcopy-ram migration. (since 2.9)
|
||||||
|
#
|
||||||
# Since: 1.2
|
# Since: 1.2
|
||||||
##
|
##
|
||||||
{ 'enum': 'MigrationCapability',
|
{ 'enum': 'MigrationCapability',
|
||||||
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
|
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
|
||||||
'compress', 'events', 'postcopy-ram', 'x-colo'] }
|
'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram'] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @MigrationCapabilityStatus:
|
# @MigrationCapabilityStatus:
|
||||||
|
@ -1190,10 +1193,12 @@
|
||||||
#
|
#
|
||||||
# @completed: finish the process of failover
|
# @completed: finish the process of failover
|
||||||
#
|
#
|
||||||
|
# @relaunch: restart the failover process, from 'none' -> 'completed' (Since 2.9)
|
||||||
|
#
|
||||||
# Since: 2.8
|
# Since: 2.8
|
||||||
##
|
##
|
||||||
{ 'enum': 'FailoverStatus',
|
{ 'enum': 'FailoverStatus',
|
||||||
'data': [ 'none', 'require', 'active', 'completed'] }
|
'data': [ 'none', 'require', 'active', 'completed', 'relaunch' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @x-colo-lost-heartbeat:
|
# @x-colo-lost-heartbeat:
|
||||||
|
|
|
@ -180,7 +180,7 @@ const VMStateDescription vmstate_s390_cpu = {
|
||||||
VMSTATE_UINT8(env.cpu_state, S390CPU),
|
VMSTATE_UINT8(env.cpu_state, S390CPU),
|
||||||
VMSTATE_UINT8(env.sigp_order, S390CPU),
|
VMSTATE_UINT8(env.sigp_order, S390CPU),
|
||||||
VMSTATE_UINT32_V(irqstate_saved_size, S390CPU, 4),
|
VMSTATE_UINT32_V(irqstate_saved_size, S390CPU, 4),
|
||||||
VMSTATE_VBUFFER_UINT32(irqstate, S390CPU, 4, NULL, 0,
|
VMSTATE_VBUFFER_UINT32(irqstate, S390CPU, 4, NULL,
|
||||||
irqstate_saved_size),
|
irqstate_saved_size),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
},
|
},
|
||||||
|
|
|
@ -90,7 +90,7 @@ static void save_buffer(const uint8_t *buf, size_t buf_size)
|
||||||
qemu_fclose(fsave);
|
qemu_fclose(fsave);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compare_vmstate(uint8_t *wire, size_t size)
|
static void compare_vmstate(const uint8_t *wire, size_t size)
|
||||||
{
|
{
|
||||||
QEMUFile *f = open_test_file(false);
|
QEMUFile *f = open_test_file(false);
|
||||||
uint8_t result[size];
|
uint8_t result[size];
|
||||||
|
@ -113,7 +113,7 @@ static void compare_vmstate(uint8_t *wire, size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_vmstate_one(const VMStateDescription *desc, void *obj,
|
static int load_vmstate_one(const VMStateDescription *desc, void *obj,
|
||||||
int version, uint8_t *wire, size_t size)
|
int version, const uint8_t *wire, size_t size)
|
||||||
{
|
{
|
||||||
QEMUFile *f;
|
QEMUFile *f;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -137,7 +137,7 @@ static int load_vmstate_one(const VMStateDescription *desc, void *obj,
|
||||||
static int load_vmstate(const VMStateDescription *desc,
|
static int load_vmstate(const VMStateDescription *desc,
|
||||||
void *obj, void *obj_clone,
|
void *obj, void *obj_clone,
|
||||||
void (*obj_copy)(void *, void*),
|
void (*obj_copy)(void *, void*),
|
||||||
int version, uint8_t *wire, size_t size)
|
int version, const uint8_t *wire, size_t size)
|
||||||
{
|
{
|
||||||
/* We test with zero size */
|
/* We test with zero size */
|
||||||
obj_copy(obj_clone, obj);
|
obj_copy(obj_clone, obj);
|
||||||
|
@ -289,7 +289,6 @@ static void test_simple_primitive(void)
|
||||||
FIELD_EQUAL(i64_1);
|
FIELD_EQUAL(i64_1);
|
||||||
FIELD_EQUAL(i64_2);
|
FIELD_EQUAL(i64_2);
|
||||||
}
|
}
|
||||||
#undef FIELD_EQUAL
|
|
||||||
|
|
||||||
typedef struct TestStruct {
|
typedef struct TestStruct {
|
||||||
uint32_t a, b, c, e;
|
uint32_t a, b, c, e;
|
||||||
|
@ -474,7 +473,6 @@ static void test_load_skip(void)
|
||||||
qemu_fclose(loading);
|
qemu_fclose(loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
} TestStructTriv;
|
} TestStructTriv;
|
||||||
|
@ -688,6 +686,94 @@ static void test_load_q(void)
|
||||||
qemu_fclose(fload);
|
qemu_fclose(fload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct TmpTestStruct {
|
||||||
|
TestStruct *parent;
|
||||||
|
int64_t diff;
|
||||||
|
} TmpTestStruct;
|
||||||
|
|
||||||
|
static void tmp_child_pre_save(void *opaque)
|
||||||
|
{
|
||||||
|
struct TmpTestStruct *tts = opaque;
|
||||||
|
|
||||||
|
tts->diff = tts->parent->b - tts->parent->a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tmp_child_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
struct TmpTestStruct *tts = opaque;
|
||||||
|
|
||||||
|
tts->parent->b = tts->parent->a + tts->diff;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_tmp_back_to_parent = {
|
||||||
|
.name = "test/tmp_child_parent",
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT64(f, TestStruct),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_tmp_child = {
|
||||||
|
.name = "test/tmp_child",
|
||||||
|
.pre_save = tmp_child_pre_save,
|
||||||
|
.post_load = tmp_child_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_INT64(diff, TmpTestStruct),
|
||||||
|
VMSTATE_STRUCT_POINTER(parent, TmpTestStruct,
|
||||||
|
vmstate_tmp_back_to_parent, TestStruct),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_with_tmp = {
|
||||||
|
.name = "test/with_tmp",
|
||||||
|
.version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(a, TestStruct),
|
||||||
|
VMSTATE_UINT64(d, TestStruct),
|
||||||
|
VMSTATE_WITH_TMP(TestStruct, TmpTestStruct, vmstate_tmp_child),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void obj_tmp_copy(void *target, void *source)
|
||||||
|
{
|
||||||
|
memcpy(target, source, sizeof(TestStruct));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_tmp_struct(void)
|
||||||
|
{
|
||||||
|
TestStruct obj, obj_clone;
|
||||||
|
|
||||||
|
uint8_t const wire_with_tmp[] = {
|
||||||
|
/* u32 a */ 0x00, 0x00, 0x00, 0x02,
|
||||||
|
/* u64 d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||||
|
/* diff */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||||
|
/* u64 f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
|
||||||
|
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
||||||
|
};
|
||||||
|
|
||||||
|
memset(&obj, 0, sizeof(obj));
|
||||||
|
obj.a = 2;
|
||||||
|
obj.b = 4;
|
||||||
|
obj.d = 1;
|
||||||
|
obj.f = 8;
|
||||||
|
save_vmstate(&vmstate_with_tmp, &obj);
|
||||||
|
|
||||||
|
compare_vmstate(wire_with_tmp, sizeof(wire_with_tmp));
|
||||||
|
|
||||||
|
memset(&obj, 0, sizeof(obj));
|
||||||
|
SUCCESS(load_vmstate(&vmstate_with_tmp, &obj, &obj_clone,
|
||||||
|
obj_tmp_copy, 1, wire_with_tmp,
|
||||||
|
sizeof(wire_with_tmp)));
|
||||||
|
g_assert_cmpint(obj.a, ==, 2); /* From top level vmsd */
|
||||||
|
g_assert_cmpint(obj.b, ==, 4); /* from the post_load */
|
||||||
|
g_assert_cmpint(obj.d, ==, 1); /* From top level vmsd */
|
||||||
|
g_assert_cmpint(obj.f, ==, 8); /* From the child->parent */
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
temp_fd = mkstemp(temp_file);
|
temp_fd = mkstemp(temp_file);
|
||||||
|
@ -708,7 +794,7 @@ int main(int argc, char **argv)
|
||||||
test_arr_ptr_str_no0_load);
|
test_arr_ptr_str_no0_load);
|
||||||
g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
|
g_test_add_func("/vmstate/qtailq/save/saveq", test_save_q);
|
||||||
g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
|
g_test_add_func("/vmstate/qtailq/load/loadq", test_load_q);
|
||||||
|
g_test_add_func("/vmstate/tmp_struct", test_tmp_struct);
|
||||||
g_test_run();
|
g_test_run();
|
||||||
|
|
||||||
close(temp_fd);
|
close(temp_fd);
|
||||||
|
|
|
@ -118,7 +118,7 @@ const VMStateDescription vmstate_fifo8 = {
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_VBUFFER_UINT32(data, Fifo8, 1, NULL, 0, capacity),
|
VMSTATE_VBUFFER_UINT32(data, Fifo8, 1, NULL, capacity),
|
||||||
VMSTATE_UINT32(head, Fifo8),
|
VMSTATE_UINT32(head, Fifo8),
|
||||||
VMSTATE_UINT32(num, Fifo8),
|
VMSTATE_UINT32(num, Fifo8),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
|
|
Loading…
Reference in New Issue