Block layer patches:

- vmdk: Support for blockdev-create
 - block: Apply auto-read-only for ro-whitelist drivers
 - virtio-scsi: Fixes related to attaching/detaching iothreads
 - scsi-disk: Fixed erroneously detected multipath setup with multiple
   disks created with node-names. Added device_id property.
 - block: Fix hangs in synchronous APIs with iothreads
 - block: Fix invalidate_cache error path for parent activation
 - block-backend, mirror, qcow2, vpc, vdi, qemu-iotests:
   Minor fixes and code improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJcVGReAAoJEH8JsnLIjy/WyXAQAMYGNa5sBndKTsRWvNfwn8p8
 GwiLrmhHreEGQrEoMrBl4O2uVq7B37j6yWRYeP5ZNboex/eLGgk8tnM6BxlIBldb
 ffi9g7o7cLPOFuJxGvJMR6q38TG7yEr4Mp/QkI6hZRq3brAwBsQaVmBCtgGsOaW6
 UBayRY5K/yfTdt21bZZd/78UwRz60v5YHUQYjbq5CBWeJv5WLlgwC+8k0cIeUl1f
 l/dNB21ZyVxBHpZRlqonJ0YyvW6JxMQ/93btVBwFS+D60Y0jUvlExtnEbMojozfp
 fcEPicByjRFK/jUUkXVlC0BJnBjhPNo2pMWgbMvCvOCMvMYT5JuKTXsdljs1Y2Gs
 UHJePa8GLp1Z1ZvMQOWRFb7NpTdGlbTv7FgngY6bkSLZuQ15Dc/OGpBpnYxq3y+j
 u7JbZJ+00ivygw51GwsQhu1pcgasOFxG2PJtV797+imtKnMk69wixAu25aC64sz5
 1LEiLNrnJqBhxy5k4OJSOFPH/UEzP4lcH6wjH8VMz7yvIUfMG1hqk040b7bWzRCQ
 c5ZRhvI530LGAgXRMlUmP4d7RUvzz3HFA5qA1DuGmnhA+qn3MGzvWHOurM98SDrL
 yzNXoaMt8pqQ8RgSbB6cNmRcV7anr+VES8tbffo+tbLMM7GcKYvgsioXfI8t6d+m
 Eahd3giMhdIXgEQ2LCkV
 =oaAn
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches:

- vmdk: Support for blockdev-create
- block: Apply auto-read-only for ro-whitelist drivers
- virtio-scsi: Fixes related to attaching/detaching iothreads
- scsi-disk: Fixed erroneously detected multipath setup with multiple
  disks created with node-names. Added device_id property.
- block: Fix hangs in synchronous APIs with iothreads
- block: Fix invalidate_cache error path for parent activation
- block-backend, mirror, qcow2, vpc, vdi, qemu-iotests:
  Minor fixes and code improvements

# gpg: Signature made Fri 01 Feb 2019 15:23:10 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (27 commits)
  scsi-disk: Add device_id property
  scsi-disk: Don't use empty string as device id
  qtest.py: Wait for the result of qtest commands
  block: Fix invalidate_cache error path for parent activation
  iotests/236: fix transaction kwarg order
  iotests: Filter second BLOCK_JOB_ERROR from 229
  virtio-scsi: Forbid devices with different iothreads sharing a blockdev
  scsi-disk: Acquire the AioContext in scsi_*_realize()
  virtio-scsi: Move BlockBackend back to the main AioContext on unplug
  block: Eliminate the S_1KiB, S_2KiB, ... macros
  block: Remove blk_attach_dev_legacy() / legacy_dev code
  block: Apply auto-read-only for ro-whitelist drivers
  uuid: Make qemu_uuid_bswap() take and return a QemuUUID
  block/vdi: Don't take address of fields in packed structs
  block/vpc: Don't take address of fields in packed structs
  vmdk: Reject excess extents in blockdev-create
  iotests: Add VMDK tests for blockdev-create
  iotests: Filter cid numbers in VMDK extent info
  vmdk: Implement .bdrv_co_create callback
  vmdk: Refactor vmdk_create_extent
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-02-01 17:58:27 +00:00
commit b3fc0af1ff
44 changed files with 1936 additions and 422 deletions

15
block.c
View File

@ -1438,14 +1438,20 @@ 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)) {
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);
ret = -ENOTSUP;
goto fail_opts;
}
}
/* bdrv_new() and bdrv_close() make it so */
assert(atomic_read(&bs->copy_on_read) == 0);
@ -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);
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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,

View File

@ -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 "

View File

@ -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);
}

View File

@ -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"

View File

@ -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);
}

View File

@ -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);

View File

@ -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,18 +1845,53 @@ 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:
if (blk) {
blk_unref(blk);
}
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) {
if (pbb) {
*pbb = blk;
} else {
blk_unref(blk);
blk = NULL;
}
}
return ret;
}
static int filename_decompose(const char *filename, char *path, char *prefix,
char *postfix, size_t buf_len, Error **errp)
{
@ -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,
/*
* 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)
{
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 idx = 0;
BlockBackend *new_blk = NULL;
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,93 +2083,74 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
goto exit;
}
blk = blk_new_open(full_backing, NULL, NULL,
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;
/* 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);
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);
}
/* 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);
if (ret < 0) {
error_propagate(errp, local_err);
goto exit;
}
}
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;
goto exit;
}
blk_set_allow_write_beyond_eof(new_blk, true);
ret = blk_pwrite(new_blk, desc_offset, desc, desc_len, 0);
ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write description");
goto exit;
@ -2148,12 +2158,152 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
/* 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);
ret = blk_truncate(blk, desc_len, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto exit;
}
}
ret = 0;
exit:
if (new_blk) {
blk_unref(new_blk);
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;
}
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;
}
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;
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);
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");
}
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,

View File

@ -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));

View File

@ -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.

View File

@ -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);
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, str, id_len);
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(),

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -4137,6 +4137,76 @@
'size': 'size',
'*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:
#
@ -4331,6 +4401,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }

View File

@ -66,6 +66,8 @@
'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

View File

@ -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)

View File

@ -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)

View File

@ -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"}}

View File

@ -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"

View File

@ -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

View File

@ -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'])

View File

@ -1,14 +1,18 @@
Launching source VM...
Launching destination VM...
{"return": {}}
{"return": {}}
Enabling migration QMP events on A...
{"return": {}}
Launching destination VM...
Enabling migration QMP events on B...
{"return": {}}
{"return": {}}
{"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"}}

View File

@ -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"
}

237
tests/qemu-iotests/237 Executable file
View File

@ -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)

348
tests/qemu-iotests/237.out Normal file
View File

@ -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

53
tests/qemu-iotests/239 Executable file
View File

@ -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

View File

@ -0,0 +1,4 @@
QA output created by 239
== Testing conversion to raw should success ==
*** done

129
tests/qemu-iotests/240 Executable file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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" \

View File

@ -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

View File

@ -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:
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(kwargs.items()):
if isinstance(v, dict):
od[k] = ordered_kwargs(v)
else:
od[k] = v
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)

Binary file not shown.

372
tests/test-block-iothread.c Normal file
View File

@ -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();
}

View File

@ -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)

View File

@ -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;
}