mirror of https://gitee.com/openkylin/qemu.git
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:
commit
b3fc0af1ff
15
block.c
15
block.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
57
block/vdi.c
57
block/vdi.c
|
@ -85,7 +85,8 @@
|
|||
#define BLOCK_OPT_STATIC "static"
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define DEFAULT_CLUSTER_SIZE S_1MiB
|
||||
#define DEFAULT_CLUSTER_SIZE 1048576
|
||||
/* Note: can't use 1 * MiB, because it's passed to stringify() */
|
||||
|
||||
#if defined(CONFIG_VDI_DEBUG)
|
||||
#define VDI_DEBUG 1
|
||||
|
@ -203,10 +204,10 @@ static void vdi_header_to_cpu(VdiHeader *header)
|
|||
header->block_extra = le32_to_cpu(header->block_extra);
|
||||
header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
|
||||
header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
|
||||
qemu_uuid_bswap(&header->uuid_image);
|
||||
qemu_uuid_bswap(&header->uuid_last_snap);
|
||||
qemu_uuid_bswap(&header->uuid_link);
|
||||
qemu_uuid_bswap(&header->uuid_parent);
|
||||
header->uuid_image = qemu_uuid_bswap(header->uuid_image);
|
||||
header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
|
||||
header->uuid_link = qemu_uuid_bswap(header->uuid_link);
|
||||
header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
|
||||
}
|
||||
|
||||
static void vdi_header_to_le(VdiHeader *header)
|
||||
|
@ -227,15 +228,16 @@ static void vdi_header_to_le(VdiHeader *header)
|
|||
header->block_extra = cpu_to_le32(header->block_extra);
|
||||
header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
|
||||
header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
|
||||
qemu_uuid_bswap(&header->uuid_image);
|
||||
qemu_uuid_bswap(&header->uuid_last_snap);
|
||||
qemu_uuid_bswap(&header->uuid_link);
|
||||
qemu_uuid_bswap(&header->uuid_parent);
|
||||
header->uuid_image = qemu_uuid_bswap(header->uuid_image);
|
||||
header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
|
||||
header->uuid_link = qemu_uuid_bswap(header->uuid_link);
|
||||
header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
|
||||
}
|
||||
|
||||
static void vdi_header_print(VdiHeader *header)
|
||||
{
|
||||
char uuid[37];
|
||||
char uuidstr[37];
|
||||
QemuUUID uuid;
|
||||
logout("text %s", header->text);
|
||||
logout("signature 0x%08x\n", header->signature);
|
||||
logout("header size 0x%04x\n", header->header_size);
|
||||
|
@ -254,14 +256,18 @@ static void vdi_header_print(VdiHeader *header)
|
|||
logout("block extra 0x%04x\n", header->block_extra);
|
||||
logout("blocks tot. 0x%04x\n", header->blocks_in_image);
|
||||
logout("blocks all. 0x%04x\n", header->blocks_allocated);
|
||||
qemu_uuid_unparse(&header->uuid_image, uuid);
|
||||
logout("uuid image %s\n", uuid);
|
||||
qemu_uuid_unparse(&header->uuid_last_snap, uuid);
|
||||
logout("uuid snap %s\n", uuid);
|
||||
qemu_uuid_unparse(&header->uuid_link, uuid);
|
||||
logout("uuid link %s\n", uuid);
|
||||
qemu_uuid_unparse(&header->uuid_parent, uuid);
|
||||
logout("uuid parent %s\n", uuid);
|
||||
uuid = header->uuid_image;
|
||||
qemu_uuid_unparse(&uuid, uuidstr);
|
||||
logout("uuid image %s\n", uuidstr);
|
||||
uuid = header->uuid_last_snap;
|
||||
qemu_uuid_unparse(&uuid, uuidstr);
|
||||
logout("uuid snap %s\n", uuidstr);
|
||||
uuid = header->uuid_link;
|
||||
qemu_uuid_unparse(&uuid, uuidstr);
|
||||
logout("uuid link %s\n", uuidstr);
|
||||
uuid = header->uuid_parent;
|
||||
qemu_uuid_unparse(&uuid, uuidstr);
|
||||
logout("uuid parent %s\n", uuidstr);
|
||||
}
|
||||
|
||||
static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
|
@ -368,6 +374,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
size_t bmap_size;
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
QemuUUID uuid_link, uuid_parent;
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
|
@ -395,6 +402,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
uuid_link = header.uuid_link;
|
||||
uuid_parent = header.uuid_parent;
|
||||
|
||||
if (header.disk_size % SECTOR_SIZE != 0) {
|
||||
/* 'VBoxManage convertfromraw' can create images with odd disk sizes.
|
||||
We accept them but round the disk size to the next multiple of
|
||||
|
@ -444,11 +454,11 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
(uint64_t)header.blocks_in_image * header.block_size);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (!qemu_uuid_is_null(&header.uuid_link)) {
|
||||
} else if (!qemu_uuid_is_null(&uuid_link)) {
|
||||
error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (!qemu_uuid_is_null(&header.uuid_parent)) {
|
||||
} else if (!qemu_uuid_is_null(&uuid_parent)) {
|
||||
error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
|
@ -733,6 +743,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
|||
BlockDriverState *bs_file = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
uint32_t *bmap = NULL;
|
||||
QemuUUID uuid;
|
||||
|
||||
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
|
||||
vdi_opts = &create_options->u.vdi;
|
||||
|
@ -819,8 +830,10 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
|||
if (image_type == VDI_TYPE_STATIC) {
|
||||
header.blocks_allocated = blocks;
|
||||
}
|
||||
qemu_uuid_generate(&header.uuid_image);
|
||||
qemu_uuid_generate(&header.uuid_last_snap);
|
||||
qemu_uuid_generate(&uuid);
|
||||
header.uuid_image = uuid;
|
||||
qemu_uuid_generate(&uuid);
|
||||
header.uuid_last_snap = uuid;
|
||||
/* There is no need to set header.uuid_link or header.uuid_parent here. */
|
||||
if (VDI_DEBUG) {
|
||||
vdi_header_print(&header);
|
||||
|
|
534
block/vmdk.c
534
block/vmdk.c
|
@ -1741,35 +1741,17 @@ static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||
bool flat, bool compress, bool zeroed_grain,
|
||||
QemuOpts *opts, Error **errp)
|
||||
static int vmdk_init_extent(BlockBackend *blk,
|
||||
int64_t filesize, bool flat,
|
||||
bool compress, bool zeroed_grain,
|
||||
Error **errp)
|
||||
{
|
||||
int ret, i;
|
||||
BlockBackend *blk = NULL;
|
||||
VMDK4Header header;
|
||||
Error *local_err = NULL;
|
||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||
uint32_t *gd_buf = NULL;
|
||||
int gd_buf_size;
|
||||
|
||||
ret = bdrv_create_file(filename, opts, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk = blk_new_open(filename, NULL, NULL,
|
||||
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
|
||||
&local_err);
|
||||
if (blk == NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
blk_set_allow_write_beyond_eof(blk, true);
|
||||
|
||||
if (flat) {
|
||||
ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp);
|
||||
goto exit;
|
||||
|
@ -1863,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,
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
} }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -31,6 +31,7 @@ def __init__(self, address, server=False):
|
|||
"""
|
||||
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 @@ def connect(self):
|
|||
@raise socket.error on socket connection errors
|
||||
"""
|
||||
self._sock.connect(self._address)
|
||||
self._sockfile = self._sock.makefile()
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
|
@ -57,6 +59,7 @@ def accept(self):
|
|||
@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 @@ def cmd(self, qtest_cmd):
|
|||
@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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"}}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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"}}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
QA output created by 239
|
||||
|
||||
== Testing conversion to raw should success ==
|
||||
*** done
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 @@ def get_qmp_events_filtered(self, wait=True):
|
|||
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.
|
@ -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();
|
||||
}
|
|
@ -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)
|
||||
|
|
10
util/uuid.c
10
util/uuid.c
|
@ -110,10 +110,10 @@ int qemu_uuid_parse(const char *str, QemuUUID *uuid)
|
|||
|
||||
/* Swap from UUID format endian (BE) to the opposite or vice versa.
|
||||
*/
|
||||
void qemu_uuid_bswap(QemuUUID *uuid)
|
||||
QemuUUID qemu_uuid_bswap(QemuUUID uuid)
|
||||
{
|
||||
assert(QEMU_PTR_IS_ALIGNED(uuid, sizeof(uint32_t)));
|
||||
bswap32s(&uuid->fields.time_low);
|
||||
bswap16s(&uuid->fields.time_mid);
|
||||
bswap16s(&uuid->fields.time_high_and_version);
|
||||
bswap32s(&uuid.fields.time_low);
|
||||
bswap16s(&uuid.fields.time_mid);
|
||||
bswap16s(&uuid.fields.time_high_and_version);
|
||||
return uuid;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue