diff options
-rw-r--r-- | block/io.c | 10 | ||||
-rw-r--r-- | block/qcow2.c | 2 | ||||
-rw-r--r-- | docs/interop/qcow2.txt | 2 | ||||
-rw-r--r-- | docs/system/arm/virt.rst | 4 | ||||
-rw-r--r-- | hw/acpi/ghes.c | 12 | ||||
-rw-r--r-- | hw/arm/boot.c | 6 | ||||
-rw-r--r-- | hw/misc/aspeed_sdmc.c | 7 | ||||
-rw-r--r-- | hw/net/virtio-net.c | 30 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.c | 2 | ||||
-rw-r--r-- | linux-user/main.c | 16 | ||||
-rw-r--r-- | linux-user/syscall.c | 13 | ||||
-rw-r--r-- | migration/block-dirty-bitmap.c | 474 | ||||
-rw-r--r-- | migration/migration.c | 15 | ||||
-rw-r--r-- | migration/migration.h | 3 | ||||
-rw-r--r-- | migration/savevm.c | 37 | ||||
-rw-r--r-- | net/queue.c | 3 | ||||
-rw-r--r-- | qga/commands-win32.c | 29 | ||||
-rw-r--r-- | qga/qapi-schema.json | 2 | ||||
m--------- | slirp | 0 | ||||
-rw-r--r-- | target/arm/helper.c | 19 | ||||
-rw-r--r-- | target/arm/mte_helper.c | 37 | ||||
-rwxr-xr-x | tests/qemu-iotests/028 | 19 | ||||
-rw-r--r-- | tests/qemu-iotests/028.out | 11 | ||||
-rwxr-xr-x | tests/qemu-iotests/197 | 8 | ||||
-rw-r--r-- | tests/qemu-iotests/197.out | 2 | ||||
-rwxr-xr-x | tests/qemu-iotests/199 | 250 | ||||
-rw-r--r-- | tests/qemu-iotests/199.out | 4 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 12 | ||||
-rw-r--r-- | tests/test-char.c | 2 |
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(¤t_incoming->postcopy_pause_sem_dst, 0); qemu_sem_init(¤t_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)); |