aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/io.c10
-rw-r--r--block/qcow2.c2
-rw-r--r--docs/interop/qcow2.txt2
-rw-r--r--docs/system/arm/virt.rst4
-rw-r--r--hw/acpi/ghes.c12
-rw-r--r--hw/arm/boot.c6
-rw-r--r--hw/misc/aspeed_sdmc.c7
-rw-r--r--hw/net/virtio-net.c30
-rw-r--r--hw/virtio/virtio-pci.c2
-rw-r--r--linux-user/main.c16
-rw-r--r--linux-user/syscall.c13
-rw-r--r--migration/block-dirty-bitmap.c474
-rw-r--r--migration/migration.c15
-rw-r--r--migration/migration.h3
-rw-r--r--migration/savevm.c37
-rw-r--r--net/queue.c3
-rw-r--r--qga/commands-win32.c29
-rw-r--r--qga/qapi-schema.json2
m---------slirp0
-rw-r--r--target/arm/helper.c19
-rw-r--r--target/arm/mte_helper.c37
-rwxr-xr-xtests/qemu-iotests/02819
-rw-r--r--tests/qemu-iotests/028.out11
-rwxr-xr-xtests/qemu-iotests/1978
-rw-r--r--tests/qemu-iotests/197.out2
-rwxr-xr-xtests/qemu-iotests/199250
-rw-r--r--tests/qemu-iotests/199.out4
-rw-r--r--tests/qemu-iotests/group12
-rw-r--r--tests/test-char.c2
29 files changed, 717 insertions, 314 deletions
diff --git a/block/io.c b/block/io.c
index b6564e34c5..ad3a51ed53 100644
--- a/block/io.c
+++ b/block/io.c
@@ -1524,12 +1524,13 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
assert(num);
ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining,
- num, qiov, bytes - bytes_remaining, 0);
+ num, qiov,
+ qiov_offset + bytes - bytes_remaining, 0);
max_bytes -= num;
} else {
num = bytes_remaining;
- ret = qemu_iovec_memset(qiov, bytes - bytes_remaining, 0,
- bytes_remaining);
+ ret = qemu_iovec_memset(qiov, qiov_offset + bytes - bytes_remaining,
+ 0, bytes_remaining);
}
if (ret < 0) {
goto out;
@@ -2032,7 +2033,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
}
ret = bdrv_driver_pwritev(bs, offset + bytes - bytes_remaining,
- num, qiov, bytes - bytes_remaining,
+ num, qiov,
+ qiov_offset + bytes - bytes_remaining,
local_flags);
if (ret < 0) {
break;
diff --git a/block/qcow2.c b/block/qcow2.c
index fadf3422f8..6ad6bdc166 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -66,7 +66,7 @@ typedef struct {
} QEMU_PACKED QCowExtension;
#define QCOW2_EXT_MAGIC_END 0
-#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
+#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xe2792aca
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
#define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77
#define QCOW2_EXT_MAGIC_BITMAPS 0x23852875
diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
index cb723463f2..f072e27900 100644
--- a/docs/interop/qcow2.txt
+++ b/docs/interop/qcow2.txt
@@ -231,7 +231,7 @@ be stored. Each extension has a structure like the following:
Byte 0 - 3: Header extension type:
0x00000000 - End of the header extension area
- 0xE2792ACA - Backing file format name string
+ 0xe2792aca - Backing file format name string
0x6803f857 - Feature name table
0x23852875 - Bitmaps extension
0x0537be77 - Full disk encryption header pointer
diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index 6621ab7205..32dc5eb22e 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -79,6 +79,10 @@ virtualization
Set ``on``/``off`` to enable/disable emulating a guest CPU which implements the
Arm Virtualization Extensions. The default is ``off``.
+mte
+ Set ``on``/``off`` to enable/disable emulating a guest CPU which implements the
+ Arm Memory Tagging Extensions. The default is ``off``.
+
highmem
Set ``on``/``off`` to enable/disable placing devices and RAM in physical
address space above 32 bits. The default is ``on`` for machine types
diff --git a/hw/acpi/ghes.c b/hw/acpi/ghes.c
index b363bc331d..f0ee9f51ca 100644
--- a/hw/acpi/ghes.c
+++ b/hw/acpi/ghes.c
@@ -204,16 +204,12 @@ static int acpi_ghes_record_mem_error(uint64_t error_block_address,
/* This is the length if adding a new generic error data entry*/
data_length = ACPI_GHES_DATA_LENGTH + ACPI_GHES_MEM_CPER_LENGTH;
-
/*
- * Check whether it will run out of the preallocated memory if adding a new
- * generic error data entry
+ * It should not run out of the preallocated memory if adding a new generic
+ * error data entry
*/
- if ((data_length + ACPI_GHES_GESB_SIZE) > ACPI_GHES_MAX_RAW_DATA_LENGTH) {
- error_report("Not enough memory to record new CPER!!!");
- g_array_free(block, true);
- return -1;
- }
+ assert((data_length + ACPI_GHES_GESB_SIZE) <=
+ ACPI_GHES_MAX_RAW_DATA_LENGTH);
/* Build the new generic error status block header */
acpi_ghes_generic_error_status(block, ACPI_GEBS_UNCORRECTABLE,
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index fef4072db1..3e9816af80 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -736,6 +736,12 @@ static void do_cpu_reset(void *opaque)
} else {
env->pstate = PSTATE_MODE_EL1h;
}
+ if (cpu_isar_feature(aa64_pauth, cpu)) {
+ env->cp15.scr_el3 |= SCR_API | SCR_APK;
+ }
+ if (cpu_isar_feature(aa64_mte, cpu)) {
+ env->cp15.scr_el3 |= SCR_ATA;
+ }
/* AArch64 kernels never boot in secure mode */
assert(!info->secure_boot);
/* This hook is only supported for AArch32 currently:
diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c
index 0737d8de81..855848b7d2 100644
--- a/hw/misc/aspeed_sdmc.c
+++ b/hw/misc/aspeed_sdmc.c
@@ -255,6 +255,7 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp)
AspeedSDMCState *s = ASPEED_SDMC(dev);
AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s);
+ assert(asc->max_ram_size < 4 * GiB); /* 32-bit address bus */
s->max_ram_size = asc->max_ram_size;
memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s,
@@ -341,7 +342,7 @@ static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data)
AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass);
dc->desc = "ASPEED 2400 SDRAM Memory Controller";
- asc->max_ram_size = 512 << 20;
+ asc->max_ram_size = 512 * MiB;
asc->compute_conf = aspeed_2400_sdmc_compute_conf;
asc->write = aspeed_2400_sdmc_write;
asc->valid_ram_sizes = aspeed_2400_ram_sizes;
@@ -408,7 +409,7 @@ static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data)
AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass);
dc->desc = "ASPEED 2500 SDRAM Memory Controller";
- asc->max_ram_size = 1024 << 20;
+ asc->max_ram_size = 1 * GiB;
asc->compute_conf = aspeed_2500_sdmc_compute_conf;
asc->write = aspeed_2500_sdmc_write;
asc->valid_ram_sizes = aspeed_2500_ram_sizes;
@@ -485,7 +486,7 @@ static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data)
AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass);
dc->desc = "ASPEED 2600 SDRAM Memory Controller";
- asc->max_ram_size = 2048 << 20;
+ asc->max_ram_size = 2 * GiB;
asc->compute_conf = aspeed_2600_sdmc_compute_conf;
asc->write = aspeed_2600_sdmc_write;
asc->valid_ram_sizes = aspeed_2600_ram_sizes;
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 4895af1cbe..a1fe9e9285 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -125,6 +125,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
{
VirtIONet *n = VIRTIO_NET(vdev);
struct virtio_net_config netcfg;
+ NetClientState *nc = qemu_get_queue(n->nic);
int ret = 0;
memset(&netcfg, 0 , sizeof(struct virtio_net_config));
@@ -142,13 +143,16 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
VIRTIO_NET_RSS_SUPPORTED_HASHES);
memcpy(config, &netcfg, n->config_size);
- NetClientState *nc = qemu_get_queue(n->nic);
- if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
+ /*
+ * Is this VDPA? No peer means not VDPA: there's no way to
+ * disconnect/reconnect a VDPA peer.
+ */
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
ret = vhost_net_get_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg,
- n->config_size);
- if (ret != -1) {
- memcpy(config, &netcfg, n->config_size);
- }
+ n->config_size);
+ if (ret != -1) {
+ memcpy(config, &netcfg, n->config_size);
+ }
}
}
@@ -156,6 +160,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
{
VirtIONet *n = VIRTIO_NET(vdev);
struct virtio_net_config netcfg = {};
+ NetClientState *nc = qemu_get_queue(n->nic);
memcpy(&netcfg, config, n->config_size);
@@ -166,11 +171,14 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
}
- NetClientState *nc = qemu_get_queue(n->nic);
- if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
- vhost_net_set_config(get_vhost_net(nc->peer), (uint8_t *)&netcfg,
- 0, n->config_size,
- VHOST_SET_CONFIG_TYPE_MASTER);
+ /*
+ * Is this VDPA? No peer means not VDPA: there's no way to
+ * disconnect/reconnect a VDPA peer.
+ */
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
+ vhost_net_set_config(get_vhost_net(nc->peer),
+ (uint8_t *)&netcfg, 0, n->config_size,
+ VHOST_SET_CONFIG_TYPE_MASTER);
}
}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 4ad3ad81a2..ccdf54e81c 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1113,7 +1113,7 @@ static bool virtio_pci_queue_enabled(DeviceState *d, int n)
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
- return proxy->vqs[vdev->queue_sel].enabled;
+ return proxy->vqs[n].enabled;
}
return virtio_queue_enabled_legacy(vdev, n);
diff --git a/linux-user/main.c b/linux-user/main.c
index 3597e99bb1..75c9785157 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -758,15 +758,27 @@ int main(int argc, char **argv, char **envp)
if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
unsigned long tmp;
- if (fscanf(fp, "%lu", &tmp) == 1) {
+ if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) {
mmap_min_addr = tmp;
- qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n", mmap_min_addr);
+ qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n",
+ mmap_min_addr);
}
fclose(fp);
}
}
/*
+ * We prefer to not make NULL pointers accessible to QEMU.
+ * If we're in a chroot with no /proc, fall back to 1 page.
+ */
+ if (mmap_min_addr == 0) {
+ mmap_min_addr = qemu_host_page_size;
+ qemu_log_mask(CPU_LOG_PAGE,
+ "host mmap_min_addr=0x%lx (fallback)\n",
+ mmap_min_addr);
+ }
+
+ /*
* Prepare copy of argv vector for target.
*/
target_argc = argc - optind;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index f5c4f6b95d..945fc25279 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -388,14 +388,7 @@ static bitmask_transtbl fcntl_flags_tbl[] = {
{ 0, 0, 0, 0 }
};
-static int sys_getcwd1(char *buf, size_t size)
-{
- if (getcwd(buf, size) == NULL) {
- /* getcwd() sets errno */
- return (-1);
- }
- return strlen(buf)+1;
-}
+_syscall2(int, sys_getcwd1, char *, buf, size_t, size)
#ifdef TARGET_NR_utimensat
#if defined(__NR_utimensat)
@@ -8868,7 +8861,9 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
unlock_user(p, arg1, 0);
if (arg3) {
puts = &uts;
- target_to_host_timespec(puts, arg3);
+ if (target_to_host_timespec(puts, arg3)) {
+ return -TARGET_EFAULT;
+ }
} else {
puts = NULL;
}
diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c
index b0dbf9eeed..784330ebe1 100644
--- a/migration/block-dirty-bitmap.c
+++ b/migration/block-dirty-bitmap.c
@@ -97,26 +97,28 @@
#define DIRTY_BITMAP_MIG_START_FLAG_ENABLED 0x01
#define DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT 0x02
-/* 0x04 was "AUTOLOAD" flags on elder versions, no it is ignored */
+/* 0x04 was "AUTOLOAD" flags on older versions, now it is ignored */
#define DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK 0xf8
-typedef struct DirtyBitmapMigBitmapState {
+/* State of one bitmap during save process */
+typedef struct SaveBitmapState {
/* Written during setup phase. */
BlockDriverState *bs;
const char *node_name;
BdrvDirtyBitmap *bitmap;
uint64_t total_sectors;
uint64_t sectors_per_chunk;
- QSIMPLEQ_ENTRY(DirtyBitmapMigBitmapState) entry;
+ QSIMPLEQ_ENTRY(SaveBitmapState) entry;
uint8_t flags;
/* For bulk phase. */
bool bulk_completed;
uint64_t cur_sector;
-} DirtyBitmapMigBitmapState;
+} SaveBitmapState;
-typedef struct DirtyBitmapMigState {
- QSIMPLEQ_HEAD(, DirtyBitmapMigBitmapState) dbms_list;
+/* State of the dirty bitmap migration (DBM) during save process */
+typedef struct DBMSaveState {
+ QSIMPLEQ_HEAD(, SaveBitmapState) dbms_list;
bool bulk_completed;
bool no_bitmaps;
@@ -124,30 +126,44 @@ typedef struct DirtyBitmapMigState {
/* for send_bitmap_bits() */
BlockDriverState *prev_bs;
BdrvDirtyBitmap *prev_bitmap;
-} DirtyBitmapMigState;
+} DBMSaveState;
-typedef struct DirtyBitmapLoadState {
+typedef struct LoadBitmapState {
+ BlockDriverState *bs;
+ BdrvDirtyBitmap *bitmap;
+ bool migrated;
+ bool enabled;
+} LoadBitmapState;
+
+/* State of the dirty bitmap migration (DBM) during load process */
+typedef struct DBMLoadState {
uint32_t flags;
char node_name[256];
char bitmap_name[256];
BlockDriverState *bs;
BdrvDirtyBitmap *bitmap;
-} DirtyBitmapLoadState;
-static DirtyBitmapMigState dirty_bitmap_mig_state;
+ bool before_vm_start_handled; /* set in dirty_bitmap_mig_before_vm_start */
-typedef struct DirtyBitmapLoadBitmapState {
- BlockDriverState *bs;
- BdrvDirtyBitmap *bitmap;
- bool migrated;
-} DirtyBitmapLoadBitmapState;
-static GSList *enabled_bitmaps;
-QemuMutex finish_lock;
+ /*
+ * cancelled
+ * Incoming migration is cancelled for some reason. That means that we
+ * still should read our chunks from migration stream, to not affect other
+ * migration objects (like RAM), but just ignore them and do not touch any
+ * bitmaps or nodes.
+ */
+ bool cancelled;
-void init_dirty_bitmap_incoming_migration(void)
-{
- qemu_mutex_init(&finish_lock);
-}
+ GSList *bitmaps;
+ QemuMutex lock; /* protect bitmaps */
+} DBMLoadState;
+
+typedef struct DBMState {
+ DBMSaveState save;
+ DBMLoadState load;
+} DBMState;
+
+static DBMState dbm_state;
static uint32_t qemu_get_bitmap_flags(QEMUFile *f)
{
@@ -164,27 +180,27 @@ static uint32_t qemu_get_bitmap_flags(QEMUFile *f)
static void qemu_put_bitmap_flags(QEMUFile *f, uint32_t flags)
{
- /* The code currently do not send flags more than one byte */
+ /* The code currently does not send flags as more than one byte */
assert(!(flags & (0xffffff00 | DIRTY_BITMAP_MIG_EXTRA_FLAGS)));
qemu_put_byte(f, flags);
}
-static void send_bitmap_header(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
- uint32_t additional_flags)
+static void send_bitmap_header(QEMUFile *f, DBMSaveState *s,
+ SaveBitmapState *dbms, uint32_t additional_flags)
{
BlockDriverState *bs = dbms->bs;
BdrvDirtyBitmap *bitmap = dbms->bitmap;
uint32_t flags = additional_flags;
trace_send_bitmap_header_enter();
- if (bs != dirty_bitmap_mig_state.prev_bs) {
- dirty_bitmap_mig_state.prev_bs = bs;
+ if (bs != s->prev_bs) {
+ s->prev_bs = bs;
flags |= DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME;
}
- if (bitmap != dirty_bitmap_mig_state.prev_bitmap) {
- dirty_bitmap_mig_state.prev_bitmap = bitmap;
+ if (bitmap != s->prev_bitmap) {
+ s->prev_bitmap = bitmap;
flags |= DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME;
}
@@ -199,19 +215,22 @@ static void send_bitmap_header(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
}
}
-static void send_bitmap_start(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
+static void send_bitmap_start(QEMUFile *f, DBMSaveState *s,
+ SaveBitmapState *dbms)
{
- send_bitmap_header(f, dbms, DIRTY_BITMAP_MIG_FLAG_START);
+ send_bitmap_header(f, s, dbms, DIRTY_BITMAP_MIG_FLAG_START);
qemu_put_be32(f, bdrv_dirty_bitmap_granularity(dbms->bitmap));
qemu_put_byte(f, dbms->flags);
}
-static void send_bitmap_complete(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
+static void send_bitmap_complete(QEMUFile *f, DBMSaveState *s,
+ SaveBitmapState *dbms)
{
- send_bitmap_header(f, dbms, DIRTY_BITMAP_MIG_FLAG_COMPLETE);
+ send_bitmap_header(f, s, dbms, DIRTY_BITMAP_MIG_FLAG_COMPLETE);
}
-static void send_bitmap_bits(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
+static void send_bitmap_bits(QEMUFile *f, DBMSaveState *s,
+ SaveBitmapState *dbms,
uint64_t start_sector, uint32_t nr_sectors)
{
/* align for buffer_is_zero() */
@@ -236,7 +255,7 @@ static void send_bitmap_bits(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
trace_send_bitmap_bits(flags, start_sector, nr_sectors, buf_size);
- send_bitmap_header(f, dbms, flags);
+ send_bitmap_header(f, s, dbms, flags);
qemu_put_be64(f, start_sector);
qemu_put_be32(f, nr_sectors);
@@ -255,12 +274,12 @@ static void send_bitmap_bits(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
}
/* Called with iothread lock taken. */
-static void dirty_bitmap_mig_cleanup(void)
+static void dirty_bitmap_do_save_cleanup(DBMSaveState *s)
{
- DirtyBitmapMigBitmapState *dbms;
+ SaveBitmapState *dbms;
- while ((dbms = QSIMPLEQ_FIRST(&dirty_bitmap_mig_state.dbms_list)) != NULL) {
- QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry);
+ while ((dbms = QSIMPLEQ_FIRST(&s->dbms_list)) != NULL) {
+ QSIMPLEQ_REMOVE_HEAD(&s->dbms_list, entry);
bdrv_dirty_bitmap_set_busy(dbms->bitmap, false);
bdrv_unref(dbms->bs);
g_free(dbms);
@@ -268,10 +287,11 @@ static void dirty_bitmap_mig_cleanup(void)
}
/* Called with iothread lock taken. */
-static int add_bitmaps_to_list(BlockDriverState *bs, const char *bs_name)
+static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs,
+ const char *bs_name)
{
BdrvDirtyBitmap *bitmap;
- DirtyBitmapMigBitmapState *dbms;
+ SaveBitmapState *dbms;
Error *local_err = NULL;
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
@@ -309,7 +329,7 @@ static int add_bitmaps_to_list(BlockDriverState *bs, const char *bs_name)
bdrv_ref(bs);
bdrv_dirty_bitmap_set_busy(bitmap, true);
- dbms = g_new0(DirtyBitmapMigBitmapState, 1);
+ dbms = g_new0(SaveBitmapState, 1);
dbms->bs = bs;
dbms->node_name = bs_name;
dbms->bitmap = bitmap;
@@ -323,25 +343,24 @@ static int add_bitmaps_to_list(BlockDriverState *bs, const char *bs_name)
dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT;
}
- QSIMPLEQ_INSERT_TAIL(&dirty_bitmap_mig_state.dbms_list,
- dbms, entry);
+ QSIMPLEQ_INSERT_TAIL(&s->dbms_list, dbms, entry);
}
return 0;
}
/* Called with iothread lock taken. */
-static int init_dirty_bitmap_migration(void)
+static int init_dirty_bitmap_migration(DBMSaveState *s)
{
BlockDriverState *bs;
- DirtyBitmapMigBitmapState *dbms;
+ SaveBitmapState *dbms;
GHashTable *handled_by_blk = g_hash_table_new(NULL, NULL);
BlockBackend *blk;
- dirty_bitmap_mig_state.bulk_completed = false;
- dirty_bitmap_mig_state.prev_bs = NULL;
- dirty_bitmap_mig_state.prev_bitmap = NULL;
- dirty_bitmap_mig_state.no_bitmaps = false;
+ s->bulk_completed = false;
+ s->prev_bs = NULL;
+ s->prev_bitmap = NULL;
+ s->no_bitmaps = false;
/*
* Use blockdevice name for direct (or filtered) children of named block
@@ -370,7 +389,7 @@ static int init_dirty_bitmap_migration(void)
}
if (bs && bs->drv && !bs->drv->is_filter) {
- if (add_bitmaps_to_list(bs, name)) {
+ if (add_bitmaps_to_list(s, bs, name)) {
goto fail;
}
g_hash_table_add(handled_by_blk, bs);
@@ -382,18 +401,18 @@ static int init_dirty_bitmap_migration(void)
continue;
}
- if (add_bitmaps_to_list(bs, bdrv_get_node_name(bs))) {
+ if (add_bitmaps_to_list(s, bs, bdrv_get_node_name(bs))) {
goto fail;
}
}
/* unset migration flags here, to not roll back it */
- QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) {
bdrv_dirty_bitmap_skip_store(dbms->bitmap, true);
}
- if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) {
- dirty_bitmap_mig_state.no_bitmaps = true;
+ if (QSIMPLEQ_EMPTY(&s->dbms_list)) {
+ s->no_bitmaps = true;
}
g_hash_table_destroy(handled_by_blk);
@@ -402,18 +421,19 @@ static int init_dirty_bitmap_migration(void)
fail:
g_hash_table_destroy(handled_by_blk);
- dirty_bitmap_mig_cleanup();
+ dirty_bitmap_do_save_cleanup(s);
return -1;
}
/* Called with no lock taken. */
-static void bulk_phase_send_chunk(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
+static void bulk_phase_send_chunk(QEMUFile *f, DBMSaveState *s,
+ SaveBitmapState *dbms)
{
uint32_t nr_sectors = MIN(dbms->total_sectors - dbms->cur_sector,
dbms->sectors_per_chunk);
- send_bitmap_bits(f, dbms, dbms->cur_sector, nr_sectors);
+ send_bitmap_bits(f, s, dbms, dbms->cur_sector, nr_sectors);
dbms->cur_sector += nr_sectors;
if (dbms->cur_sector >= dbms->total_sectors) {
@@ -422,61 +442,66 @@ static void bulk_phase_send_chunk(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
}
/* Called with no lock taken. */
-static void bulk_phase(QEMUFile *f, bool limit)
+static void bulk_phase(QEMUFile *f, DBMSaveState *s, bool limit)
{
- DirtyBitmapMigBitmapState *dbms;
+ SaveBitmapState *dbms;
- QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) {
while (!dbms->bulk_completed) {
- bulk_phase_send_chunk(f, dbms);
+ bulk_phase_send_chunk(f, s, dbms);
if (limit && qemu_file_rate_limit(f)) {
return;
}
}
}
- dirty_bitmap_mig_state.bulk_completed = true;
+ s->bulk_completed = true;
}
/* for SaveVMHandlers */
static void dirty_bitmap_save_cleanup(void *opaque)
{
- dirty_bitmap_mig_cleanup();
+ DBMSaveState *s = &((DBMState *)opaque)->save;
+
+ dirty_bitmap_do_save_cleanup(s);
}
static int dirty_bitmap_save_iterate(QEMUFile *f, void *opaque)
{
+ DBMSaveState *s = &((DBMState *)opaque)->save;
+
trace_dirty_bitmap_save_iterate(migration_in_postcopy());
- if (migration_in_postcopy() && !dirty_bitmap_mig_state.bulk_completed) {
- bulk_phase(f, true);
+ if (migration_in_postcopy() && !s->bulk_completed) {
+ bulk_phase(f, s, true);
}
qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
- return dirty_bitmap_mig_state.bulk_completed;
+ return s->bulk_completed;
}
/* Called with iothread lock taken. */
static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque)
{
- DirtyBitmapMigBitmapState *dbms;
+ DBMSaveState *s = &((DBMState *)opaque)->save;
+ SaveBitmapState *dbms;
trace_dirty_bitmap_save_complete_enter();
- if (!dirty_bitmap_mig_state.bulk_completed) {
- bulk_phase(f, false);
+ if (!s->bulk_completed) {
+ bulk_phase(f, s, false);
}
- QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
- send_bitmap_complete(f, dbms);
+ QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) {
+ send_bitmap_complete(f, s, dbms);
}
qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
trace_dirty_bitmap_save_complete_finish();
- dirty_bitmap_mig_cleanup();
+ dirty_bitmap_save_cleanup(opaque);
return 0;
}
@@ -486,12 +511,13 @@ static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque,
uint64_t *res_compatible,
uint64_t *res_postcopy_only)
{
- DirtyBitmapMigBitmapState *dbms;
+ DBMSaveState *s = &((DBMState *)opaque)->save;
+ SaveBitmapState *dbms;
uint64_t pending = 0;
qemu_mutex_lock_iothread();
- QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) {
uint64_t gran = bdrv_dirty_bitmap_granularity(dbms->bitmap);
uint64_t sectors = dbms->bulk_completed ? 0 :
dbms->total_sectors - dbms->cur_sector;
@@ -507,11 +533,16 @@ static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque,
}
/* First occurrence of this bitmap. It should be created if doesn't exist */
-static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s)
+static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s)
{
Error *local_err = NULL;
uint32_t granularity = qemu_get_be32(f);
uint8_t flags = qemu_get_byte(f);
+ LoadBitmapState *b;
+
+ if (s->cancelled) {
+ return 0;
+ }
if (s->bitmap) {
error_report("Bitmap with the same name ('%s') already exists on "
@@ -538,90 +569,140 @@ static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s)
bdrv_disable_dirty_bitmap(s->bitmap);
if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) {
- DirtyBitmapLoadBitmapState *b;
-
bdrv_dirty_bitmap_create_successor(s->bitmap, &local_err);
if (local_err) {
error_report_err(local_err);
return -EINVAL;
}
-
- b = g_new(DirtyBitmapLoadBitmapState, 1);
- b->bs = s->bs;
- b->bitmap = s->bitmap;
- b->migrated = false;
- enabled_bitmaps = g_slist_prepend(enabled_bitmaps, b);
}
+ b = g_new(LoadBitmapState, 1);
+ b->bs = s->bs;
+ b->bitmap = s->bitmap;
+ b->migrated = false;
+ b->enabled = flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED;
+
+ s->bitmaps = g_slist_prepend(s->bitmaps, b);
+
return 0;
}
-void dirty_bitmap_mig_before_vm_start(void)
+/*
+ * before_vm_start_handle_item
+ *
+ * g_slist_foreach helper
+ *
+ * item is LoadBitmapState*
+ * opaque is DBMLoadState*
+ */
+static void before_vm_start_handle_item(void *item, void *opaque)
{
- GSList *item;
-
- qemu_mutex_lock(&finish_lock);
-
- for (item = enabled_bitmaps; item; item = g_slist_next(item)) {
- DirtyBitmapLoadBitmapState *b = item->data;
+ DBMLoadState *s = opaque;
+ LoadBitmapState *b = item;
+ if (b->enabled) {
if (b->migrated) {
- bdrv_enable_dirty_bitmap_locked(b->bitmap);
+ bdrv_enable_dirty_bitmap(b->bitmap);
} else {
bdrv_dirty_bitmap_enable_successor(b->bitmap);
}
+ }
+ if (b->migrated) {
+ s->bitmaps = g_slist_remove(s->bitmaps, b);
g_free(b);
}
+}
+
+void dirty_bitmap_mig_before_vm_start(void)
+{
+ DBMLoadState *s = &dbm_state.load;
+ qemu_mutex_lock(&s->lock);
+
+ assert(!s->before_vm_start_handled);
+ g_slist_foreach(s->bitmaps, before_vm_start_handle_item, s);
+ s->before_vm_start_handled = true;
+
+ qemu_mutex_unlock(&s->lock);
+}
+
+static void cancel_incoming_locked(DBMLoadState *s)
+{
+ GSList *item;
+
+ if (s->cancelled) {
+ return;
+ }
+
+ s->cancelled = true;
+ s->bs = NULL;
+ s->bitmap = NULL;
+
+ /* Drop all unfinished bitmaps */
+ for (item = s->bitmaps; item; item = g_slist_next(item)) {
+ LoadBitmapState *b = item->data;
+
+ /*
+ * Bitmap must be unfinished, as finished bitmaps should already be
+ * removed from the list.
+ */
+ assert(!s->before_vm_start_handled || !b->migrated);
+ if (bdrv_dirty_bitmap_has_successor(b->bitmap)) {
+ bdrv_reclaim_dirty_bitmap(b->bitmap, &error_abort);
+ }
+ bdrv_release_dirty_bitmap(b->bitmap);
+ }
+
+ g_slist_free_full(s->bitmaps, g_free);
+ s->bitmaps = NULL;
+}
+
+void dirty_bitmap_mig_cancel_outgoing(void)
+{
+ dirty_bitmap_do_save_cleanup(&dbm_state.save);
+}
- g_slist_free(enabled_bitmaps);
- enabled_bitmaps = NULL;
+void dirty_bitmap_mig_cancel_incoming(void)
+{
+ DBMLoadState *s = &dbm_state.load;
+
+ qemu_mutex_lock(&s->lock);
- qemu_mutex_unlock(&finish_lock);
+ cancel_incoming_locked(s);
+
+ qemu_mutex_unlock(&s->lock);
}
-static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s)
+static void dirty_bitmap_load_complete(QEMUFile *f, DBMLoadState *s)
{
GSList *item;
trace_dirty_bitmap_load_complete();
+
+ if (s->cancelled) {
+ return;
+ }
+
bdrv_dirty_bitmap_deserialize_finish(s->bitmap);
- qemu_mutex_lock(&finish_lock);
+ if (bdrv_dirty_bitmap_has_successor(s->bitmap)) {
+ bdrv_reclaim_dirty_bitmap(s->bitmap, &error_abort);
+ }
- for (item = enabled_bitmaps; item; item = g_slist_next(item)) {
- DirtyBitmapLoadBitmapState *b = item->data;
+ for (item = s->bitmaps; item; item = g_slist_next(item)) {
+ LoadBitmapState *b = item->data;
if (b->bitmap == s->bitmap) {
b->migrated = true;
+ if (s->before_vm_start_handled) {
+ s->bitmaps = g_slist_remove(s->bitmaps, b);
+ g_free(b);
+ }
break;
}
}
-
- if (bdrv_dirty_bitmap_has_successor(s->bitmap)) {
- bdrv_dirty_bitmap_lock(s->bitmap);
- if (enabled_bitmaps == NULL) {
- /* in postcopy */
- bdrv_reclaim_dirty_bitmap_locked(s->bitmap, &error_abort);
- bdrv_enable_dirty_bitmap_locked(s->bitmap);
- } else {
- /* target not started, successor must be empty */
- int64_t count = bdrv_get_dirty_count(s->bitmap);
- BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bitmap,
- NULL);
- /* bdrv_reclaim_dirty_bitmap can fail only on no successor (it
- * must be) or on merge fail, but merge can't fail when second
- * bitmap is empty
- */
- assert(ret == s->bitmap &&
- count == bdrv_get_dirty_count(s->bitmap));
- }
- bdrv_dirty_bitmap_unlock(s->bitmap);
- }
-
- qemu_mutex_unlock(&finish_lock);
}
-static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s)
+static int dirty_bitmap_load_bits(QEMUFile *f, DBMLoadState *s)
{
uint64_t first_byte = qemu_get_be64(f) << BDRV_SECTOR_BITS;
uint64_t nr_bytes = (uint64_t)qemu_get_be32(f) << BDRV_SECTOR_BITS;
@@ -630,15 +711,46 @@ static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s)
if (s->flags & DIRTY_BITMAP_MIG_FLAG_ZEROES) {
trace_dirty_bitmap_load_bits_zeroes();
- bdrv_dirty_bitmap_deserialize_zeroes(s->bitmap, first_byte, nr_bytes,
- false);
+ if (!s->cancelled) {
+ bdrv_dirty_bitmap_deserialize_zeroes(s->bitmap, first_byte,
+ nr_bytes, false);
+ }
} else {
size_t ret;
- uint8_t *buf;
+ g_autofree uint8_t *buf = NULL;
uint64_t buf_size = qemu_get_be64(f);
- uint64_t needed_size =
- bdrv_dirty_bitmap_serialization_size(s->bitmap,
- first_byte, nr_bytes);
+ uint64_t needed_size;
+
+ /*
+ * The actual check for buf_size is done a bit later. We can't do it in
+ * cancelled mode as we don't have the bitmap to check the constraints
+ * (so, we allocate a buffer and read prior to the check). On the other
+ * hand, we shouldn't blindly g_malloc the number from the stream.
+ * Actually one chunk should not be larger than CHUNK_SIZE. Let's allow
+ * a bit larger (which means that bitmap migration will fail anyway and
+ * the whole migration will most probably fail soon due to broken
+ * stream).
+ */
+ if (buf_size > 10 * CHUNK_SIZE) {
+ error_report("Bitmap migration stream buffer allocation request "
+ "is too large");
+ return -EIO;
+ }
+
+ buf = g_malloc(buf_size);
+ ret = qemu_get_buffer(f, buf, buf_size);
+ if (ret != buf_size) {
+ error_report("Failed to read bitmap bits");
+ return -EIO;
+ }
+
+ if (s->cancelled) {
+ return 0;
+ }
+
+ needed_size = bdrv_dirty_bitmap_serialization_size(s->bitmap,
+ first_byte,
+ nr_bytes);
if (needed_size > buf_size ||
buf_size > QEMU_ALIGN_UP(needed_size, 4 * sizeof(long))
@@ -647,26 +759,18 @@ static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s)
error_report("Migrated bitmap granularity doesn't "
"match the destination bitmap '%s' granularity",
bdrv_dirty_bitmap_name(s->bitmap));
- return -EINVAL;
- }
-
- buf = g_malloc(buf_size);
- ret = qemu_get_buffer(f, buf, buf_size);
- if (ret != buf_size) {
- error_report("Failed to read bitmap bits");
- g_free(buf);
- return -EIO;
+ cancel_incoming_locked(s);
+ return 0;
}
bdrv_dirty_bitmap_deserialize_part(s->bitmap, buf, first_byte, nr_bytes,
false);
- g_free(buf);
}
return 0;
}
-static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s)
+static int dirty_bitmap_load_header(QEMUFile *f, DBMLoadState *s)
{
Error *local_err = NULL;
bool nothing;
@@ -680,14 +784,16 @@ static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s)
error_report("Unable to read node name string");
return -EINVAL;
}
- s->bs = bdrv_lookup_bs(s->node_name, s->node_name, &local_err);
- if (!s->bs) {
- error_report_err(local_err);
- return -EINVAL;
+ if (!s->cancelled) {
+ s->bs = bdrv_lookup_bs(s->node_name, s->node_name, &local_err);
+ if (!s->bs) {
+ error_report_err(local_err);
+ cancel_incoming_locked(s);
+ }
}
- } else if (!s->bs && !nothing) {
+ } else if (!s->bs && !nothing && !s->cancelled) {
error_report("Error: block device name is not set");
- return -EINVAL;
+ cancel_incoming_locked(s);
}
if (s->flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) {
@@ -695,47 +801,66 @@ static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s)
error_report("Unable to read bitmap name string");
return -EINVAL;
}
- s->bitmap = bdrv_find_dirty_bitmap(s->bs, s->bitmap_name);
-
- /* bitmap may be NULL here, it wouldn't be an error if it is the
- * first occurrence of the bitmap */
- if (!s->bitmap && !(s->flags & DIRTY_BITMAP_MIG_FLAG_START)) {
- error_report("Error: unknown dirty bitmap "
- "'%s' for block device '%s'",
- s->bitmap_name, s->node_name);
- return -EINVAL;
+ if (!s->cancelled) {
+ s->bitmap = bdrv_find_dirty_bitmap(s->bs, s->bitmap_name);
+
+ /*
+ * bitmap may be NULL here, it wouldn't be an error if it is the
+ * first occurrence of the bitmap
+ */
+ if (!s->bitmap && !(s->flags & DIRTY_BITMAP_MIG_FLAG_START)) {
+ error_report("Error: unknown dirty bitmap "
+ "'%s' for block device '%s'",
+ s->bitmap_name, s->node_name);
+ cancel_incoming_locked(s);
+ }
}
- } else if (!s->bitmap && !nothing) {
+ } else if (!s->bitmap && !nothing && !s->cancelled) {
error_report("Error: block device name is not set");
- return -EINVAL;
+ cancel_incoming_locked(s);
}
return 0;
}
+/*
+ * dirty_bitmap_load
+ *
+ * Load sequence of dirty bitmap chunks. Return error only on fatal io stream
+ * violations. On other errors just cancel bitmaps incoming migration and return
+ * 0.
+ *
+ * Note, than when incoming bitmap migration is canceled, we still must read all
+ * our chunks (and just ignore them), to not affect other migration objects.
+ */
static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id)
{
- static DirtyBitmapLoadState s;
+ DBMLoadState *s = &((DBMState *)opaque)->load;
int ret = 0;
trace_dirty_bitmap_load_enter();
if (version_id != 1) {
+ QEMU_LOCK_GUARD(&s->lock);
+ cancel_incoming_locked(s);
return -EINVAL;
}
do {
- ret = dirty_bitmap_load_header(f, &s);
+ QEMU_LOCK_GUARD(&s->lock);
+
+ ret = dirty_bitmap_load_header(f, s);
if (ret < 0) {
+ cancel_incoming_locked(s);
return ret;
}
- if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) {
- ret = dirty_bitmap_load_start(f, &s);
- } else if (s.flags & DIRTY_BITMAP_MIG_FLAG_COMPLETE) {
- dirty_bitmap_load_complete(f, &s);
- } else if (s.flags & DIRTY_BITMAP_MIG_FLAG_BITS) {
- ret = dirty_bitmap_load_bits(f, &s);
+ if (s->flags & DIRTY_BITMAP_MIG_FLAG_START) {
+ ret = dirty_bitmap_load_start(f, s);
+ } else if (s->flags & DIRTY_BITMAP_MIG_FLAG_COMPLETE) {
+ dirty_bitmap_load_complete(f, s);
+ } else if (s->flags & DIRTY_BITMAP_MIG_FLAG_BITS) {
+ ret = dirty_bitmap_load_bits(f, s);
}
if (!ret) {
@@ -743,9 +868,10 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id)
}
if (ret) {
+ cancel_incoming_locked(s);
return ret;
}
- } while (!(s.flags & DIRTY_BITMAP_MIG_FLAG_EOS));
+ } while (!(s->flags & DIRTY_BITMAP_MIG_FLAG_EOS));
trace_dirty_bitmap_load_success();
return 0;
@@ -753,13 +879,14 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id)
static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque)
{
- DirtyBitmapMigBitmapState *dbms = NULL;
- if (init_dirty_bitmap_migration() < 0) {
+ DBMSaveState *s = &((DBMState *)opaque)->save;
+ SaveBitmapState *dbms = NULL;
+ if (init_dirty_bitmap_migration(s) < 0) {
return -1;
}
- QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
- send_bitmap_start(f, dbms);
+ QSIMPLEQ_FOREACH(dbms, &s->dbms_list, entry) {
+ send_bitmap_start(f, s, dbms);
}
qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
@@ -768,7 +895,9 @@ static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque)
static bool dirty_bitmap_is_active(void *opaque)
{
- return migrate_dirty_bitmaps() && !dirty_bitmap_mig_state.no_bitmaps;
+ DBMSaveState *s = &((DBMState *)opaque)->save;
+
+ return migrate_dirty_bitmaps() && !s->no_bitmaps;
}
static bool dirty_bitmap_is_active_iterate(void *opaque)
@@ -796,9 +925,10 @@ static SaveVMHandlers savevm_dirty_bitmap_handlers = {
void dirty_bitmap_mig_init(void)
{
- QSIMPLEQ_INIT(&dirty_bitmap_mig_state.dbms_list);
+ QSIMPLEQ_INIT(&dbm_state.save.dbms_list);
+ qemu_mutex_init(&dbm_state.load.lock);
register_savevm_live("dirty-bitmap", 0, 1,
&savevm_dirty_bitmap_handlers,
- &dirty_bitmap_mig_state);
+ &dbm_state);
}
diff --git a/migration/migration.c b/migration/migration.c
index 2ed9923227..8fe36339db 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -165,8 +165,6 @@ void migration_object_init(void)
qemu_sem_init(&current_incoming->postcopy_pause_sem_dst, 0);
qemu_sem_init(&current_incoming->postcopy_pause_sem_fault, 0);
- init_dirty_bitmap_incoming_migration();
-
if (!migration_object_check(current_migration, &err)) {
error_report_err(err);
exit(1);
@@ -190,6 +188,19 @@ void migration_shutdown(void)
*/
migrate_fd_cancel(current_migration);
object_unref(OBJECT(current_migration));
+
+ /*
+ * Cancel outgoing migration of dirty bitmaps. It should
+ * at least unref used block nodes.
+ */
+ dirty_bitmap_mig_cancel_outgoing();
+
+ /*
+ * Cancel incoming migration of dirty bitmaps. Dirty bitmaps
+ * are non-critical data, and their loss never considered as
+ * something serious.
+ */
+ dirty_bitmap_mig_cancel_incoming();
}
/* For outgoing */
diff --git a/migration/migration.h b/migration/migration.h
index f617960522..6c6a931d0d 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -335,7 +335,8 @@ void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis,
void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value);
void dirty_bitmap_mig_before_vm_start(void);
-void init_dirty_bitmap_incoming_migration(void);
+void dirty_bitmap_mig_cancel_outgoing(void);
+void dirty_bitmap_mig_cancel_incoming(void);
void migrate_add_address(SocketAddress *address);
int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque);
diff --git a/migration/savevm.c b/migration/savevm.c
index 45c9dd9d8a..a843d202b5 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1813,6 +1813,9 @@ static void *postcopy_ram_listen_thread(void *opaque)
MigrationIncomingState *mis = migration_incoming_get_current();
QEMUFile *f = mis->from_src_file;
int load_res;
+ MigrationState *migr = migrate_get_current();
+
+ object_ref(OBJECT(migr));
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_POSTCOPY_ACTIVE);
@@ -1839,11 +1842,24 @@ static void *postcopy_ram_listen_thread(void *opaque)
trace_postcopy_ram_listen_thread_exit();
if (load_res < 0) {
- error_report("%s: loadvm failed: %d", __func__, load_res);
qemu_file_set_error(f, load_res);
- migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
- MIGRATION_STATUS_FAILED);
- } else {
+ dirty_bitmap_mig_cancel_incoming();
+ if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
+ !migrate_postcopy_ram() && migrate_dirty_bitmaps())
+ {
+ error_report("%s: loadvm failed during postcopy: %d. All states "
+ "are migrated except dirty bitmaps. Some dirty "
+ "bitmaps may be lost, and present migrated dirty "
+ "bitmaps are correctly migrated and valid.",
+ __func__, load_res);
+ load_res = 0; /* prevent further exit() */
+ } else {
+ error_report("%s: loadvm failed: %d", __func__, load_res);
+ migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
+ MIGRATION_STATUS_FAILED);
+ }
+ }
+ if (load_res >= 0) {
/*
* This looks good, but it's possible that the device loading in the
* main thread hasn't finished yet, and so we might not be in 'RUN'
@@ -1879,6 +1895,8 @@ static void *postcopy_ram_listen_thread(void *opaque)
mis->have_listen_thread = false;
postcopy_state_set(POSTCOPY_INCOMING_END);
+ object_unref(OBJECT(migr));
+
return NULL;
}
@@ -2437,6 +2455,8 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis)
{
trace_postcopy_pause_incoming();
+ assert(migrate_postcopy_ram());
+
/* Clear the triggered bit to allow one recovery */
mis->postcopy_recover_triggered = false;
@@ -2521,15 +2541,22 @@ out:
if (ret < 0) {
qemu_file_set_error(f, ret);
+ /* Cancel bitmaps incoming regardless of recovery */
+ dirty_bitmap_mig_cancel_incoming();
+
/*
* If we are during an active postcopy, then we pause instead
* of bail out to at least keep the VM's dirty data. Note
* that POSTCOPY_INCOMING_LISTENING stage is still not enough,
* during which we're still receiving device states and we
* still haven't yet started the VM on destination.
+ *
+ * Only RAM postcopy supports recovery. Still, if RAM postcopy is
+ * enabled, canceled bitmaps postcopy will not affect RAM postcopy
+ * recovering.
*/
if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING &&
- postcopy_pause_incoming(mis)) {
+ migrate_postcopy_ram() && postcopy_pause_incoming(mis)) {
/* Reset f to point to the newly created channel */
f = mis->from_src_file;
goto retry;
diff --git a/net/queue.c b/net/queue.c
index 0164727e39..19e32c80fd 100644
--- a/net/queue.c
+++ b/net/queue.c
@@ -250,6 +250,9 @@ void qemu_net_queue_purge(NetQueue *queue, NetClientState *from)
bool qemu_net_queue_flush(NetQueue *queue)
{
+ if (queue->delivering)
+ return false;
+
while (!QTAILQ_EMPTY(&queue->packets)) {
NetPacket *packet;
int ret;
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index aaa71f147b..15c9d7944b 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -958,11 +958,13 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
{
DWORD info_size;
char mnt, *mnt_point;
+ wchar_t wfs_name[32];
char fs_name[32];
- char vol_info[MAX_PATH+1];
+ wchar_t vol_info[MAX_PATH + 1];
size_t len;
uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes;
GuestFilesystemInfo *fs = NULL;
+ HANDLE hLocalDiskHandle = NULL;
GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
if (GetLastError() != ERROR_MORE_DATA) {
@@ -977,18 +979,27 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
goto free;
}
+ hLocalDiskHandle = CreateFile(guid, 0 , 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL |
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (INVALID_HANDLE_VALUE == hLocalDiskHandle) {
+ error_setg_win32(errp, GetLastError(), "failed to get handle for volume");
+ goto free;
+ }
+
len = strlen(mnt_point);
mnt_point[len] = '\\';
mnt_point[len+1] = 0;
- if (!GetVolumeInformation(mnt_point, vol_info, sizeof(vol_info), NULL, NULL,
- NULL, (LPSTR)&fs_name, sizeof(fs_name))) {
+
+ if (!GetVolumeInformationByHandleW(hLocalDiskHandle, vol_info,
+ sizeof(vol_info), NULL, NULL, NULL,
+ (LPWSTR) & wfs_name, sizeof(wfs_name))) {
if (GetLastError() != ERROR_NOT_READY) {
error_setg_win32(errp, GetLastError(), "failed to get volume info");
}
goto free;
}
- fs_name[sizeof(fs_name) - 1] = 0;
fs = g_malloc(sizeof(*fs));
fs->name = g_strdup(guid);
fs->has_total_bytes = false;
@@ -1007,9 +1018,11 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
fs->has_used_bytes = true;
}
}
+ wcstombs(fs_name, wfs_name, sizeof(wfs_name));
fs->type = g_strdup(fs_name);
fs->disk = build_guest_disk_info(guid, errp);
free:
+ CloseHandle(hLocalDiskHandle);
g_free(mnt_point);
return fs;
}
@@ -1027,8 +1040,12 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
}
do {
- GuestFilesystemInfo *info = build_guest_fsinfo(guid, errp);
- if (info == NULL) {
+ Error *local_err = NULL;
+ GuestFilesystemInfo *info = build_guest_fsinfo(guid, &local_err);
+ if (local_err) {
+ g_debug("failed to get filesystem info, ignoring error: %s",
+ error_get_pretty(local_err));
+ error_free(local_err);
continue;
}
new = g_malloc(sizeof(*ret));
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 4be9aad48e..408a662ea5 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -846,7 +846,7 @@
##
# @GuestDiskAddress:
#
-# @pci-controller: controller's PCI address
+# @pci-controller: controller's PCI address (fields are set to -1 if invalid)
# @bus-type: bus type
# @bus: bus id
# @target: target id
diff --git a/slirp b/slirp
-Subproject 2faae0f778f818fadc873308f983289df697eb9
+Subproject ce94eba2042d52a0ba3d9e252ebce86715e9427
diff --git a/target/arm/helper.c b/target/arm/helper.c
index c69a2baf1d..8ef0fb478f 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -10204,21 +10204,11 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx,
int s2prot;
int ret;
ARMCacheAttrs cacheattrs = {};
- ARMCacheAttrs *pcacheattrs = NULL;
-
- if (env->cp15.hcr_el2 & HCR_PTW) {
- /*
- * PTW means we must fault if this S1 walk touches S2 Device
- * memory; otherwise we don't care about the attributes and can
- * save the S2 translation the effort of computing them.
- */
- pcacheattrs = &cacheattrs;
- }
ret = get_phys_addr_lpae(env, addr, MMU_DATA_LOAD, ARMMMUIdx_Stage2,
false,
&s2pa, &txattrs, &s2prot, &s2size, fi,
- pcacheattrs);
+ &cacheattrs);
if (ret) {
assert(fi->type != ARMFault_None);
fi->s2addr = addr;
@@ -10226,8 +10216,11 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx,
fi->s1ptw = true;
return ~0;
}
- if (pcacheattrs && (pcacheattrs->attrs & 0xf0) == 0) {
- /* Access was to Device memory: generate Permission fault */
+ if ((env->cp15.hcr_el2 & HCR_PTW) && (cacheattrs.attrs & 0xf0) == 0) {
+ /*
+ * PTW set and S1 walk touched S2 Device memory:
+ * generate Permission fault.
+ */
fi->type = ARMFault_Permission;
fi->s2addr = addr;
fi->stage2 = true;
diff --git a/target/arm/mte_helper.c b/target/arm/mte_helper.c
index 5ea57d487a..104752041f 100644
--- a/target/arm/mte_helper.c
+++ b/target/arm/mte_helper.c
@@ -24,6 +24,8 @@
#include "exec/ram_addr.h"
#include "exec/cpu_ldst.h"
#include "exec/helper-proto.h"
+#include "qapi/error.h"
+#include "qemu/guest-random.h"
static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude)
@@ -211,16 +213,37 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
uint64_t HELPER(irg)(CPUARMState *env, uint64_t rn, uint64_t rm)
{
- int rtag;
-
- /*
- * Our IMPDEF choice for GCR_EL1.RRND==1 is to behave as if
- * GCR_EL1.RRND==0, always producing deterministic results.
- */
uint16_t exclude = extract32(rm | env->cp15.gcr_el1, 0, 16);
+ int rrnd = extract32(env->cp15.gcr_el1, 16, 1);
int start = extract32(env->cp15.rgsr_el1, 0, 4);
int seed = extract32(env->cp15.rgsr_el1, 8, 16);
- int offset, i;
+ int offset, i, rtag;
+
+ /*
+ * Our IMPDEF choice for GCR_EL1.RRND==1 is to continue to use the
+ * deterministic algorithm. Except that with RRND==1 the kernel is
+ * not required to have set RGSR_EL1.SEED != 0, which is required for
+ * the deterministic algorithm to function. So we force a non-zero
+ * SEED for that case.
+ */
+ if (unlikely(seed == 0) && rrnd) {
+ do {
+ Error *err = NULL;
+ uint16_t two;
+
+ if (qemu_guest_getrandom(&two, sizeof(two), &err) < 0) {
+ /*
+ * Failed, for unknown reasons in the crypto subsystem.
+ * Best we can do is log the reason and use a constant seed.
+ */
+ qemu_log_mask(LOG_UNIMP, "IRG: Crypto failure: %s\n",
+ error_get_pretty(err));
+ error_free(err);
+ two = 1;
+ }
+ seed = two;
+ } while (seed == 0);
+ }
/* RandomTag */
for (i = offset = 0; i < 4; ++i) {
diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028
index 5d043cef92..6dd3ae09a3 100755
--- a/tests/qemu-iotests/028
+++ b/tests/qemu-iotests/028
@@ -142,6 +142,25 @@ TEST_IMG="${TEST_IMG}.copy" io_zero readv $(( offset + 32 * 1024 )) 512 1024 32
_check_test_img
+echo
+echo '=== Reading across backing EOF in one operation ==='
+echo
+
+# Use a cluster boundary as the base end here
+base_size=$((3 * 1024 * 1024 * 1024))
+
+TEST_IMG="$TEST_IMG.base" _make_test_img $base_size
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $image_size
+
+# Write 16 times 42 at the end of the base image
+$QEMU_IO -c "write -P 42 $((base_size - 16)) 16" "$TEST_IMG.base" \
+ | _filter_qemu_io
+
+# Read 32 bytes across the base EOF from the top;
+# should be 16 times 0x2a, then 16 times 0x00
+$QEMU_IO -c "read -v $((base_size - 16)) 32" "$TEST_IMG" \
+ | _filter_qemu_io
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out
index 12f82c6a6c..5a68de5c46 100644
--- a/tests/qemu-iotests/028.out
+++ b/tests/qemu-iotests/028.out
@@ -730,4 +730,15 @@ read 512/512 bytes at offset 3221257728
read 512/512 bytes at offset 3221258752
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
+
+=== Reading across backing EOF in one operation ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=3221225472
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+wrote 16/16 bytes at offset 3221225456
+16 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+bffffff0: 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a ................
+c0000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+read 32/32 bytes at offset 3221225456
+32 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
index 121959a09c..a161c89816 100755
--- a/tests/qemu-iotests/197
+++ b/tests/qemu-iotests/197
@@ -114,9 +114,11 @@ echo
# Force compat=1.1, because writing zeroes on a v2 image without a
# backing file would just result in an unallocated cluster
-_make_test_img -o compat=1.1 1024
-$QEMU_IO -f $IMGFMT -C -c 'read 0 1024' "$TEST_IMG" | _filter_qemu_io
-$QEMU_IO -f $IMGFMT -c map "$TEST_IMG"
+# (Also, note that this is really a pure qcow2 test.)
+IMGPROTO=file IMGFMT=qcow2 TEST_IMG_FILE="$TEST_WRAP" \
+ _make_test_img --no-opts -o compat=1.1 1024
+$QEMU_IO -f qcow2 -C -c 'read 0 1024' "$TEST_WRAP" | _filter_qemu_io
+$QEMU_IO -f qcow2 -c map "$TEST_WRAP"
_check_test_img
# success, all done
diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out
index 7ca46be6e4..ad414c3b0e 100644
--- a/tests/qemu-iotests/197.out
+++ b/tests/qemu-iotests/197.out
@@ -26,7 +26,7 @@ Images are identical.
=== Partial final cluster ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024
+Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=1024
read 1024/1024 bytes at offset 0
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
1 KiB (0x400) bytes allocated at offset 0 bytes (0x0)
diff --git a/tests/qemu-iotests/199 b/tests/qemu-iotests/199
index 40774eed74..58fad872a1 100755
--- a/tests/qemu-iotests/199
+++ b/tests/qemu-iotests/199
@@ -20,17 +20,76 @@
import os
import iotests
-import time
from iotests import qemu_img
+debug = False
+
disk_a = os.path.join(iotests.test_dir, 'disk_a')
disk_b = os.path.join(iotests.test_dir, 'disk_b')
size = '256G'
fifo = os.path.join(iotests.test_dir, 'mig_fifo')
-class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
+granularity = 512
+nb_bitmaps = 15
+
+GiB = 1024 * 1024 * 1024
+
+discards1 = (
+ (0, GiB),
+ (2 * GiB + 512 * 5, 512),
+ (3 * GiB + 512 * 5, 512),
+ (100 * GiB, GiB)
+)
+
+discards2 = (
+ (3 * GiB + 512 * 8, 512),
+ (4 * GiB + 512 * 8, 512),
+ (50 * GiB, GiB),
+ (100 * GiB + GiB // 2, GiB)
+)
+
+
+def apply_discards(vm, discards):
+ for d in discards:
+ vm.hmp_qemu_io('drive0', 'discard {} {}'.format(*d))
+
+
+def event_seconds(event):
+ return event['timestamp']['seconds'] + \
+ event['timestamp']['microseconds'] / 1000000.0
+
+
+def event_dist(e1, e2):
+ return event_seconds(e2) - event_seconds(e1)
+
+
+def check_bitmaps(vm, count):
+ result = vm.qmp('query-block')
+
+ if count == 0:
+ assert 'dirty-bitmaps' not in result['return'][0]
+ else:
+ assert len(result['return'][0]['dirty-bitmaps']) == count
+
+class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
def tearDown(self):
+ if debug:
+ self.vm_a_events += self.vm_a.get_qmp_events()
+ self.vm_b_events += self.vm_b.get_qmp_events()
+ for e in self.vm_a_events:
+ e['vm'] = 'SRC'
+ for e in self.vm_b_events:
+ e['vm'] = 'DST'
+ events = (self.vm_a_events + self.vm_b_events)
+ events = [(e['timestamp']['seconds'],
+ e['timestamp']['microseconds'],
+ e['vm'],
+ e['event'],
+ e.get('data', '')) for e in events]
+ for e in sorted(events):
+ print('{}.{:06} {} {} {}'.format(*e))
+
self.vm_a.shutdown()
self.vm_b.shutdown()
os.remove(disk_a)
@@ -41,51 +100,66 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
os.mkfifo(fifo)
qemu_img('create', '-f', iotests.imgfmt, disk_a, size)
qemu_img('create', '-f', iotests.imgfmt, disk_b, size)
- self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a)
- self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
+ self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a,
+ 'discard=unmap')
+ self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b,
+ 'discard=unmap')
self.vm_b.add_incoming("exec: cat '" + fifo + "'")
self.vm_a.launch()
self.vm_b.launch()
- def test_postcopy(self):
- write_size = 0x40000000
- granularity = 512
- chunk = 4096
+ # collect received events for debug
+ self.vm_a_events = []
+ self.vm_b_events = []
- result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0',
- name='bitmap', granularity=granularity)
- self.assert_qmp(result, 'return', {});
+ def start_postcopy(self):
+ """ Run migration until RESUME event on target. Return this event. """
+ for i in range(nb_bitmaps):
+ result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0',
+ name='bitmap{}'.format(i),
+ granularity=granularity,
+ persistent=True)
+ self.assert_qmp(result, 'return', {})
- s = 0
- while s < write_size:
- self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
- s += 0x10000
- s = 0x8000
- while s < write_size:
- self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
- s += 0x10000
+ result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap0')
+ empty_sha256 = result['return']['sha256']
+
+ apply_discards(self.vm_a, discards1)
result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
- node='drive0', name='bitmap')
- sha256 = result['return']['sha256']
-
- result = self.vm_a.qmp('block-dirty-bitmap-clear', node='drive0',
- name='bitmap')
- self.assert_qmp(result, 'return', {});
- s = 0
- while s < write_size:
- self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
- s += 0x10000
-
- bitmaps_cap = {'capability': 'dirty-bitmaps', 'state': True}
- events_cap = {'capability': 'events', 'state': True}
-
- result = self.vm_a.qmp('migrate-set-capabilities',
- capabilities=[bitmaps_cap, events_cap])
+ node='drive0', name='bitmap0')
+ self.discards1_sha256 = result['return']['sha256']
+
+ # Check, that updating the bitmap by discards works
+ assert self.discards1_sha256 != empty_sha256
+
+ # We want to calculate resulting sha256. Do it in bitmap0, so, disable
+ # other bitmaps
+ for i in range(1, nb_bitmaps):
+ result = self.vm_a.qmp('block-dirty-bitmap-disable', node='drive0',
+ name='bitmap{}'.format(i))
+ self.assert_qmp(result, 'return', {})
+
+ apply_discards(self.vm_a, discards2)
+
+ result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap0')
+ self.all_discards_sha256 = result['return']['sha256']
+
+ # Now, enable some bitmaps, to be updated during migration
+ for i in range(2, nb_bitmaps, 2):
+ result = self.vm_a.qmp('block-dirty-bitmap-enable', node='drive0',
+ name='bitmap{}'.format(i))
+ self.assert_qmp(result, 'return', {})
+
+ caps = [{'capability': 'dirty-bitmaps', 'state': True},
+ {'capability': 'events', 'state': True}]
+
+ result = self.vm_a.qmp('migrate-set-capabilities', capabilities=caps)
self.assert_qmp(result, 'return', {})
- result = self.vm_b.qmp('migrate-set-capabilities',
- capabilities=[bitmaps_cap])
+ result = self.vm_b.qmp('migrate-set-capabilities', capabilities=caps)
self.assert_qmp(result, 'return', {})
result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo)
@@ -94,26 +168,94 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
result = self.vm_a.qmp('migrate-start-postcopy')
self.assert_qmp(result, 'return', {})
- while True:
- event = self.vm_a.event_wait('MIGRATION')
- if event['data']['status'] == 'completed':
- break
+ event_resume = self.vm_b.event_wait('RESUME')
+ self.vm_b_events.append(event_resume)
+ return event_resume
+
+ def test_postcopy_success(self):
+ event_resume = self.start_postcopy()
+
+ # enabled bitmaps should be updated
+ apply_discards(self.vm_b, discards2)
+
+ match = {'data': {'status': 'completed'}}
+ event_complete = self.vm_b.event_wait('MIGRATION', match=match)
+ self.vm_b_events.append(event_complete)
+
+ # take queued event, should already been happened
+ event_stop = self.vm_a.event_wait('STOP')
+ self.vm_a_events.append(event_stop)
+
+ downtime = event_dist(event_stop, event_resume)
+ postcopy_time = event_dist(event_resume, event_complete)
+
+ assert downtime * 10 < postcopy_time
+ if debug:
+ print('downtime:', downtime)
+ print('postcopy_time:', postcopy_time)
+
+ # check that there are no bitmaps stored on source
+ self.vm_a_events += self.vm_a.get_qmp_events()
+ self.vm_a.shutdown()
+ self.vm_a.launch()
+ check_bitmaps(self.vm_a, 0)
+
+ # check that bitmaps are migrated and persistence works
+ check_bitmaps(self.vm_b, nb_bitmaps)
+ self.vm_b.shutdown()
+ # recreate vm_b, so there is no incoming option, which prevents
+ # loading bitmaps from disk
+ self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
+ self.vm_b.launch()
+ check_bitmaps(self.vm_b, nb_bitmaps)
+
+ # Check content of migrated bitmaps. Still, don't waste time checking
+ # every bitmap
+ for i in range(0, nb_bitmaps, 5):
+ result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap{}'.format(i))
+ sha = self.discards1_sha256 if i % 2 else self.all_discards_sha256
+ self.assert_qmp(result, 'return/sha256', sha)
+
+ def test_early_shutdown_destination(self):
+ self.start_postcopy()
+
+ self.vm_b_events += self.vm_b.get_qmp_events()
+ self.vm_b.shutdown()
+ # recreate vm_b, so there is no incoming option, which prevents
+ # loading bitmaps from disk
+ self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
+ self.vm_b.launch()
+ check_bitmaps(self.vm_b, 0)
- s = 0x8000
- while s < write_size:
- self.vm_b.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
- s += 0x10000
+ # Bitmaps will be lost if we just shutdown the vm, as they are marked
+ # to skip storing to disk when prepared for migration. And that's
+ # correct, as actual data may be modified in target vm, so we play
+ # safe.
+ # Still, this mark would be taken away if we do 'cont', and bitmaps
+ # become persistent again. (see iotest 169 for such behavior case)
+ result = self.vm_a.qmp('query-status')
+ assert not result['return']['running']
+ self.vm_a_events += self.vm_a.get_qmp_events()
+ self.vm_a.shutdown()
+ self.vm_a.launch()
+ check_bitmaps(self.vm_a, 0)
+
+ def test_early_kill_source(self):
+ self.start_postcopy()
+
+ self.vm_a_events = self.vm_a.get_qmp_events()
+ self.vm_a.kill()
+
+ self.vm_a.launch()
- result = self.vm_b.qmp('query-block');
- while len(result['return'][0]['dirty-bitmaps']) > 1:
- time.sleep(2)
- result = self.vm_b.qmp('query-block');
+ match = {'data': {'status': 'completed'}}
+ e_complete = self.vm_b.event_wait('MIGRATION', match=match)
+ self.vm_b_events.append(e_complete)
- result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256',
- node='drive0', name='bitmap')
+ check_bitmaps(self.vm_a, 0)
+ check_bitmaps(self.vm_b, 0)
- self.assert_qmp(result, 'return/sha256', sha256);
if __name__ == '__main__':
- iotests.main(supported_fmts=['qcow2'], supported_cache_modes=['none'],
- supported_protocols=['file'])
+ iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/199.out b/tests/qemu-iotests/199.out
index ae1213e6f8..8d7e996700 100644
--- a/tests/qemu-iotests/199.out
+++ b/tests/qemu-iotests/199.out
@@ -1,5 +1,5 @@
-.
+...
----------------------------------------------------------------------
-Ran 1 tests
+Ran 3 tests
OK
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 1e1cb27bc8..025ed5238d 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -112,7 +112,7 @@
088 rw quick
089 rw auto quick
090 rw auto quick
-091 rw migration
+091 rw migration quick
092 rw quick
093 throttle
094 rw quick
@@ -186,7 +186,7 @@
162 quick
163 rw
165 rw quick
-169 rw quick migration
+169 rw migration
170 rw auto quick
171 rw quick
172 auto
@@ -197,9 +197,9 @@
177 rw auto quick
178 img
179 rw auto quick
-181 rw auto migration
+181 rw auto migration quick
182 rw quick
-183 rw migration
+183 rw migration quick
184 rw auto quick
185 rw
186 rw auto
@@ -216,9 +216,9 @@
198 rw
199 rw migration
200 rw
-201 rw migration
+201 rw migration quick
202 rw quick
-203 rw auto migration
+203 rw auto migration quick
204 rw quick
205 rw quick
206 rw
diff --git a/tests/test-char.c b/tests/test-char.c
index 614bdac2df..d35cc839bc 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -1200,7 +1200,7 @@ static void char_serial_test(void)
/* test tty alias */
qemu_opt_set(opts, "backend", "tty", &error_abort);
- chr = qemu_chr_new_from_opts(opts, NULL, NULL);
+ chr = qemu_chr_new_from_opts(opts, NULL, &error_abort);
g_assert_nonnull(chr);
object_unparent(OBJECT(chr));