aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c27
-rw-r--r--block/blklogwrites.c5
-rw-r--r--block/block-backend.c59
-rw-r--r--block/io.c8
-rw-r--r--block/mirror.c11
-rw-r--r--block/nbd-client.c1
-rw-r--r--block/nvme.c1
-rw-r--r--block/qcow2-refcount.c3
-rw-r--r--block/qcow2.c1
-rw-r--r--block/qcow2.h10
-rw-r--r--block/qed.c1
-rw-r--r--block/vdi.c57
-rw-r--r--block/vmdk.c532
-rw-r--r--block/vpc.c4
-rw-r--r--hw/acpi/vmgenid.c6
-rw-r--r--hw/scsi/scsi-disk.c59
-rw-r--r--hw/scsi/virtio-scsi.c13
-rw-r--r--include/qemu/units.h73
-rw-r--r--include/qemu/uuid.h2
-rw-r--r--include/sysemu/block-backend.h5
-rw-r--r--qapi/block-core.json71
-rw-r--r--qapi/qapi-schema.json16
-rw-r--r--scripts/qtest.py6
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/qemu-iotests/141.out4
-rwxr-xr-xtests/qemu-iotests/2296
-rw-r--r--tests/qemu-iotests/229.out1
-rwxr-xr-xtests/qemu-iotests/23456
-rw-r--r--tests/qemu-iotests/234.out10
-rw-r--r--tests/qemu-iotests/236.out56
-rwxr-xr-xtests/qemu-iotests/237237
-rw-r--r--tests/qemu-iotests/237.out348
-rwxr-xr-xtests/qemu-iotests/23953
-rw-r--r--tests/qemu-iotests/239.out4
-rwxr-xr-xtests/qemu-iotests/240129
-rw-r--r--tests/qemu-iotests/240.out54
-rwxr-xr-xtests/qemu-iotests/check7
-rw-r--r--tests/qemu-iotests/common.filter1
-rw-r--r--tests/qemu-iotests/group3
-rw-r--r--tests/qemu-iotests/iotests.py22
-rw-r--r--tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2bin0 -> 3479 bytes
-rw-r--r--tests/test-block-iothread.c372
-rw-r--r--tests/vmgenid-test.c2
-rw-r--r--util/uuid.c10
44 files changed, 1931 insertions, 417 deletions
diff --git a/block.c b/block.c
index e795a7c5de..b67d9b7b65 100644
--- a/block.c
+++ b/block.c
@@ -1438,13 +1438,19 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
- error_setg(errp,
- !bs->read_only && bdrv_is_whitelisted(drv, true)
- ? "Driver '%s' can only be used for read-only devices"
- : "Driver '%s' is not whitelisted",
- drv->format_name);
- ret = -ENOTSUP;
- goto fail_opts;
+ if (!bs->read_only && bdrv_is_whitelisted(drv, true)) {
+ ret = bdrv_apply_auto_read_only(bs, NULL, NULL);
+ } else {
+ ret = -ENOTSUP;
+ }
+ if (ret < 0) {
+ error_setg(errp,
+ !bs->read_only && bdrv_is_whitelisted(drv, true)
+ ? "Driver '%s' can only be used for read-only devices"
+ : "Driver '%s' is not whitelisted",
+ drv->format_name);
+ goto fail_opts;
+ }
}
/* bdrv_new() and bdrv_close() make it so */
@@ -3725,6 +3731,7 @@ static void bdrv_check_co_entry(void *opaque)
{
CheckCo *cco = opaque;
cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
+ aio_wait_kick();
}
int bdrv_check(BlockDriverState *bs,
@@ -3743,7 +3750,7 @@ int bdrv_check(BlockDriverState *bs,
bdrv_check_co_entry(&cco);
} else {
co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
- qemu_coroutine_enter(co);
+ bdrv_coroutine_enter(bs, co);
BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
}
@@ -4690,6 +4697,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
if (parent->role->activate) {
parent->role->activate(parent, &local_err);
if (local_err) {
+ bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
return;
}
@@ -4708,6 +4716,7 @@ static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
InvalidateCacheCo *ico = opaque;
bdrv_co_invalidate_cache(ico->bs, ico->errp);
ico->done = true;
+ aio_wait_kick();
}
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
@@ -4724,7 +4733,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
bdrv_invalidate_cache_co_entry(&ico);
} else {
co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
- qemu_coroutine_enter(co);
+ bdrv_coroutine_enter(bs, co);
BDRV_POLL_WHILE(bs, !ico.done);
}
}
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
index ff98cd5533..d2e01bdb1d 100644
--- a/block/blklogwrites.c
+++ b/block/blklogwrites.c
@@ -295,10 +295,9 @@ static void blk_log_writes_refresh_filename(BlockDriverState *bs,
qdict_put_str(opts, "driver", "blklogwrites");
qobject_ref(bs->file->bs->full_open_options);
- qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
+ qdict_put(opts, "file", bs->file->bs->full_open_options);
qobject_ref(s->log_file->bs->full_open_options);
- qdict_put_obj(opts, "log",
- QOBJECT(s->log_file->bs->full_open_options));
+ qdict_put(opts, "log", s->log_file->bs->full_open_options);
qdict_put_int(opts, "log-sector-size", s->sectorsize);
bs->full_open_options = opts;
diff --git a/block/block-backend.c b/block/block-backend.c
index cf05abd89d..f6ea824308 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -47,9 +47,7 @@ struct BlockBackend {
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
BlockBackendPublic public;
- void *dev; /* attached device model, if any */
- bool legacy_dev; /* true if dev is not a DeviceState */
- /* TODO change to DeviceState when all users are qdevified */
+ DeviceState *dev; /* attached device model, if any */
const BlockDevOps *dev_ops;
void *dev_opaque;
@@ -836,7 +834,11 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm)
*shared_perm = blk->shared_perm;
}
-static int blk_do_attach_dev(BlockBackend *blk, void *dev)
+/*
+ * Attach device model @dev to @blk.
+ * Return 0 on success, -EBUSY when a device model is attached already.
+ */
+int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
{
if (blk->dev) {
return -EBUSY;
@@ -851,40 +853,16 @@ static int blk_do_attach_dev(BlockBackend *blk, void *dev)
blk_ref(blk);
blk->dev = dev;
- blk->legacy_dev = false;
blk_iostatus_reset(blk);
return 0;
}
/*
- * Attach device model @dev to @blk.
- * Return 0 on success, -EBUSY when a device model is attached already.
- */
-int blk_attach_dev(BlockBackend *blk, DeviceState *dev)
-{
- return blk_do_attach_dev(blk, dev);
-}
-
-/*
- * Attach device model @dev to @blk.
- * @blk must not have a device model attached already.
- * TODO qdevified devices don't use this, remove when devices are qdevified
- */
-void blk_attach_dev_legacy(BlockBackend *blk, void *dev)
-{
- if (blk_do_attach_dev(blk, dev) < 0) {
- abort();
- }
- blk->legacy_dev = true;
-}
-
-/*
* Detach device model @dev from @blk.
* @dev must be currently attached to @blk.
*/
-void blk_detach_dev(BlockBackend *blk, void *dev)
-/* TODO change to DeviceState *dev when all users are qdevified */
+void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
{
assert(blk->dev == dev);
blk->dev = NULL;
@@ -898,8 +876,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev)
/*
* Return the device model attached to @blk if any, else null.
*/
-void *blk_get_attached_dev(BlockBackend *blk)
-/* TODO change to return DeviceState * when all users are qdevified */
+DeviceState *blk_get_attached_dev(BlockBackend *blk)
{
return blk->dev;
}
@@ -908,10 +885,7 @@ void *blk_get_attached_dev(BlockBackend *blk)
* device attached to the BlockBackend. */
char *blk_get_attached_dev_id(BlockBackend *blk)
{
- DeviceState *dev;
-
- assert(!blk->legacy_dev);
- dev = blk->dev;
+ DeviceState *dev = blk->dev;
if (!dev) {
return g_strdup("");
@@ -949,11 +923,6 @@ BlockBackend *blk_by_dev(void *dev)
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
void *opaque)
{
- /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep
- * it that way, so we can assume blk->dev, if present, is a DeviceState if
- * blk->dev_ops is set. Non-device users may use dev_ops without device. */
- assert(!blk->legacy_dev);
-
blk->dev_ops = ops;
blk->dev_opaque = opaque;
@@ -979,8 +948,6 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
bool tray_was_open, tray_is_open;
Error *local_err = NULL;
- assert(!blk->legacy_dev);
-
tray_was_open = blk_dev_is_tray_open(blk);
blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
if (local_err) {
@@ -1220,6 +1187,7 @@ static void blk_read_entry(void *opaque)
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags);
+ aio_wait_kick();
}
static void blk_write_entry(void *opaque)
@@ -1229,6 +1197,7 @@ static void blk_write_entry(void *opaque)
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags);
+ aio_wait_kick();
}
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
@@ -1540,6 +1509,7 @@ static void blk_ioctl_entry(void *opaque)
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
qiov->iov[0].iov_base);
+ aio_wait_kick();
}
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
@@ -1586,6 +1556,7 @@ static void blk_flush_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
rwco->ret = blk_co_flush(rwco->blk);
+ aio_wait_kick();
}
int blk_flush(BlockBackend *blk)
@@ -1779,9 +1750,6 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
BlockDriverState *bs = blk_bs(blk);
char *id;
- /* blk_eject is only called by qdevified devices */
- assert(!blk->legacy_dev);
-
if (bs) {
bdrv_eject(bs, eject_flag);
}
@@ -2018,6 +1986,7 @@ static void blk_pdiscard_entry(void *opaque)
QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
+ aio_wait_kick();
}
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
diff --git a/block/io.c b/block/io.c
index bd9d688f8b..213ca03d8d 100644
--- a/block/io.c
+++ b/block/io.c
@@ -806,6 +806,7 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
rwco->qiov->size, rwco->qiov,
rwco->flags);
}
+ aio_wait_kick();
}
/*
@@ -2279,6 +2280,7 @@ static void coroutine_fn bdrv_block_status_above_co_entry(void *opaque)
data->offset, data->bytes,
data->pnum, data->map, data->file);
data->done = true;
+ aio_wait_kick();
}
/*
@@ -2438,6 +2440,7 @@ static void coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque)
{
BdrvVmstateCo *co = opaque;
co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read);
+ aio_wait_kick();
}
static inline int
@@ -2559,6 +2562,7 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
FlushCo *rwco = opaque;
rwco->ret = bdrv_co_flush(rwco->bs);
+ aio_wait_kick();
}
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
@@ -2704,6 +2708,7 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque)
DiscardCo *rwco = opaque;
rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes);
+ aio_wait_kick();
}
int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
@@ -3217,6 +3222,7 @@ static void coroutine_fn bdrv_truncate_co_entry(void *opaque)
TruncateCo *tco = opaque;
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc,
tco->errp);
+ aio_wait_kick();
}
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
@@ -3236,7 +3242,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
bdrv_truncate_co_entry(&tco);
} else {
co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco);
- qemu_coroutine_enter(co);
+ bdrv_coroutine_enter(child->bs, co);
BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE);
}
diff --git a/block/mirror.c b/block/mirror.c
index 24ede6fdaa..b67b0120f8 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1612,6 +1612,14 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
goto fail;
}
+ ret = block_job_add_bdrv(&s->common, "source", bs, 0,
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
+ BLK_PERM_CONSISTENT_READ,
+ errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
/* Required permissions are already taken with blk_new() */
block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
@@ -1649,6 +1657,9 @@ fail:
g_free(s->replaces);
blk_unref(s->target);
bs_opaque->job = NULL;
+ if (s->dirty_bitmap) {
+ bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
+ }
job_early_fail(&s->common.job);
}
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 813539676d..50a8dadd85 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -119,6 +119,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
s->quit = true;
nbd_recv_coroutines_wake_all(s);
s->read_reply_co = NULL;
+ aio_wait_kick();
}
static int nbd_co_send_request(BlockDriverState *bs,
diff --git a/block/nvme.c b/block/nvme.c
index 982097b5b1..b5952c9b08 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -390,6 +390,7 @@ static void nvme_cmd_sync_cb(void *opaque, int ret)
{
int *pret = opaque;
*pret = ret;
+ aio_wait_kick();
}
static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 1c63ac244a..6f13d470d3 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -368,6 +368,9 @@ static int alloc_refcount_block(BlockDriverState *bs,
return new_block;
}
+ /* The offset must fit in the offset field of the refcount table entry */
+ assert((new_block & REFT_OFFSET_MASK) == new_block);
+
/* If we're allocating the block at offset 0 then something is wrong */
if (new_block == 0) {
qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
diff --git a/block/qcow2.c b/block/qcow2.c
index 4897abae5e..8c91b92865 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1671,6 +1671,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
/* From bdrv_co_create. */
qcow2_open_entry(&qoc);
} else {
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
}
diff --git a/block/qcow2.h b/block/qcow2.h
index 438a1dee9e..32cce9eee2 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -50,11 +50,11 @@
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
-#define QCOW_MAX_REFTABLE_SIZE S_8MiB
+#define QCOW_MAX_REFTABLE_SIZE (8 * MiB)
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
-#define QCOW_MAX_L1_SIZE S_32MiB
+#define QCOW_MAX_L1_SIZE (32 * MiB)
/* Allow for an average of 1k per snapshot table entry, should be plenty of
* space for snapshot names and IDs */
@@ -81,15 +81,15 @@
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
#ifdef CONFIG_LINUX
-#define DEFAULT_L2_CACHE_MAX_SIZE S_32MiB
+#define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB)
#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */
#else
-#define DEFAULT_L2_CACHE_MAX_SIZE S_8MiB
+#define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB)
/* Cache clean interval is currently available only on Linux, so must be 0 */
#define DEFAULT_CACHE_CLEAN_INTERVAL 0
#endif
-#define DEFAULT_CLUSTER_SIZE S_64KiB
+#define DEFAULT_CLUSTER_SIZE 65536
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
diff --git a/block/qed.c b/block/qed.c
index 9377c0b7ad..1280870024 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -559,6 +559,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
if (qemu_in_coroutine()) {
bdrv_qed_open_entry(&qoc);
} else {
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
}
diff --git a/block/vdi.c b/block/vdi.c
index 2380daa583..e1c42ad732 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -85,7 +85,8 @@
#define BLOCK_OPT_STATIC "static"
#define SECTOR_SIZE 512
-#define DEFAULT_CLUSTER_SIZE S_1MiB
+#define DEFAULT_CLUSTER_SIZE 1048576
+/* Note: can't use 1 * MiB, because it's passed to stringify() */
#if defined(CONFIG_VDI_DEBUG)
#define VDI_DEBUG 1
@@ -203,10 +204,10 @@ static void vdi_header_to_cpu(VdiHeader *header)
header->block_extra = le32_to_cpu(header->block_extra);
header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
- qemu_uuid_bswap(&header->uuid_image);
- qemu_uuid_bswap(&header->uuid_last_snap);
- qemu_uuid_bswap(&header->uuid_link);
- qemu_uuid_bswap(&header->uuid_parent);
+ header->uuid_image = qemu_uuid_bswap(header->uuid_image);
+ header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
+ header->uuid_link = qemu_uuid_bswap(header->uuid_link);
+ header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
}
static void vdi_header_to_le(VdiHeader *header)
@@ -227,15 +228,16 @@ static void vdi_header_to_le(VdiHeader *header)
header->block_extra = cpu_to_le32(header->block_extra);
header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
- qemu_uuid_bswap(&header->uuid_image);
- qemu_uuid_bswap(&header->uuid_last_snap);
- qemu_uuid_bswap(&header->uuid_link);
- qemu_uuid_bswap(&header->uuid_parent);
+ header->uuid_image = qemu_uuid_bswap(header->uuid_image);
+ header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
+ header->uuid_link = qemu_uuid_bswap(header->uuid_link);
+ header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
}
static void vdi_header_print(VdiHeader *header)
{
- char uuid[37];
+ char uuidstr[37];
+ QemuUUID uuid;
logout("text %s", header->text);
logout("signature 0x%08x\n", header->signature);
logout("header size 0x%04x\n", header->header_size);
@@ -254,14 +256,18 @@ static void vdi_header_print(VdiHeader *header)
logout("block extra 0x%04x\n", header->block_extra);
logout("blocks tot. 0x%04x\n", header->blocks_in_image);
logout("blocks all. 0x%04x\n", header->blocks_allocated);
- qemu_uuid_unparse(&header->uuid_image, uuid);
- logout("uuid image %s\n", uuid);
- qemu_uuid_unparse(&header->uuid_last_snap, uuid);
- logout("uuid snap %s\n", uuid);
- qemu_uuid_unparse(&header->uuid_link, uuid);
- logout("uuid link %s\n", uuid);
- qemu_uuid_unparse(&header->uuid_parent, uuid);
- logout("uuid parent %s\n", uuid);
+ uuid = header->uuid_image;
+ qemu_uuid_unparse(&uuid, uuidstr);
+ logout("uuid image %s\n", uuidstr);
+ uuid = header->uuid_last_snap;
+ qemu_uuid_unparse(&uuid, uuidstr);
+ logout("uuid snap %s\n", uuidstr);
+ uuid = header->uuid_link;
+ qemu_uuid_unparse(&uuid, uuidstr);
+ logout("uuid link %s\n", uuidstr);
+ uuid = header->uuid_parent;
+ qemu_uuid_unparse(&uuid, uuidstr);
+ logout("uuid parent %s\n", uuidstr);
}
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
@@ -368,6 +374,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
size_t bmap_size;
int ret;
Error *local_err = NULL;
+ QemuUUID uuid_link, uuid_parent;
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp);
@@ -395,6 +402,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
+ uuid_link = header.uuid_link;
+ uuid_parent = header.uuid_parent;
+
if (header.disk_size % SECTOR_SIZE != 0) {
/* 'VBoxManage convertfromraw' can create images with odd disk sizes.
We accept them but round the disk size to the next multiple of
@@ -444,11 +454,11 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
(uint64_t)header.blocks_in_image * header.block_size);
ret = -ENOTSUP;
goto fail;
- } else if (!qemu_uuid_is_null(&header.uuid_link)) {
+ } else if (!qemu_uuid_is_null(&uuid_link)) {
error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
ret = -ENOTSUP;
goto fail;
- } else if (!qemu_uuid_is_null(&header.uuid_parent)) {
+ } else if (!qemu_uuid_is_null(&uuid_parent)) {
error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
ret = -ENOTSUP;
goto fail;
@@ -733,6 +743,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
BlockDriverState *bs_file = NULL;
BlockBackend *blk = NULL;
uint32_t *bmap = NULL;
+ QemuUUID uuid;
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
vdi_opts = &create_options->u.vdi;
@@ -819,8 +830,10 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
if (image_type == VDI_TYPE_STATIC) {
header.blocks_allocated = blocks;
}
- qemu_uuid_generate(&header.uuid_image);
- qemu_uuid_generate(&header.uuid_last_snap);
+ qemu_uuid_generate(&uuid);
+ header.uuid_image = uuid;
+ qemu_uuid_generate(&uuid);
+ header.uuid_last_snap = uuid;
/* There is no need to set header.uuid_link or header.uuid_parent here. */
if (VDI_DEBUG) {
vdi_header_print(&header);
diff --git a/block/vmdk.c b/block/vmdk.c
index 2c9e86d98f..682ad93aa1 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1741,35 +1741,17 @@ static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs,
return ret;
}
-static int vmdk_create_extent(const char *filename, int64_t filesize,
- bool flat, bool compress, bool zeroed_grain,
- QemuOpts *opts, Error **errp)
+static int vmdk_init_extent(BlockBackend *blk,
+ int64_t filesize, bool flat,
+ bool compress, bool zeroed_grain,
+ Error **errp)
{
int ret, i;
- BlockBackend *blk = NULL;
VMDK4Header header;
- Error *local_err = NULL;
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
uint32_t *gd_buf = NULL;
int gd_buf_size;
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- goto exit;
- }
-
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
- ret = -EIO;
- goto exit;
- }
-
- blk_set_allow_write_beyond_eof(blk, true);
-
if (flat) {
ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp);
goto exit;
@@ -1863,15 +1845,50 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
gd_buf, gd_buf_size, 0);
if (ret < 0) {
error_setg(errp, QERR_IO_ERROR);
- goto exit;
}
ret = 0;
exit:
+ g_free(gd_buf);
+ return ret;
+}
+
+static int vmdk_create_extent(const char *filename, int64_t filesize,
+ bool flat, bool compress, bool zeroed_grain,
+ BlockBackend **pbb,
+ QemuOpts *opts, Error **errp)
+{
+ int ret;
+ BlockBackend *blk = NULL;
+ Error *local_err = NULL;
+
+ ret = bdrv_create_file(filename, opts, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto exit;
+ }
+
+ blk = blk_new_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
+ &local_err);
+ if (blk == NULL) {
+ error_propagate(errp, local_err);
+ ret = -EIO;
+ goto exit;
+ }
+
+ blk_set_allow_write_beyond_eof(blk, true);
+
+ ret = vmdk_init_extent(blk, filesize, flat, compress, zeroed_grain, errp);
+exit:
if (blk) {
- blk_unref(blk);
+ if (pbb) {
+ *pbb = blk;
+ } else {
+ blk_unref(blk);
+ blk = NULL;
+ }
}
- g_free(gd_buf);
return ret;
}
@@ -1915,33 +1932,57 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
return VMDK_OK;
}
-static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+/*
+ * idx == 0: get or create the descriptor file (also the image file if in a
+ * non-split format.
+ * idx >= 1: get the n-th extent if in a split subformat
+ */
+typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size,
+ int idx,
+ bool flat,
+ bool split,
+ bool compress,
+ bool zeroed_grain,
+ void *opaque,
+ Error **errp);
+
+static void vmdk_desc_add_extent(GString *desc,
+ const char *extent_line_fmt,
+ int64_t size, const char *filename)
{
- int idx = 0;
- BlockBackend *new_blk = NULL;
+ char *basename = g_path_get_basename(filename);
+
+ g_string_append_printf(desc, extent_line_fmt,
+ DIV_ROUND_UP(size, BDRV_SECTOR_SIZE), basename);
+ g_free(basename);
+}
+
+static int coroutine_fn vmdk_co_do_create(int64_t size,
+ BlockdevVmdkSubformat subformat,
+ BlockdevVmdkAdapterType adapter_type,
+ const char *backing_file,
+ const char *hw_version,
+ bool compat6,
+ bool zeroed_grain,
+ vmdk_create_extent_fn extent_fn,
+ void *opaque,
+ Error **errp)
+{
+ int extent_idx;
+ BlockBackend *blk = NULL;
+ BlockBackend *extent_blk;
Error *local_err = NULL;
char *desc = NULL;
- int64_t total_size = 0, filesize;
- char *adapter_type = NULL;
- char *backing_file = NULL;
- char *hw_version = NULL;
- char *fmt = NULL;
int ret = 0;
bool flat, split, compress;
GString *ext_desc_lines;
- char *path = g_malloc0(PATH_MAX);
- char *prefix = g_malloc0(PATH_MAX);
- char *postfix = g_malloc0(PATH_MAX);
- char *desc_line = g_malloc0(BUF_SIZE);
- char *ext_filename = g_malloc0(PATH_MAX);
- char *desc_filename = g_malloc0(PATH_MAX);
const int64_t split_size = 0x80000000; /* VMDK has constant split size */
- const char *desc_extent_line;
+ int64_t extent_size;
+ int64_t created_size = 0;
+ const char *extent_line_fmt;
char *parent_desc_line = g_malloc0(BUF_SIZE);
uint32_t parent_cid = 0xffffffff;
uint32_t number_heads = 16;
- bool zeroed_grain = false;
uint32_t desc_offset = 0, desc_len;
const char desc_template[] =
"# Disk DescriptorFile\n"
@@ -1965,71 +2006,35 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
ext_desc_lines = g_string_new(NULL);
- if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
- ret = -EINVAL;
- goto exit;
- }
/* Read out options */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION);
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) {
- if (strcmp(hw_version, "undefined")) {
+ if (compat6) {
+ if (hw_version) {
error_setg(errp,
"compat6 cannot be enabled with hwversion set");
ret = -EINVAL;
goto exit;
}
- g_free(hw_version);
- hw_version = g_strdup("6");
+ hw_version = "6";
}
- if (strcmp(hw_version, "undefined") == 0) {
- g_free(hw_version);
- hw_version = g_strdup("4");
- }
- fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false)) {
- zeroed_grain = true;
+ if (!hw_version) {
+ hw_version = "4";
}
- if (!adapter_type) {
- adapter_type = g_strdup("ide");
- } else if (strcmp(adapter_type, "ide") &&
- strcmp(adapter_type, "buslogic") &&
- strcmp(adapter_type, "lsilogic") &&
- strcmp(adapter_type, "legacyESX")) {
- error_setg(errp, "Unknown adapter type: '%s'", adapter_type);
- ret = -EINVAL;
- goto exit;
- }
- if (strcmp(adapter_type, "ide") != 0) {
+ if (adapter_type != BLOCKDEV_VMDK_ADAPTER_TYPE_IDE) {
/* that's the number of heads with which vmware operates when
creating, exporting, etc. vmdk files with a non-ide adapter type */
number_heads = 255;
}
- if (!fmt) {
- /* Default format to monolithicSparse */
- fmt = g_strdup("monolithicSparse");
- } else if (strcmp(fmt, "monolithicFlat") &&
- strcmp(fmt, "monolithicSparse") &&
- strcmp(fmt, "twoGbMaxExtentSparse") &&
- strcmp(fmt, "twoGbMaxExtentFlat") &&
- strcmp(fmt, "streamOptimized")) {
- error_setg(errp, "Unknown subformat: '%s'", fmt);
- ret = -EINVAL;
- goto exit;
- }
- split = !(strcmp(fmt, "twoGbMaxExtentFlat") &&
- strcmp(fmt, "twoGbMaxExtentSparse"));
- flat = !(strcmp(fmt, "monolithicFlat") &&
- strcmp(fmt, "twoGbMaxExtentFlat"));
- compress = !strcmp(fmt, "streamOptimized");
+ split = (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT) ||
+ (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE);
+ flat = (subformat == BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT) ||
+ (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT);
+ compress = subformat == BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED;
+
if (flat) {
- desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n";
+ extent_line_fmt = "RW %" PRId64 " FLAT \"%s\" 0\n";
} else {
- desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n";
+ extent_line_fmt = "RW %" PRId64 " SPARSE \"%s\"\n";
}
if (flat && backing_file) {
error_setg(errp, "Flat image can't have backing file");
@@ -2041,10 +2046,34 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
ret = -ENOTSUP;
goto exit;
}
+
+ /* Create extents */
+ if (split) {
+ extent_size = split_size;
+ } else {
+ extent_size = size;
+ }
+ if (!split && !flat) {
+ created_size = extent_size;
+ } else {
+ created_size = 0;
+ }
+ /* Get the descriptor file BDS */
+ blk = extent_fn(created_size, 0, flat, split, compress, zeroed_grain,
+ opaque, errp);
+ if (!blk) {
+ ret = -EIO;
+ goto exit;
+ }
+ if (!split && !flat) {
+ vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, created_size,
+ blk_bs(blk)->filename);
+ }
+
if (backing_file) {
- BlockBackend *blk;
+ BlockBackend *backing;
char *full_backing = g_new0(char, PATH_MAX);
- bdrv_get_full_backing_filename_from_filename(filename, backing_file,
+ bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename, backing_file,
full_backing, PATH_MAX,
&local_err);
if (local_err) {
@@ -2054,106 +2083,227 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
goto exit;
}
- blk = blk_new_open(full_backing, NULL, NULL,
- BDRV_O_NO_BACKING, errp);
+ backing = blk_new_open(full_backing, NULL, NULL,
+ BDRV_O_NO_BACKING, errp);
g_free(full_backing);
- if (blk == NULL) {
+ if (backing == NULL) {
ret = -EIO;
goto exit;
}
- if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) {
- blk_unref(blk);
+ if (strcmp(blk_bs(backing)->drv->format_name, "vmdk")) {
+ error_setg(errp, "Invalid backing file format: %s. Must be vmdk",
+ blk_bs(backing)->drv->format_name);
+ blk_unref(backing);
ret = -EINVAL;
goto exit;
}
- ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid);
- blk_unref(blk);
+ ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid);
+ blk_unref(backing);
if (ret) {
+ error_setg(errp, "Failed to read parent CID");
goto exit;
}
snprintf(parent_desc_line, BUF_SIZE,
"parentFileNameHint=\"%s\"", backing_file);
}
-
- /* Create extents */
- filesize = total_size;
- while (filesize > 0) {
- int64_t size = filesize;
-
- if (split && size > split_size) {
- size = split_size;
- }
- if (split) {
- snprintf(desc_filename, PATH_MAX, "%s-%c%03d%s",
- prefix, flat ? 'f' : 's', ++idx, postfix);
- } else if (flat) {
- snprintf(desc_filename, PATH_MAX, "%s-flat%s", prefix, postfix);
- } else {
- snprintf(desc_filename, PATH_MAX, "%s%s", prefix, postfix);
- }
- snprintf(ext_filename, PATH_MAX, "%s%s", path, desc_filename);
-
- if (vmdk_create_extent(ext_filename, size,
- flat, compress, zeroed_grain, opts, errp)) {
+ extent_idx = 1;
+ while (created_size < size) {
+ int64_t cur_size = MIN(size - created_size, extent_size);
+ extent_blk = extent_fn(cur_size, extent_idx, flat, split, compress,
+ zeroed_grain, opaque, errp);
+ if (!extent_blk) {
ret = -EINVAL;
goto exit;
}
- filesize -= size;
+ vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, cur_size,
+ blk_bs(extent_blk)->filename);
+ created_size += cur_size;
+ extent_idx++;
+ blk_unref(extent_blk);
+ }
- /* Format description line */
- snprintf(desc_line, BUF_SIZE,
- desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename);
- g_string_append(ext_desc_lines, desc_line);
+ /* Check whether we got excess extents */
+ extent_blk = extent_fn(-1, extent_idx, flat, split, compress, zeroed_grain,
+ opaque, NULL);
+ if (extent_blk) {
+ blk_unref(extent_blk);
+ error_setg(errp, "List of extents contains unused extents");
+ ret = -EINVAL;
+ goto exit;
}
+
/* generate descriptor file */
desc = g_strdup_printf(desc_template,
g_random_int(),
parent_cid,
- fmt,
+ BlockdevVmdkSubformat_str(subformat),
parent_desc_line,
ext_desc_lines->str,
hw_version,
- total_size /
+ size /
(int64_t)(63 * number_heads * BDRV_SECTOR_SIZE),
number_heads,
- adapter_type);
+ BlockdevVmdkAdapterType_str(adapter_type));
desc_len = strlen(desc);
/* the descriptor offset = 0x200 */
if (!split && !flat) {
desc_offset = 0x200;
- } else {
- ret = bdrv_create_file(filename, opts, &local_err);
+ }
+
+ ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write description");
+ goto exit;
+ }
+ /* bdrv_pwrite write padding zeros to align to sector, we don't need that
+ * for description file */
+ if (desc_offset == 0) {
+ ret = blk_truncate(blk, desc_len, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
- error_propagate(errp, local_err);
goto exit;
}
}
+ ret = 0;
+exit:
+ if (blk) {
+ blk_unref(blk);
+ }
+ g_free(desc);
+ g_free(parent_desc_line);
+ g_string_free(ext_desc_lines, true);
+ return ret;
+}
+
+typedef struct {
+ char *path;
+ char *prefix;
+ char *postfix;
+ QemuOpts *opts;
+} VMDKCreateOptsData;
+
+static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx,
+ bool flat, bool split, bool compress,
+ bool zeroed_grain, void *opaque,
+ Error **errp)
+{
+ BlockBackend *blk = NULL;
+ BlockDriverState *bs = NULL;
+ VMDKCreateOptsData *data = opaque;
+ char *ext_filename = NULL;
+ char *rel_filename = NULL;
+
+ /* We're done, don't create excess extents. */
+ if (size == -1) {
+ assert(errp == NULL);
+ return NULL;
+ }
- new_blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (new_blk == NULL) {
- error_propagate(errp, local_err);
- ret = -EIO;
+ if (idx == 0) {
+ rel_filename = g_strdup_printf("%s%s", data->prefix, data->postfix);
+ } else if (split) {
+ rel_filename = g_strdup_printf("%s-%c%03d%s",
+ data->prefix,
+ flat ? 'f' : 's', idx, data->postfix);
+ } else {
+ assert(idx == 1);
+ rel_filename = g_strdup_printf("%s-flat%s", data->prefix, data->postfix);
+ }
+
+ ext_filename = g_strdup_printf("%s%s", data->path, rel_filename);
+ g_free(rel_filename);
+
+ if (vmdk_create_extent(ext_filename, size,
+ flat, compress, zeroed_grain, &blk, data->opts,
+ errp)) {
goto exit;
}
+ bdrv_unref(bs);
+exit:
+ g_free(ext_filename);
+ return blk;
+}
- blk_set_allow_write_beyond_eof(new_blk, true);
+static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ char *desc = NULL;
+ int64_t total_size = 0;
+ char *adapter_type = NULL;
+ BlockdevVmdkAdapterType adapter_type_enum;
+ char *backing_file = NULL;
+ char *hw_version = NULL;
+ char *fmt = NULL;
+ BlockdevVmdkSubformat subformat;
+ int ret = 0;
+ char *path = g_malloc0(PATH_MAX);
+ char *prefix = g_malloc0(PATH_MAX);
+ char *postfix = g_malloc0(PATH_MAX);
+ char *desc_line = g_malloc0(BUF_SIZE);
+ char *ext_filename = g_malloc0(PATH_MAX);
+ char *desc_filename = g_malloc0(PATH_MAX);
+ char *parent_desc_line = g_malloc0(BUF_SIZE);
+ bool zeroed_grain;
+ bool compat6;
+ VMDKCreateOptsData data;
- ret = blk_pwrite(new_blk, desc_offset, desc, desc_len, 0);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not write description");
+ if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
+ ret = -EINVAL;
goto exit;
}
- /* bdrv_pwrite write padding zeros to align to sector, we don't need that
- * for description file */
- if (desc_offset == 0) {
- ret = blk_truncate(new_blk, desc_len, PREALLOC_MODE_OFF, errp);
+ /* Read out options */
+ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
+ BDRV_SECTOR_SIZE);
+ adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE);
+ backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
+ hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION);
+ compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false);
+ if (strcmp(hw_version, "undefined") == 0) {
+ g_free(hw_version);
+ hw_version = g_strdup("4");
}
-exit:
- if (new_blk) {
- blk_unref(new_blk);
+ fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
+ zeroed_grain = qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false);
+
+ if (adapter_type) {
+ adapter_type_enum = qapi_enum_parse(&BlockdevVmdkAdapterType_lookup,
+ adapter_type,
+ BLOCKDEV_VMDK_ADAPTER_TYPE_IDE,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto exit;
+ }
+ } else {
+ adapter_type_enum = BLOCKDEV_VMDK_ADAPTER_TYPE_IDE;
+ }
+
+ if (!fmt) {
+ /* Default format to monolithicSparse */
+ subformat = BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE;
+ } else {
+ subformat = qapi_enum_parse(&BlockdevVmdkSubformat_lookup,
+ fmt,
+ BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto exit;
+ }
}
+ data = (VMDKCreateOptsData){
+ .prefix = prefix,
+ .postfix = postfix,
+ .path = path,
+ .opts = opts,
+ };
+ ret = vmdk_co_do_create(total_size, subformat, adapter_type_enum,
+ backing_file, hw_version, compat6, zeroed_grain,
+ vmdk_co_create_opts_cb, &data, errp);
+
+exit:
g_free(adapter_type);
g_free(backing_file);
g_free(hw_version);
@@ -2166,7 +2316,86 @@ exit:
g_free(ext_filename);
g_free(desc_filename);
g_free(parent_desc_line);
- g_string_free(ext_desc_lines, true);
+ return ret;
+}
+
+static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
+ bool flat, bool split, bool compress,
+ bool zeroed_grain, void *opaque,
+ Error **errp)
+{
+ int ret;
+ BlockDriverState *bs;
+ BlockBackend *blk;
+ BlockdevCreateOptionsVmdk *opts = opaque;
+
+ if (idx == 0) {
+ bs = bdrv_open_blockdev_ref(opts->file, errp);
+ } else {
+ int i;
+ BlockdevRefList *list = opts->extents;
+ for (i = 1; i < idx; i++) {
+ if (!list || !list->next) {
+ error_setg(errp, "Extent [%d] not specified", i);
+ return NULL;
+ }
+ list = list->next;
+ }
+ if (!list) {
+ error_setg(errp, "Extent [%d] not specified", idx - 1);
+ return NULL;
+ }
+ bs = bdrv_open_blockdev_ref(list->value, errp);
+ }
+ if (!bs) {
+ return NULL;
+ }
+ blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
+ BLK_PERM_ALL);
+ if (blk_insert_bs(blk, bs, errp)) {
+ bdrv_unref(bs);
+ return NULL;
+ }
+ blk_set_allow_write_beyond_eof(blk, true);
+ bdrv_unref(bs);
+
+ if (size != -1) {
+ ret = vmdk_init_extent(blk, size, flat, compress, zeroed_grain, errp);
+ if (ret) {
+ blk_unref(blk);
+ blk = NULL;
+ }
+ }
+ return blk;
+}
+
+static int coroutine_fn vmdk_co_create(BlockdevCreateOptions *create_options,
+ Error **errp)
+{
+ int ret;
+ BlockdevCreateOptionsVmdk *opts;
+
+ opts = &create_options->u.vmdk;
+
+ /* Validate options */
+ if (!QEMU_IS_ALIGNED(opts->size, BDRV_SECTOR_SIZE)) {
+ error_setg(errp, "Image size must be a multiple of 512 bytes");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = vmdk_co_do_create(opts->size,
+ opts->subformat,
+ opts->adapter_type,
+ opts->backing_file,
+ opts->hwversion,
+ false,
+ opts->zeroed_grain,
+ vmdk_co_create_cb,
+ opts, errp);
+ return ret;
+
+out:
return ret;
}
@@ -2434,6 +2663,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes,
.bdrv_close = vmdk_close,
.bdrv_co_create_opts = vmdk_co_create_opts,
+ .bdrv_co_create = vmdk_co_create,
.bdrv_co_flush_to_disk = vmdk_co_flush,
.bdrv_co_block_status = vmdk_co_block_status,
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
diff --git a/block/vpc.c b/block/vpc.c
index d886465b7e..52ab717642 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -979,6 +979,7 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
int64_t total_size;
int disk_type;
int ret = -EIO;
+ QemuUUID uuid;
assert(opts->driver == BLOCKDEV_DRIVER_VPC);
vpc_opts = &opts->u.vpc;
@@ -1062,7 +1063,8 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
footer->type = cpu_to_be32(disk_type);
- qemu_uuid_generate(&footer->uuid);
+ qemu_uuid_generate(&uuid);
+ footer->uuid = uuid;
footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));
diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c
index d78b579a20..02717a8b0d 100644
--- a/hw/acpi/vmgenid.c
+++ b/hw/acpi/vmgenid.c
@@ -30,8 +30,7 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
* first, since that's what the guest expects
*/
g_array_set_size(guid, VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data));
- guid_le = vms->guid;
- qemu_uuid_bswap(&guid_le);
+ guid_le = qemu_uuid_bswap(vms->guid);
/* The GUID is written at a fixed offset into the fw_cfg file
* in order to implement the "OVMF SDT Header probe suppressor"
* see docs/specs/vmgenid.txt for more details
@@ -149,8 +148,7 @@ static void vmgenid_update_guest(VmGenIdState *vms)
* however, will expect the fields to be little-endian.
* Perform a byte swap immediately before writing.
*/
- guid_le = vms->guid;
- qemu_uuid_bswap(&guid_le);
+ guid_le = qemu_uuid_bswap(vms->guid);
/* The GUID is written at a fixed offset into the fw_cfg file
* in order to implement the "OVMF SDT Header probe suppressor"
* see docs/specs/vmgenid.txt for more details.
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 0e9027c8f3..e6db6d7c15 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -104,6 +104,7 @@ typedef struct SCSIDiskState
char *serial;
char *vendor;
char *product;
+ char *device_id;
bool tray_open;
bool tray_locked;
/*
@@ -642,22 +643,19 @@ static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
case 0x83: /* Device identification page, mandatory */
{
- const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
- int max_len = s->serial ? 20 : 255 - 8;
- int id_len = strlen(str);
+ int id_len = s->device_id ? MIN(strlen(s->device_id), 255 - 8) : 0;
- if (id_len > max_len) {
- id_len = max_len;
- }
DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %zd\n", req->cmd.xfer);
- outbuf[buflen++] = 0x2; /* ASCII */
- outbuf[buflen++] = 0; /* not officially assigned */
- outbuf[buflen++] = 0; /* reserved */
- outbuf[buflen++] = id_len; /* length of data following */
- memcpy(outbuf + buflen, str, id_len);
- buflen += id_len;
+ if (id_len) {
+ outbuf[buflen++] = 0x2; /* ASCII */
+ outbuf[buflen++] = 0; /* not officially assigned */
+ outbuf[buflen++] = 0; /* reserved */
+ outbuf[buflen++] = id_len; /* length of data following */
+ memcpy(outbuf + buflen, s->device_id, id_len);
+ buflen += id_len;
+ }
if (s->qdev.wwn) {
outbuf[buflen++] = 0x1; /* Binary */
@@ -2361,6 +2359,16 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
if (!s->vendor) {
s->vendor = g_strdup("QEMU");
}
+ if (!s->device_id) {
+ if (s->serial) {
+ s->device_id = g_strdup_printf("%.20s", s->serial);
+ } else {
+ const char *str = blk_name(s->qdev.conf.blk);
+ if (str && *str) {
+ s->device_id = g_strdup(str);
+ }
+ }
+ }
if (blk_is_sg(s->qdev.conf.blk)) {
error_setg(errp, "unwanted /dev/sg*");
@@ -2381,10 +2389,13 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ AioContext *ctx = NULL;
/* can happen for devices without drive. The error message for missing
* backend will be issued in scsi_realize
*/
if (s->qdev.conf.blk) {
+ ctx = blk_get_aio_context(s->qdev.conf.blk);
+ aio_context_acquire(ctx);
blkconf_blocksizes(&s->qdev.conf);
}
s->qdev.blocksize = s->qdev.conf.logical_block_size;
@@ -2393,11 +2404,15 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
s->product = g_strdup("QEMU HARDDISK");
}
scsi_realize(&s->qdev, errp);
+ if (ctx) {
+ aio_context_release(ctx);
+ }
}
static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ AioContext *ctx;
int ret;
if (!dev->conf.blk) {
@@ -2408,6 +2423,8 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
assert(ret == 0);
}
+ ctx = blk_get_aio_context(dev->conf.blk);
+ aio_context_acquire(ctx);
s->qdev.blocksize = 2048;
s->qdev.type = TYPE_ROM;
s->features |= 1 << SCSI_DISK_F_REMOVABLE;
@@ -2415,6 +2432,7 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
s->product = g_strdup("QEMU CD-ROM");
}
scsi_realize(&s->qdev, errp);
+ aio_context_release(ctx);
}
static void scsi_disk_realize(SCSIDevice *dev, Error **errp)
@@ -2553,6 +2571,7 @@ static int get_device_type(SCSIDiskState *s)
static void scsi_block_realize(SCSIDevice *dev, Error **errp)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ AioContext *ctx;
int sg_version;
int rc;
@@ -2567,6 +2586,9 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
"be removed in a future version");
}
+ ctx = blk_get_aio_context(s->qdev.conf.blk);
+ aio_context_acquire(ctx);
+
/* check we are using a driver managing SG_IO (version 3 and after) */
rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version);
if (rc < 0) {
@@ -2574,18 +2596,18 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
if (rc != -EPERM) {
error_append_hint(errp, "Is this a SCSI device?\n");
}
- return;
+ goto out;
}
if (sg_version < 30000) {
error_setg(errp, "scsi generic interface too old");
- return;
+ goto out;
}
/* get device type from INQUIRY data */
rc = get_device_type(s);
if (rc < 0) {
error_setg(errp, "INQUIRY failed");
- return;
+ goto out;
}
/* Make a guess for the block size, we'll fix it when the guest sends.
@@ -2605,6 +2627,9 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
scsi_realize(&s->qdev, errp);
scsi_generic_read_device_inquiry(&s->qdev);
+
+out:
+ aio_context_release(ctx);
}
typedef struct SCSIBlockReq {
@@ -2902,7 +2927,9 @@ static const TypeInfo scsi_disk_base_info = {
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \
DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \
- DEFINE_PROP_STRING("product", SCSIDiskState, product)
+ DEFINE_PROP_STRING("product", SCSIDiskState, product), \
+ DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id)
+
static Property scsi_hd_properties[] = {
DEFINE_SCSI_DISK_PROPERTIES(),
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 3aa99717e2..eb90288f47 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -791,9 +791,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
SCSIDevice *sd = SCSI_DEVICE(dev);
if (s->ctx && !s->dataplane_fenced) {
+ AioContext *ctx;
if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
return;
}
+ ctx = blk_get_aio_context(sd->conf.blk);
+ if (ctx != s->ctx && ctx != qemu_get_aio_context()) {
+ error_setg(errp, "Cannot attach a blockdev that is using "
+ "a different iothread");
+ return;
+ }
virtio_scsi_acquire(s);
blk_set_aio_context(sd->conf.blk, s->ctx);
virtio_scsi_release(s);
@@ -824,6 +831,12 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
virtio_scsi_release(s);
}
+ if (s->ctx) {
+ virtio_scsi_acquire(s);
+ blk_set_aio_context(sd->conf.blk, qemu_get_aio_context());
+ virtio_scsi_release(s);
+ }
+
qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
}
diff --git a/include/qemu/units.h b/include/qemu/units.h
index 1c959d182e..692db3fbb2 100644
--- a/include/qemu/units.h
+++ b/include/qemu/units.h
@@ -17,77 +17,4 @@
#define PiB (INT64_C(1) << 50)
#define EiB (INT64_C(1) << 60)
-/*
- * The following lookup table is intended to be used when a literal string of
- * the number of bytes is required (for example if it needs to be stringified).
- * It can also be used for generic shortcuts of power-of-two sizes.
- * This table is generated using the AWK script below:
- *
- * BEGIN {
- * suffix="KMGTPE";
- * for(i=10; i<64; i++) {
- * val=2**i;
- * s=substr(suffix, int(i/10), 1);
- * n=2**(i%10);
- * pad=21-int(log(n)/log(10));
- * printf("#define S_%d%siB %*d\n", n, s, pad, val);
- * }
- * }
- */
-
-#define S_1KiB 1024
-#define S_2KiB 2048
-#define S_4KiB 4096
-#define S_8KiB 8192
-#define S_16KiB 16384
-#define S_32KiB 32768
-#define S_64KiB 65536
-#define S_128KiB 131072
-#define S_256KiB 262144
-#define S_512KiB 524288
-#define S_1MiB 1048576
-#define S_2MiB 2097152
-#define S_4MiB 4194304
-#define S_8MiB 8388608
-#define S_16MiB 16777216
-#define S_32MiB 33554432
-#define S_64MiB 67108864
-#define S_128MiB 134217728
-#define S_256MiB 268435456
-#define S_512MiB 536870912
-#define S_1GiB 1073741824
-#define S_2GiB 2147483648
-#define S_4GiB 4294967296
-#define S_8GiB 8589934592
-#define S_16GiB 17179869184
-#define S_32GiB 34359738368
-#define S_64GiB 68719476736
-#define S_128GiB 137438953472
-#define S_256GiB 274877906944
-#define S_512GiB 549755813888
-#define S_1TiB 1099511627776
-#define S_2TiB 2199023255552
-#define S_4TiB 4398046511104
-#define S_8TiB 8796093022208
-#define S_16TiB 17592186044416
-#define S_32TiB 35184372088832
-#define S_64TiB 70368744177664
-#define S_128TiB 140737488355328
-#define S_256TiB 281474976710656
-#define S_512TiB 562949953421312
-#define S_1PiB 1125899906842624
-#define S_2PiB 2251799813685248
-#define S_4PiB 4503599627370496
-#define S_8PiB 9007199254740992
-#define S_16PiB 18014398509481984
-#define S_32PiB 36028797018963968
-#define S_64PiB 72057594037927936
-#define S_128PiB 144115188075855872
-#define S_256PiB 288230376151711744
-#define S_512PiB 576460752303423488
-#define S_1EiB 1152921504606846976
-#define S_2EiB 2305843009213693952
-#define S_4EiB 4611686018427387904
-#define S_8EiB 9223372036854775808
-
#endif
diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h
index 09489ce5c5..037357d990 100644
--- a/include/qemu/uuid.h
+++ b/include/qemu/uuid.h
@@ -56,6 +56,6 @@ char *qemu_uuid_unparse_strdup(const QemuUUID *uuid);
int qemu_uuid_parse(const char *str, QemuUUID *uuid);
-void qemu_uuid_bswap(QemuUUID *uuid);
+QemuUUID qemu_uuid_bswap(QemuUUID uuid);
#endif
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index c8f816a200..832a4bf168 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -110,9 +110,8 @@ void blk_iostatus_disable(BlockBackend *blk);
void blk_iostatus_reset(BlockBackend *blk);
void blk_iostatus_set_err(BlockBackend *blk, int error);
int blk_attach_dev(BlockBackend *blk, DeviceState *dev);
-void blk_attach_dev_legacy(BlockBackend *blk, void *dev);
-void blk_detach_dev(BlockBackend *blk, void *dev);
-void *blk_get_attached_dev(BlockBackend *blk);
+void blk_detach_dev(BlockBackend *blk, DeviceState *dev);
+DeviceState *blk_get_attached_dev(BlockBackend *blk);
char *blk_get_attached_dev_id(BlockBackend *blk);
BlockBackend *blk_by_dev(void *dev);
BlockBackend *blk_by_qdev_id(const char *id, Error **errp);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index cde1b53708..5f17d67d71 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -4138,6 +4138,76 @@
'*cluster-size' : 'size' } }
##
+# @BlockdevVmdkSubformat:
+#
+# Subformat options for VMDK images
+#
+# @monolithicSparse: Single file image with sparse cluster allocation
+#
+# @monolithicFlat: Single flat data image and a descriptor file
+#
+# @twoGbMaxExtentSparse: Data is split into 2GB (per virtual LBA) sparse extent
+# files, in addition to a descriptor file
+#
+# @twoGbMaxExtentFlat: Data is split into 2GB (per virtual LBA) flat extent
+# files, in addition to a descriptor file
+#
+# @streamOptimized: Single file image sparse cluster allocation, optimized
+# for streaming over network.
+#
+# Since: 4.0
+##
+{ 'enum': 'BlockdevVmdkSubformat',
+ 'data': [ 'monolithicSparse', 'monolithicFlat', 'twoGbMaxExtentSparse',
+ 'twoGbMaxExtentFlat', 'streamOptimized'] }
+
+##
+# @BlockdevVmdkAdapterType:
+#
+# Adapter type info for VMDK images
+#
+# Since: 4.0
+##
+{ 'enum': 'BlockdevVmdkAdapterType',
+ 'data': [ 'ide', 'buslogic', 'lsilogic', 'legacyESX'] }
+
+##
+# @BlockdevCreateOptionsVmdk:
+#
+# Driver specific image creation options for VMDK.
+#
+# @file Where to store the new image file. This refers to the image
+# file for monolithcSparse and streamOptimized format, or the
+# descriptor file for other formats.
+# @size Size of the virtual disk in bytes
+# @extents Where to store the data extents. Required for monolithcFlat,
+# twoGbMaxExtentSparse and twoGbMaxExtentFlat formats. For
+# monolithicFlat, only one entry is required; for
+# twoGbMaxExtent* formats, the number of entries required is
+# calculated as extent_number = virtual_size / 2GB. Providing
+# more extents than will be used is an error.
+# @subformat The subformat of the VMDK image. Default: "monolithicSparse".
+# @backing-file The path of backing file. Default: no backing file is used.
+# @adapter-type The adapter type used to fill in the descriptor. Default: ide.
+# @hwversion Hardware version. The meaningful options are "4" or "6".
+# Default: "4".
+# @zeroed-grain Whether to enable zeroed-grain feature for sparse subformats.
+# Default: false.
+#
+# Since: 4.0
+##
+{ 'struct': 'BlockdevCreateOptionsVmdk',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*extents': ['BlockdevRef'],
+ '*subformat': 'BlockdevVmdkSubformat',
+ '*backing-file': 'str',
+ '*adapter-type': 'BlockdevVmdkAdapterType',
+ '*hwversion': 'str',
+ '*zeroed-grain': 'bool' } }
+
+
+##
# @SheepdogRedundancyType:
#
# @full Create a fully replicated vdi with x copies
@@ -4331,6 +4401,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
+ 'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index 3bbdfcee84..1845aa78ff 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -63,13 +63,15 @@
'query-tpm-types',
'ringbuf-read' ],
'name-case-whitelist': [
- 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
- 'CpuInfoMIPS', # PC, visible through query-cpu
- 'CpuInfoTricore', # PC, visible through query-cpu
- 'QapiErrorClass', # all members, visible through errors
- 'UuidInfo', # UUID, visible through query-uuid
- 'X86CPURegister32', # all members, visible indirectly through qom-get
- 'q_obj_CpuInfo-base' # CPU, visible through query-cpu
+ 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
+ 'CpuInfoMIPS', # PC, visible through query-cpu
+ 'CpuInfoTricore', # PC, visible through query-cpu
+ 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
+ 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
+ 'QapiErrorClass', # all members, visible through errors
+ 'UuidInfo', # UUID, visible through query-uuid
+ 'X86CPURegister32', # all members, visible indirectly through qom-get
+ 'q_obj_CpuInfo-base' # CPU, visible through query-cpu
] } }
# Documentation generated with qapi-gen.py is in source order, with
diff --git a/scripts/qtest.py b/scripts/qtest.py
index adf1fe3f26..afac3fe900 100644
--- a/scripts/qtest.py
+++ b/scripts/qtest.py
@@ -31,6 +31,7 @@ class QEMUQtestProtocol(object):
"""
self._address = address
self._sock = self._get_sock()
+ self._sockfile = None
if server:
self._sock.bind(self._address)
self._sock.listen(1)
@@ -49,6 +50,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors
"""
self._sock.connect(self._address)
+ self._sockfile = self._sock.makefile()
def accept(self):
"""
@@ -57,6 +59,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors
"""
self._sock, _ = self._sock.accept()
+ self._sockfile = self._sock.makefile()
def cmd(self, qtest_cmd):
"""
@@ -65,9 +68,12 @@ class QEMUQtestProtocol(object):
@param qtest_cmd: qtest command text to be sent
"""
self._sock.sendall((qtest_cmd + "\n").encode('utf-8'))
+ resp = self._sockfile.readline()
+ return resp
def close(self):
self._sock.close()
+ self._sockfile.close()
def settimeout(self, timeout):
self._sock.settimeout(timeout)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 19b4c0a696..75ad9c0dd3 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -73,6 +73,7 @@ check-unit-y += tests/test-bdrv-drain$(EXESUF)
check-unit-y += tests/test-blockjob$(EXESUF)
check-unit-y += tests/test-blockjob-txn$(EXESUF)
check-unit-y += tests/test-block-backend$(EXESUF)
+check-unit-y += tests/test-block-iothread$(EXESUF)
check-unit-y += tests/test-image-locking$(EXESUF)
check-unit-y += tests/test-x86-cpuid$(EXESUF)
# all code tested by test-x86-cpuid is inside topology.h
@@ -557,6 +558,7 @@ tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(te
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y)
+tests/test-block-iothread$(EXESUF): tests/test-block-iothread.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-image-locking$(EXESUF): tests/test-image-locking.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
index f252c86875..41c7291258 100644
--- a/tests/qemu-iotests/141.out
+++ b/tests/qemu-iotests/141.out
@@ -28,7 +28,7 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
{"return": {}}
-{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
@@ -45,7 +45,7 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
{"return": {}}
-{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
+{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229
index 893d098ad2..b0d4885fa6 100755
--- a/tests/qemu-iotests/229
+++ b/tests/qemu-iotests/229
@@ -81,11 +81,15 @@ echo
echo '=== Force cancel job paused in error state ==='
echo
+# Filter out BLOCK_JOB_ERROR events because they may or may not occur.
+# Cancelling the job means resuming it for a bit before it is actually
+# aborted, and in that time it may or may not re-encounter the error.
success_or_failure="y" _send_qemu_cmd $QEMU_HANDLE \
"{'execute': 'block-job-cancel',
'arguments': { 'device': 'testdisk',
'force': true}}" \
- "BLOCK_JOB_CANCELLED" "Assertion"
+ "BLOCK_JOB_CANCELLED" "Assertion" \
+ | grep -v '"BLOCK_JOB_ERROR"'
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/229.out b/tests/qemu-iotests/229.out
index 4c4112805f..a3eb33788a 100644
--- a/tests/qemu-iotests/229.out
+++ b/tests/qemu-iotests/229.out
@@ -17,7 +17,6 @@ wrote 2097152/2097152 bytes at offset 0
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}}
{"return": {}}
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "testdisk", "operation": "write", "action": "stop"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "testdisk"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "testdisk", "len": 2097152, "offset": 1048576, "speed": 0, "type": "mirror"}}
*** done
diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234
index a8185b4360..c4c26bc21e 100755
--- a/tests/qemu-iotests/234
+++ b/tests/qemu-iotests/234
@@ -26,6 +26,22 @@ import os
iotests.verify_image_format(supported_fmts=['qcow2'])
iotests.verify_platform(['linux'])
+def enable_migration_events(vm, name):
+ iotests.log('Enabling migration QMP events on %s...' % name)
+ iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[
+ {
+ 'capability': 'events',
+ 'state': True
+ }
+ ]))
+
+def wait_migration(vm):
+ while True:
+ event = vm.event_wait('MIGRATION')
+ iotests.log(event, filters=[iotests.filter_qmp_event])
+ if event['data']['status'] == 'completed':
+ break
+
with iotests.FilePath('img') as img_path, \
iotests.FilePath('backing') as backing_path, \
iotests.FilePath('mig_fifo_a') as fifo_a, \
@@ -46,6 +62,8 @@ with iotests.FilePath('img') as img_path, \
.add_blockdev('%s,file=drive0-backing-file,node-name=drive0-backing' % (iotests.imgfmt))
.launch())
+ enable_migration_events(vm_a, 'A')
+
iotests.log('Launching destination VM...')
(vm_b.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path))
.add_blockdev('%s,file=drive0-file,node-name=drive0' % (iotests.imgfmt))
@@ -54,6 +72,8 @@ with iotests.FilePath('img') as img_path, \
.add_incoming("exec: cat '%s'" % (fifo_a))
.launch())
+ enable_migration_events(vm_b, 'B')
+
# Add a child node that was created after the parent node. The reverse case
# is covered by the -blockdev options above.
iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing',
@@ -61,22 +81,13 @@ with iotests.FilePath('img') as img_path, \
iotests.log(vm_b.qmp('blockdev-snapshot', node='drive0-backing',
overlay='drive0'))
- iotests.log('Enabling migration QMP events on A...')
- iotests.log(vm_a.qmp('migrate-set-capabilities', capabilities=[
- {
- 'capability': 'events',
- 'state': True
- }
- ]))
-
iotests.log('Starting migration to B...')
iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo_a)))
with iotests.Timeout(3, 'Migration does not complete'):
- while True:
- event = vm_a.event_wait('MIGRATION')
- iotests.log(event, filters=[iotests.filter_qmp_event])
- if event['data']['status'] == 'completed':
- break
+ # Wait for the source first (which includes setup=setup)
+ wait_migration(vm_a)
+ # Wait for the destination second (which does not)
+ wait_migration(vm_b)
iotests.log(vm_a.qmp('query-migrate')['return']['status'])
iotests.log(vm_b.qmp('query-migrate')['return']['status'])
@@ -94,25 +105,18 @@ with iotests.FilePath('img') as img_path, \
.add_incoming("exec: cat '%s'" % (fifo_b))
.launch())
+ enable_migration_events(vm_a, 'A')
+
iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing',
overlay='drive0'))
- iotests.log('Enabling migration QMP events on B...')
- iotests.log(vm_b.qmp('migrate-set-capabilities', capabilities=[
- {
- 'capability': 'events',
- 'state': True
- }
- ]))
-
iotests.log('Starting migration back to A...')
iotests.log(vm_b.qmp('migrate', uri='exec:cat >%s' % (fifo_b)))
with iotests.Timeout(3, 'Migration does not complete'):
- while True:
- event = vm_b.event_wait('MIGRATION')
- iotests.log(event, filters=[iotests.filter_qmp_event])
- if event['data']['status'] == 'completed':
- break
+ # Wait for the source first (which includes setup=setup)
+ wait_migration(vm_b)
+ # Wait for the destination second (which does not)
+ wait_migration(vm_a)
iotests.log(vm_a.qmp('query-migrate')['return']['status'])
iotests.log(vm_b.qmp('query-migrate')['return']['status'])
diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out
index b9ed910b1a..692976d1c6 100644
--- a/tests/qemu-iotests/234.out
+++ b/tests/qemu-iotests/234.out
@@ -1,14 +1,18 @@
Launching source VM...
+Enabling migration QMP events on A...
+{"return": {}}
Launching destination VM...
+Enabling migration QMP events on B...
{"return": {}}
{"return": {}}
-Enabling migration QMP events on A...
{"return": {}}
Starting migration to B...
{"return": {}}
{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
+{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
+{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
completed
completed
{"return": {"running": false, "singlestep": false, "status": "postmigrate"}}
@@ -16,14 +20,16 @@ completed
Add a second parent to drive0-file...
{"return": {}}
Restart A with -incoming and second parent...
+Enabling migration QMP events on A...
{"return": {}}
-Enabling migration QMP events on B...
{"return": {}}
Starting migration back to A...
{"return": {}}
{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
+{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
+{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
completed
completed
{"return": {"running": true, "singlestep": false, "status": "running"}}
diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out
index 1dad24db0d..bb2d71ea5e 100644
--- a/tests/qemu-iotests/236.out
+++ b/tests/qemu-iotests/236.out
@@ -45,23 +45,23 @@ write -P0xcd 0x3ff0000 64k
"actions": [
{
"data": {
- "node": "drive0",
- "name": "bitmapB"
+ "name": "bitmapB",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-disable"
},
{
"data": {
- "node": "drive0",
+ "granularity": 65536,
"name": "bitmapC",
- "granularity": 65536
+ "node": "drive0"
},
"type": "block-dirty-bitmap-add"
},
{
"data": {
- "node": "drive0",
- "name": "bitmapA"
+ "name": "bitmapA",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-clear"
},
@@ -105,30 +105,30 @@ write -P0xcd 0x3ff0000 64k
"actions": [
{
"data": {
- "node": "drive0",
- "name": "bitmapB"
+ "name": "bitmapB",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-disable"
},
{
"data": {
- "node": "drive0",
+ "granularity": 65536,
"name": "bitmapC",
- "granularity": 65536
+ "node": "drive0"
},
"type": "block-dirty-bitmap-add"
},
{
"data": {
- "node": "drive0",
- "name": "bitmapC"
+ "name": "bitmapC",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-disable"
},
{
"data": {
- "node": "drive0",
- "name": "bitmapC"
+ "name": "bitmapC",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-enable"
}
@@ -158,15 +158,15 @@ write -P0xea 0x3fe0000 64k
"actions": [
{
"data": {
- "node": "drive0",
- "name": "bitmapA"
+ "name": "bitmapA",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-disable"
},
{
"data": {
- "node": "drive0",
- "name": "bitmapC"
+ "name": "bitmapC",
+ "node": "drive0"
},
"type": "block-dirty-bitmap-disable"
}
@@ -209,21 +209,21 @@ write -P0xea 0x3fe0000 64k
"actions": [
{
"data": {
- "node": "drive0",
"disabled": true,
+ "granularity": 65536,
"name": "bitmapD",
- "granularity": 65536
+ "node": "drive0"
},
"type": "block-dirty-bitmap-add"
},
{
"data": {
- "node": "drive0",
- "target": "bitmapD",
"bitmaps": [
"bitmapB",
"bitmapC"
- ]
+ ],
+ "node": "drive0",
+ "target": "bitmapD"
},
"type": "block-dirty-bitmap-merge"
},
@@ -273,21 +273,21 @@ write -P0xea 0x3fe0000 64k
"actions": [
{
"data": {
- "node": "drive0",
"disabled": true,
+ "granularity": 65536,
"name": "bitmapD",
- "granularity": 65536
+ "node": "drive0"
},
"type": "block-dirty-bitmap-add"
},
{
"data": {
- "node": "drive0",
- "target": "bitmapD",
"bitmaps": [
"bitmapB",
"bitmapC"
- ]
+ ],
+ "node": "drive0",
+ "target": "bitmapD"
},
"type": "block-dirty-bitmap-merge"
}
diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237
new file mode 100755
index 0000000000..251771d7fb
--- /dev/null
+++ b/tests/qemu-iotests/237
@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+#
+# Test vmdk and file image creation
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import math
+import iotests
+from iotests import imgfmt
+
+iotests.verify_image_format(supported_fmts=['vmdk'])
+
+def blockdev_create(vm, options):
+ result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+
+ if 'return' in result:
+ assert result['return'] == {}
+ vm.run_job('job0')
+ iotests.log("")
+
+with iotests.FilePath('t.vmdk') as disk_path, \
+ iotests.FilePath('t.vmdk.1') as extent1_path, \
+ iotests.FilePath('t.vmdk.2') as extent2_path, \
+ iotests.FilePath('t.vmdk.3') as extent3_path, \
+ iotests.VM() as vm:
+
+ #
+ # Successful image creation (defaults)
+ #
+ iotests.log("=== Successful image creation (defaults) ===")
+ iotests.log("")
+
+ size = 5 * 1024 * 1024 * 1024
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': 'file',
+ 'filename': disk_path,
+ 'size': 0 })
+
+ vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
+ node_name='imgfile')
+
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'imgfile',
+ 'size': size })
+ vm.shutdown()
+
+ iotests.img_info_log(disk_path)
+
+ #
+ # Successful image creation (inline blockdev-add, explicit defaults)
+ #
+ iotests.log("=== Successful image creation (inline blockdev-add, explicit defaults) ===")
+ iotests.log("")
+
+ # Choose a different size to show that we got a new image
+ size = 64 * 1024 * 1024
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': 'file',
+ 'filename': disk_path,
+ 'size': 0 })
+
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': disk_path,
+ },
+ 'size': size,
+ 'extents': [],
+ 'subformat': 'monolithicSparse',
+ 'adapter-type': 'ide',
+ 'hwversion': '4',
+ 'zeroed-grain': False })
+ vm.shutdown()
+
+ iotests.img_info_log(disk_path)
+
+ #
+ # Successful image creation (non-default options)
+ #
+ iotests.log("=== Successful image creation (with non-default options) ===")
+ iotests.log("")
+
+ # Choose a different size to show that we got a new image
+ size = 32 * 1024 * 1024
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': 'file',
+ 'filename': disk_path,
+ 'size': 0 })
+
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': disk_path,
+ },
+ 'size': size,
+ 'extents': [],
+ 'subformat': 'monolithicSparse',
+ 'adapter-type': 'buslogic',
+ 'zeroed-grain': True })
+ vm.shutdown()
+
+ iotests.img_info_log(disk_path)
+
+ #
+ # Invalid BlockdevRef
+ #
+ iotests.log("=== Invalid BlockdevRef ===")
+ iotests.log("")
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': "this doesn't exist",
+ 'size': size })
+ vm.shutdown()
+
+ #
+ # Adapter types
+ #
+
+ iotests.log("=== Adapter types ===")
+ iotests.log("")
+
+ vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path))
+
+ # Valid
+ iotests.log("== Valid adapter types ==")
+ iotests.log("")
+
+ vm.launch()
+ for adapter_type in [ 'ide', 'buslogic', 'lsilogic', 'legacyESX' ]:
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'node0',
+ 'size': size,
+ 'adapter-type': adapter_type })
+ vm.shutdown()
+
+ # Invalid
+ iotests.log("== Invalid adapter types ==")
+ iotests.log("")
+
+ vm.launch()
+ for adapter_type in [ 'foo', 'IDE', 'legacyesx', 1 ]:
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'node0',
+ 'size': size,
+ 'adapter-type': adapter_type })
+ vm.shutdown()
+
+ #
+ # Other subformats
+ #
+ iotests.log("=== Other subformats ===")
+ iotests.log("")
+
+ for path in [ extent1_path, extent2_path, extent3_path ]:
+ msg = iotests.qemu_img_pipe('create', '-f', imgfmt, path, '0')
+ iotests.log(msg, [iotests.filter_testfiles])
+
+ vm.add_blockdev('driver=file,filename=%s,node-name=ext1' % (extent1_path))
+ vm.add_blockdev('driver=file,filename=%s,node-name=ext2' % (extent2_path))
+ vm.add_blockdev('driver=file,filename=%s,node-name=ext3' % (extent3_path))
+
+ # Missing extent
+ iotests.log("== Missing extent ==")
+ iotests.log("")
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'node0',
+ 'size': size,
+ 'subformat': 'monolithicFlat' })
+ vm.shutdown()
+
+ # Correct extent
+ iotests.log("== Correct extent ==")
+ iotests.log("")
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'node0',
+ 'size': size,
+ 'subformat': 'monolithicFlat',
+ 'extents': ['ext1'] })
+ vm.shutdown()
+
+ # Extra extent
+ iotests.log("== Extra extent ==")
+ iotests.log("")
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'node0',
+ 'size': 512,
+ 'subformat': 'monolithicFlat',
+ 'extents': ['ext1', 'ext2', 'ext3'] })
+ vm.shutdown()
+
+ # Split formats
+ iotests.log("== Split formats ==")
+ iotests.log("")
+
+ for size in [ 512, 1073741824, 2147483648, 5368709120 ]:
+ for subfmt in [ 'twoGbMaxExtentFlat', 'twoGbMaxExtentSparse' ]:
+ iotests.log("= %s %d =" % (subfmt, size))
+ iotests.log("")
+
+ num_extents = math.ceil(size / 2.0**31)
+ extents = [ "ext%d" % (i) for i in range(1, num_extents + 1) ]
+
+ vm.launch()
+ blockdev_create(vm, { 'driver': imgfmt,
+ 'file': 'node0',
+ 'size': size,
+ 'subformat': subfmt,
+ 'extents': extents })
+ vm.shutdown()
+
+ iotests.img_info_log(disk_path)
diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out
new file mode 100644
index 0000000000..241c864369
--- /dev/null
+++ b/tests/qemu-iotests/237.out
@@ -0,0 +1,348 @@
+=== Successful image creation (defaults) ===
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node_name": "imgfile"}}
+{"return": {}}
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 5.0G (5368709120 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: monolithicSparse
+ extents:
+ [0]:
+ virtual size: 5368709120
+ filename: TEST_IMG
+ cluster size: 65536
+ format:
+
+=== Successful image creation (inline blockdev-add, explicit defaults) ===
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: monolithicSparse
+ extents:
+ [0]:
+ virtual size: 67108864
+ filename: TEST_IMG
+ cluster size: 65536
+ format:
+
+=== Successful image creation (with non-default options) ===
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 32M (33554432 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: monolithicSparse
+ extents:
+ [0]:
+ virtual size: 33554432
+ filename: TEST_IMG
+ cluster size: 65536
+ format:
+
+=== Invalid BlockdevRef ===
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}}
+{"return": {}}
+Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+=== Adapter types ===
+
+== Valid adapter types ==
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+== Invalid adapter types ==
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}}
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}}
+
+=== Other subformats ===
+
+Formatting 'TEST_DIR/PID-t.vmdk.1', fmt=vmdk size=0 compat6=off hwversion=undefined
+
+Formatting 'TEST_DIR/PID-t.vmdk.2', fmt=vmdk size=0 compat6=off hwversion=undefined
+
+Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefined
+
+== Missing extent ==
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
+{"return": {}}
+Job failed: Extent [0] not specified
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+== Correct extent ==
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+== Extra extent ==
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}}
+{"return": {}}
+Job failed: List of extents contains unused extents
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+== Split formats ==
+
+= twoGbMaxExtentFlat 512 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 512 (512 bytes)
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentFlat
+ extents:
+ [0]:
+ virtual size: 512
+ filename: TEST_IMG.1
+ format: FLAT
+
+= twoGbMaxExtentSparse 512 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 512 (512 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentSparse
+ extents:
+ [0]:
+ virtual size: 512
+ filename: TEST_IMG.1
+ cluster size: 65536
+ format: SPARSE
+
+= twoGbMaxExtentFlat 1073741824 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 1.0G (1073741824 bytes)
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentFlat
+ extents:
+ [0]:
+ virtual size: 1073741824
+ filename: TEST_IMG.1
+ format: FLAT
+
+= twoGbMaxExtentSparse 1073741824 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 1.0G (1073741824 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentSparse
+ extents:
+ [0]:
+ virtual size: 1073741824
+ filename: TEST_IMG.1
+ cluster size: 65536
+ format: SPARSE
+
+= twoGbMaxExtentFlat 2147483648 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 2.0G (2147483648 bytes)
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentFlat
+ extents:
+ [0]:
+ virtual size: 2147483648
+ filename: TEST_IMG.1
+ format: FLAT
+
+= twoGbMaxExtentSparse 2147483648 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 2.0G (2147483648 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentSparse
+ extents:
+ [0]:
+ virtual size: 2147483648
+ filename: TEST_IMG.1
+ cluster size: 65536
+ format: SPARSE
+
+= twoGbMaxExtentFlat 5368709120 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 5.0G (5368709120 bytes)
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentFlat
+ extents:
+ [0]:
+ virtual size: 2147483648
+ filename: TEST_IMG.1
+ format: FLAT
+ [1]:
+ virtual size: 2147483648
+ filename: TEST_IMG.2
+ format: FLAT
+ [2]:
+ virtual size: 1073741824
+ filename: TEST_IMG.3
+ format: FLAT
+
+= twoGbMaxExtentSparse 5368709120 =
+
+{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}}
+{"return": {}}
+{"execute": "job-dismiss", "arguments": {"id": "job0"}}
+{"return": {}}
+
+image: TEST_IMG
+file format: IMGFMT
+virtual size: 5.0G (5368709120 bytes)
+cluster_size: 65536
+Format specific information:
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
+ create type: twoGbMaxExtentSparse
+ extents:
+ [0]:
+ virtual size: 2147483648
+ filename: TEST_IMG.1
+ cluster size: 65536
+ format: SPARSE
+ [1]:
+ virtual size: 2147483648
+ filename: TEST_IMG.2
+ cluster size: 65536
+ format: SPARSE
+ [2]:
+ virtual size: 1073741824
+ filename: TEST_IMG.3
+ cluster size: 65536
+ format: SPARSE
+
diff --git a/tests/qemu-iotests/239 b/tests/qemu-iotests/239
new file mode 100755
index 0000000000..6f085d573d
--- /dev/null
+++ b/tests/qemu-iotests/239
@@ -0,0 +1,53 @@
+#!/bin/bash
+#
+# Test case for dmg
+#
+# Copyright (C) 2019 yuchenlin <npes87184@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=npes87184@gmail.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ rm -f "$TEST_IMG.raw"
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+
+_supported_fmt dmg
+_supported_proto file
+_supported_os Linux
+
+echo
+echo "== Testing conversion to raw should success =="
+_use_sample_img simple-dmg.dmg.bz2
+if ! $QEMU_IMG convert -f $IMGFMT -O raw "$TEST_IMG" "$TEST_IMG.raw" ; then
+ exit 1
+fi
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/239.out b/tests/qemu-iotests/239.out
new file mode 100644
index 0000000000..bedbad065b
--- /dev/null
+++ b/tests/qemu-iotests/239.out
@@ -0,0 +1,4 @@
+QA output created by 239
+
+== Testing conversion to raw should success ==
+*** done
diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240
new file mode 100755
index 0000000000..65cc3b39b1
--- /dev/null
+++ b/tests/qemu-iotests/240
@@ -0,0 +1,129 @@
+#!/bin/bash
+#
+# Test hot plugging and unplugging with iothreads
+#
+# Copyright (C) 2019 Igalia, S.L.
+# Author: Alberto Garcia <berto@igalia.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=berto@igalia.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto generic
+_supported_os Linux
+
+do_run_qemu()
+{
+ echo Testing: "$@"
+ $QEMU -nographic -qmp stdio -serial none "$@"
+ echo
+}
+
+# Remove QMP events from (pretty-printed) output. Doesn't handle
+# nested dicts correctly, but we don't get any of those in this test.
+_filter_qmp_events()
+{
+ tr '\n' '\t' | sed -e \
+ 's/{\s*"timestamp":\s*{[^}]*},\s*"event":[^,}]*\(,\s*"data":\s*{[^}]*}\)\?\s*}\s*//g' \
+ | tr '\t' '\n'
+}
+
+run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_qmp | _filter_qmp_events
+}
+
+case "$QEMU_DEFAULT_MACHINE" in
+ s390-ccw-virtio)
+ virtio_scsi=virtio-scsi-ccw
+ ;;
+ *)
+ virtio_scsi=virtio-scsi-pci
+ ;;
+esac
+
+echo
+echo === Unplug a SCSI disk and then plug it again ===
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0"}}
+{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi0"}}
+{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}}
+{ "execute": "quit"}
+EOF
+
+echo
+echo === Attach two SCSI disks using the same block device and the same iothread ===
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
+{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi-hd1"}}
+{ "execute": "device_del", "arguments": {"id": "scsi0"}}
+{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}}
+{ "execute": "quit"}
+EOF
+
+echo
+echo === Attach two SCSI disks using the same block device but different iothreads ===
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
+{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
+{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread1"}}
+{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi1", "driver": "${virtio_scsi}", "iothread": "iothread1"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi0.0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi1.0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
+{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi1.0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi-hd1"}}
+{ "execute": "device_del", "arguments": {"id": "scsi0"}}
+{ "execute": "device_del", "arguments": {"id": "scsi1"}}
+{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}}
+{ "execute": "quit"}
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out
new file mode 100644
index 0000000000..d76392966c
--- /dev/null
+++ b/tests/qemu-iotests/240.out
@@ -0,0 +1,54 @@
+QA output created by 240
+
+=== Unplug a SCSI disk and then plug it again ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+
+=== Attach two SCSI disks using the same block device and the same iothread ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+
+=== Attach two SCSI disks using the same block device but different iothreads ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cannot attach a blockdev that is using a different iothread"}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+*** done
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index 89ed275988..895e1e3dcb 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -237,6 +237,7 @@ image format options
-vhdx test vhdx
-vmdk test vmdk
-luks test luks
+ -dmg test dmg
image protocol options
-file test file (default)
@@ -304,6 +305,12 @@ testlist options
xpand=false
;;
+ -dmg)
+ IMGFMT=dmg
+ IMGFMT_GENERIC=false
+ xpand=false
+ ;;
+
-qed)
IMGFMT=qed
xpand=false
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index 2031e353a5..1aa7d57140 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -165,6 +165,7 @@ _filter_img_info()
-e "/table_size: [0-9]\\+/d" \
-e "/compat: '[^']*'/d" \
-e "/compat6: \\(on\\|off\\)/d" \
+ -e "s/cid: [0-9]\+/cid: XXXXXXXXXX/" \
-e "/static: \\(on\\|off\\)/d" \
-e "/zeroed_grain: \\(on\\|off\\)/d" \
-e "/subformat: '[^']*'/d" \
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 0f1c3f9cdf..959ffe85fc 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -234,4 +234,7 @@
234 auto quick migration
235 auto quick
236 auto quick
+237 rw auto quick
238 auto quick
+239 rw auto quick
+240 auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 009c614ef7..b461f53abf 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -76,15 +76,16 @@ def qemu_img(*args):
sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
return exitcode
-def ordered_kwargs(kwargs):
- # kwargs prior to 3.6 are not ordered, so:
- od = OrderedDict()
- for k, v in sorted(kwargs.items()):
- if isinstance(v, dict):
- od[k] = ordered_kwargs(v)
- else:
- od[k] = v
- return od
+def ordered_qmp(qmsg):
+ # Dictionaries are not ordered prior to 3.6, therefore:
+ if isinstance(qmsg, list):
+ return [ordered_qmp(atom) for atom in qmsg]
+ if isinstance(qmsg, dict):
+ od = OrderedDict()
+ for k, v in sorted(qmsg.items()):
+ od[k] = ordered_qmp(v)
+ return od
+ return qmsg
def qemu_img_create(*args):
args = list(args)
@@ -299,6 +300,7 @@ def filter_img_info(output, filename):
.replace(imgfmt, 'IMGFMT')
line = re.sub('iters: [0-9]+', 'iters: XXX', line)
line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line)
+ line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line)
lines.append(line)
return '\n'.join(lines)
@@ -505,7 +507,7 @@ class VM(qtest.QEMUQtestMachine):
def qmp_log(self, cmd, filters=[], indent=None, **kwargs):
full_cmd = OrderedDict((
("execute", cmd),
- ("arguments", ordered_kwargs(kwargs))
+ ("arguments", ordered_qmp(kwargs))
))
log(full_cmd, filters, indent=indent)
result = self.qmp(cmd, **kwargs)
diff --git a/tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2 b/tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2
new file mode 100644
index 0000000000..05e719d03d
--- /dev/null
+++ b/tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2
Binary files differ
diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c
new file mode 100644
index 0000000000..97ac0b159d
--- /dev/null
+++ b/tests/test-block-iothread.c
@@ -0,0 +1,372 @@
+/*
+ * Block tests for iothreads
+ *
+ * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block.h"
+#include "block/blockjob_int.h"
+#include "sysemu/block-backend.h"
+#include "qapi/error.h"
+#include "iothread.h"
+
+static int coroutine_fn bdrv_test_co_prwv(BlockDriverState *bs,
+ uint64_t offset, uint64_t bytes,
+ QEMUIOVector *qiov, int flags)
+{
+ return 0;
+}
+
+static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs,
+ int64_t offset, int bytes)
+{
+ return 0;
+}
+
+static int coroutine_fn
+bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset,
+ PreallocMode prealloc, Error **errp)
+{
+ return 0;
+}
+
+static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset, int64_t count,
+ int64_t *pnum, int64_t *map,
+ BlockDriverState **file)
+{
+ *pnum = count;
+ return 0;
+}
+
+static BlockDriver bdrv_test = {
+ .format_name = "test",
+ .instance_size = 1,
+
+ .bdrv_co_preadv = bdrv_test_co_prwv,
+ .bdrv_co_pwritev = bdrv_test_co_prwv,
+ .bdrv_co_pdiscard = bdrv_test_co_pdiscard,
+ .bdrv_co_truncate = bdrv_test_co_truncate,
+ .bdrv_co_block_status = bdrv_test_co_block_status,
+};
+
+static void test_sync_op_pread(BdrvChild *c)
+{
+ uint8_t buf[512];
+ int ret;
+
+ /* Success */
+ ret = bdrv_pread(c, 0, buf, sizeof(buf));
+ g_assert_cmpint(ret, ==, 512);
+
+ /* Early error: Negative offset */
+ ret = bdrv_pread(c, -2, buf, sizeof(buf));
+ g_assert_cmpint(ret, ==, -EIO);
+}
+
+static void test_sync_op_pwrite(BdrvChild *c)
+{
+ uint8_t buf[512];
+ int ret;
+
+ /* Success */
+ ret = bdrv_pwrite(c, 0, buf, sizeof(buf));
+ g_assert_cmpint(ret, ==, 512);
+
+ /* Early error: Negative offset */
+ ret = bdrv_pwrite(c, -2, buf, sizeof(buf));
+ g_assert_cmpint(ret, ==, -EIO);
+}
+
+static void test_sync_op_blk_pread(BlockBackend *blk)
+{
+ uint8_t buf[512];
+ int ret;
+
+ /* Success */
+ ret = blk_pread(blk, 0, buf, sizeof(buf));
+ g_assert_cmpint(ret, ==, 512);
+
+ /* Early error: Negative offset */
+ ret = blk_pread(blk, -2, buf, sizeof(buf));
+ g_assert_cmpint(ret, ==, -EIO);
+}
+
+static void test_sync_op_blk_pwrite(BlockBackend *blk)
+{
+ uint8_t buf[512];
+ int ret;
+
+ /* Success */
+ ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0);
+ g_assert_cmpint(ret, ==, 512);
+
+ /* Early error: Negative offset */
+ ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0);
+ g_assert_cmpint(ret, ==, -EIO);
+}
+
+static void test_sync_op_load_vmstate(BdrvChild *c)
+{
+ uint8_t buf[512];
+ int ret;
+
+ /* Error: Driver does not support snapshots */
+ ret = bdrv_load_vmstate(c->bs, buf, 0, sizeof(buf));
+ g_assert_cmpint(ret, ==, -ENOTSUP);
+}
+
+static void test_sync_op_save_vmstate(BdrvChild *c)
+{
+ uint8_t buf[512];
+ int ret;
+
+ /* Error: Driver does not support snapshots */
+ ret = bdrv_save_vmstate(c->bs, buf, 0, sizeof(buf));
+ g_assert_cmpint(ret, ==, -ENOTSUP);
+}
+
+static void test_sync_op_pdiscard(BdrvChild *c)
+{
+ int ret;
+
+ /* Normal success path */
+ c->bs->open_flags |= BDRV_O_UNMAP;
+ ret = bdrv_pdiscard(c, 0, 512);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early success: UNMAP not supported */
+ c->bs->open_flags &= ~BDRV_O_UNMAP;
+ ret = bdrv_pdiscard(c, 0, 512);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early error: Negative offset */
+ ret = bdrv_pdiscard(c, -2, 512);
+ g_assert_cmpint(ret, ==, -EIO);
+}
+
+static void test_sync_op_blk_pdiscard(BlockBackend *blk)
+{
+ int ret;
+
+ /* Early success: UNMAP not supported */
+ ret = blk_pdiscard(blk, 0, 512);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early error: Negative offset */
+ ret = blk_pdiscard(blk, -2, 512);
+ g_assert_cmpint(ret, ==, -EIO);
+}
+
+static void test_sync_op_truncate(BdrvChild *c)
+{
+ int ret;
+
+ /* Normal success path */
+ ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early error: Negative offset */
+ ret = bdrv_truncate(c, -2, PREALLOC_MODE_OFF, NULL);
+ g_assert_cmpint(ret, ==, -EINVAL);
+
+ /* Error: Read-only image */
+ c->bs->read_only = true;
+ c->bs->open_flags &= ~BDRV_O_RDWR;
+
+ ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL);
+ g_assert_cmpint(ret, ==, -EACCES);
+
+ c->bs->read_only = false;
+ c->bs->open_flags |= BDRV_O_RDWR;
+}
+
+static void test_sync_op_block_status(BdrvChild *c)
+{
+ int ret;
+ int64_t n;
+
+ /* Normal success path */
+ ret = bdrv_is_allocated(c->bs, 0, 65536, &n);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early success: No driver support */
+ bdrv_test.bdrv_co_block_status = NULL;
+ ret = bdrv_is_allocated(c->bs, 0, 65536, &n);
+ g_assert_cmpint(ret, ==, 1);
+
+ /* Early success: bytes = 0 */
+ ret = bdrv_is_allocated(c->bs, 0, 0, &n);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early success: Offset > image size*/
+ ret = bdrv_is_allocated(c->bs, 0x1000000, 0x1000000, &n);
+ g_assert_cmpint(ret, ==, 0);
+}
+
+static void test_sync_op_flush(BdrvChild *c)
+{
+ int ret;
+
+ /* Normal success path */
+ ret = bdrv_flush(c->bs);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early success: Read-only image */
+ c->bs->read_only = true;
+ c->bs->open_flags &= ~BDRV_O_RDWR;
+
+ ret = bdrv_flush(c->bs);
+ g_assert_cmpint(ret, ==, 0);
+
+ c->bs->read_only = false;
+ c->bs->open_flags |= BDRV_O_RDWR;
+}
+
+static void test_sync_op_blk_flush(BlockBackend *blk)
+{
+ BlockDriverState *bs = blk_bs(blk);
+ int ret;
+
+ /* Normal success path */
+ ret = blk_flush(blk);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* Early success: Read-only image */
+ bs->read_only = true;
+ bs->open_flags &= ~BDRV_O_RDWR;
+
+ ret = blk_flush(blk);
+ g_assert_cmpint(ret, ==, 0);
+
+ bs->read_only = false;
+ bs->open_flags |= BDRV_O_RDWR;
+}
+
+static void test_sync_op_check(BdrvChild *c)
+{
+ BdrvCheckResult result;
+ int ret;
+
+ /* Error: Driver does not implement check */
+ ret = bdrv_check(c->bs, &result, 0);
+ g_assert_cmpint(ret, ==, -ENOTSUP);
+}
+
+static void test_sync_op_invalidate_cache(BdrvChild *c)
+{
+ /* Early success: Image is not inactive */
+ bdrv_invalidate_cache(c->bs, NULL);
+}
+
+
+typedef struct SyncOpTest {
+ const char *name;
+ void (*fn)(BdrvChild *c);
+ void (*blkfn)(BlockBackend *blk);
+} SyncOpTest;
+
+const SyncOpTest sync_op_tests[] = {
+ {
+ .name = "/sync-op/pread",
+ .fn = test_sync_op_pread,
+ .blkfn = test_sync_op_blk_pread,
+ }, {
+ .name = "/sync-op/pwrite",
+ .fn = test_sync_op_pwrite,
+ .blkfn = test_sync_op_blk_pwrite,
+ }, {
+ .name = "/sync-op/load_vmstate",
+ .fn = test_sync_op_load_vmstate,
+ }, {
+ .name = "/sync-op/save_vmstate",
+ .fn = test_sync_op_save_vmstate,
+ }, {
+ .name = "/sync-op/pdiscard",
+ .fn = test_sync_op_pdiscard,
+ .blkfn = test_sync_op_blk_pdiscard,
+ }, {
+ .name = "/sync-op/truncate",
+ .fn = test_sync_op_truncate,
+ }, {
+ .name = "/sync-op/block_status",
+ .fn = test_sync_op_block_status,
+ }, {
+ .name = "/sync-op/flush",
+ .fn = test_sync_op_flush,
+ .blkfn = test_sync_op_blk_flush,
+ }, {
+ .name = "/sync-op/check",
+ .fn = test_sync_op_check,
+ }, {
+ .name = "/sync-op/invalidate_cache",
+ .fn = test_sync_op_invalidate_cache,
+ },
+};
+
+/* Test synchronous operations that run in a different iothread, so we have to
+ * poll for the coroutine there to return. */
+static void test_sync_op(const void *opaque)
+{
+ const SyncOpTest *t = opaque;
+ IOThread *iothread = iothread_new();
+ AioContext *ctx = iothread_get_aio_context(iothread);
+ BlockBackend *blk;
+ BlockDriverState *bs;
+ BdrvChild *c;
+
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+ bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort);
+ bs->total_sectors = 65536 / BDRV_SECTOR_SIZE;
+ blk_insert_bs(blk, bs, &error_abort);
+ c = QLIST_FIRST(&bs->parents);
+
+ blk_set_aio_context(blk, ctx);
+ aio_context_acquire(ctx);
+ t->fn(c);
+ if (t->blkfn) {
+ t->blkfn(blk);
+ }
+ aio_context_release(ctx);
+ blk_set_aio_context(blk, qemu_get_aio_context());
+
+ bdrv_unref(bs);
+ blk_unref(blk);
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+
+ bdrv_init();
+ qemu_init_main_loop(&error_abort);
+
+ g_test_init(&argc, &argv, NULL);
+
+ for (i = 0; i < ARRAY_SIZE(sync_op_tests); i++) {
+ const SyncOpTest *t = &sync_op_tests[i];
+ g_test_add_data_func(t->name, t, test_sync_op);
+ }
+
+ return g_test_run();
+}
diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c
index 52cdd83ec0..ae38ee5ac0 100644
--- a/tests/vmgenid-test.c
+++ b/tests/vmgenid-test.c
@@ -88,7 +88,7 @@ static void read_guid_from_memory(QTestState *qts, QemuUUID *guid)
/* The GUID is in little-endian format in the guest, while QEMU
* uses big-endian. Swap after reading.
*/
- qemu_uuid_bswap(guid);
+ *guid = qemu_uuid_bswap(*guid);
}
static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid)
diff --git a/util/uuid.c b/util/uuid.c
index ebf06c049a..5787f0978c 100644
--- a/util/uuid.c
+++ b/util/uuid.c
@@ -110,10 +110,10 @@ int qemu_uuid_parse(const char *str, QemuUUID *uuid)
/* Swap from UUID format endian (BE) to the opposite or vice versa.
*/
-void qemu_uuid_bswap(QemuUUID *uuid)
+QemuUUID qemu_uuid_bswap(QemuUUID uuid)
{
- assert(QEMU_PTR_IS_ALIGNED(uuid, sizeof(uint32_t)));
- bswap32s(&uuid->fields.time_low);
- bswap16s(&uuid->fields.time_mid);
- bswap16s(&uuid->fields.time_high_and_version);
+ bswap32s(&uuid.fields.time_low);
+ bswap16s(&uuid.fields.time_mid);
+ bswap16s(&uuid.fields.time_high_and_version);
+ return uuid;
}