Block layer patches:

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

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

Block layer patches:

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

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

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

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

27
block.c
View File

@ -1438,13 +1438,19 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
bs->read_only = !(bs->open_flags & BDRV_O_RDWR); bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
error_setg(errp, if (!bs->read_only && bdrv_is_whitelisted(drv, true)) {
!bs->read_only && bdrv_is_whitelisted(drv, true) ret = bdrv_apply_auto_read_only(bs, NULL, NULL);
? "Driver '%s' can only be used for read-only devices" } else {
: "Driver '%s' is not whitelisted", ret = -ENOTSUP;
drv->format_name); }
ret = -ENOTSUP; if (ret < 0) {
goto fail_opts; error_setg(errp,
!bs->read_only && bdrv_is_whitelisted(drv, true)
? "Driver '%s' can only be used for read-only devices"
: "Driver '%s' is not whitelisted",
drv->format_name);
goto fail_opts;
}
} }
/* bdrv_new() and bdrv_close() make it so */ /* bdrv_new() and bdrv_close() make it so */
@ -3725,6 +3731,7 @@ static void bdrv_check_co_entry(void *opaque)
{ {
CheckCo *cco = opaque; CheckCo *cco = opaque;
cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix); cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
aio_wait_kick();
} }
int bdrv_check(BlockDriverState *bs, int bdrv_check(BlockDriverState *bs,
@ -3743,7 +3750,7 @@ int bdrv_check(BlockDriverState *bs,
bdrv_check_co_entry(&cco); bdrv_check_co_entry(&cco);
} else { } else {
co = qemu_coroutine_create(bdrv_check_co_entry, &cco); 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); 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) { if (parent->role->activate) {
parent->role->activate(parent, &local_err); parent->role->activate(parent, &local_err);
if (local_err) { if (local_err) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
@ -4708,6 +4716,7 @@ static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
InvalidateCacheCo *ico = opaque; InvalidateCacheCo *ico = opaque;
bdrv_co_invalidate_cache(ico->bs, ico->errp); bdrv_co_invalidate_cache(ico->bs, ico->errp);
ico->done = true; ico->done = true;
aio_wait_kick();
} }
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) 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); bdrv_invalidate_cache_co_entry(&ico);
} else { } else {
co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico); 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); BDRV_POLL_WHILE(bs, !ico.done);
} }
} }

View File

@ -295,10 +295,9 @@ static void blk_log_writes_refresh_filename(BlockDriverState *bs,
qdict_put_str(opts, "driver", "blklogwrites"); qdict_put_str(opts, "driver", "blklogwrites");
qobject_ref(bs->file->bs->full_open_options); 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); qobject_ref(s->log_file->bs->full_open_options);
qdict_put_obj(opts, "log", qdict_put(opts, "log", s->log_file->bs->full_open_options);
QOBJECT(s->log_file->bs->full_open_options));
qdict_put_int(opts, "log-sector-size", s->sectorsize); qdict_put_int(opts, "log-sector-size", s->sectorsize);
bs->full_open_options = opts; bs->full_open_options = opts;

View File

@ -47,9 +47,7 @@ struct BlockBackend {
QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */
BlockBackendPublic public; BlockBackendPublic public;
void *dev; /* attached device model, if any */ DeviceState *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 */
const BlockDevOps *dev_ops; const BlockDevOps *dev_ops;
void *dev_opaque; 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; *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) { if (blk->dev) {
return -EBUSY; return -EBUSY;
@ -851,40 +853,16 @@ static int blk_do_attach_dev(BlockBackend *blk, void *dev)
blk_ref(blk); blk_ref(blk);
blk->dev = dev; blk->dev = dev;
blk->legacy_dev = false;
blk_iostatus_reset(blk); blk_iostatus_reset(blk);
return 0; 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. * Detach device model @dev from @blk.
* @dev must be currently attached to @blk. * @dev must be currently attached to @blk.
*/ */
void blk_detach_dev(BlockBackend *blk, void *dev) void blk_detach_dev(BlockBackend *blk, DeviceState *dev)
/* TODO change to DeviceState *dev when all users are qdevified */
{ {
assert(blk->dev == dev); assert(blk->dev == dev);
blk->dev = NULL; 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. * Return the device model attached to @blk if any, else null.
*/ */
void *blk_get_attached_dev(BlockBackend *blk) DeviceState *blk_get_attached_dev(BlockBackend *blk)
/* TODO change to return DeviceState * when all users are qdevified */
{ {
return blk->dev; return blk->dev;
} }
@ -908,10 +885,7 @@ void *blk_get_attached_dev(BlockBackend *blk)
* device attached to the BlockBackend. */ * device attached to the BlockBackend. */
char *blk_get_attached_dev_id(BlockBackend *blk) char *blk_get_attached_dev_id(BlockBackend *blk)
{ {
DeviceState *dev; DeviceState *dev = blk->dev;
assert(!blk->legacy_dev);
dev = blk->dev;
if (!dev) { if (!dev) {
return g_strdup(""); return g_strdup("");
@ -949,11 +923,6 @@ BlockBackend *blk_by_dev(void *dev)
void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops,
void *opaque) 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_ops = ops;
blk->dev_opaque = opaque; 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; bool tray_was_open, tray_is_open;
Error *local_err = NULL; Error *local_err = NULL;
assert(!blk->legacy_dev);
tray_was_open = blk_dev_is_tray_open(blk); tray_was_open = blk_dev_is_tray_open(blk);
blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err);
if (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, rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags); qiov, rwco->flags);
aio_wait_kick();
} }
static void blk_write_entry(void *opaque) 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, rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags); qiov, rwco->flags);
aio_wait_kick();
} }
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, 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, rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
qiov->iov[0].iov_base); qiov->iov[0].iov_base);
aio_wait_kick();
} }
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) 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; BlkRwCo *rwco = opaque;
rwco->ret = blk_co_flush(rwco->blk); rwco->ret = blk_co_flush(rwco->blk);
aio_wait_kick();
} }
int blk_flush(BlockBackend *blk) int blk_flush(BlockBackend *blk)
@ -1779,9 +1750,6 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
char *id; char *id;
/* blk_eject is only called by qdevified devices */
assert(!blk->legacy_dev);
if (bs) { if (bs) {
bdrv_eject(bs, eject_flag); bdrv_eject(bs, eject_flag);
} }
@ -2018,6 +1986,7 @@ static void blk_pdiscard_entry(void *opaque)
QEMUIOVector *qiov = rwco->iobuf; QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
aio_wait_kick();
} }
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)

View File

@ -806,6 +806,7 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
rwco->qiov->size, rwco->qiov, rwco->qiov->size, rwco->qiov,
rwco->flags); 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->offset, data->bytes,
data->pnum, data->map, data->file); data->pnum, data->map, data->file);
data->done = true; data->done = true;
aio_wait_kick();
} }
/* /*
@ -2438,6 +2440,7 @@ static void coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque)
{ {
BdrvVmstateCo *co = opaque; BdrvVmstateCo *co = opaque;
co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read); co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read);
aio_wait_kick();
} }
static inline int static inline int
@ -2559,6 +2562,7 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
FlushCo *rwco = opaque; FlushCo *rwco = opaque;
rwco->ret = bdrv_co_flush(rwco->bs); rwco->ret = bdrv_co_flush(rwco->bs);
aio_wait_kick();
} }
int coroutine_fn bdrv_co_flush(BlockDriverState *bs) 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; DiscardCo *rwco = opaque;
rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes); 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) 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; TruncateCo *tco = opaque;
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc,
tco->errp); tco->errp);
aio_wait_kick();
} }
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, 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); bdrv_truncate_co_entry(&tco);
} else { } else {
co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); 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); BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE);
} }

View File

@ -1612,6 +1612,14 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
goto fail; 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() */ /* Required permissions are already taken with blk_new() */
block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL, block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL,
&error_abort); &error_abort);
@ -1649,6 +1657,9 @@ fail:
g_free(s->replaces); g_free(s->replaces);
blk_unref(s->target); blk_unref(s->target);
bs_opaque->job = NULL; bs_opaque->job = NULL;
if (s->dirty_bitmap) {
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
}
job_early_fail(&s->common.job); job_early_fail(&s->common.job);
} }

View File

@ -119,6 +119,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
s->quit = true; s->quit = true;
nbd_recv_coroutines_wake_all(s); nbd_recv_coroutines_wake_all(s);
s->read_reply_co = NULL; s->read_reply_co = NULL;
aio_wait_kick();
} }
static int nbd_co_send_request(BlockDriverState *bs, static int nbd_co_send_request(BlockDriverState *bs,

View File

@ -390,6 +390,7 @@ static void nvme_cmd_sync_cb(void *opaque, int ret)
{ {
int *pret = opaque; int *pret = opaque;
*pret = ret; *pret = ret;
aio_wait_kick();
} }
static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q, static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,

View File

@ -368,6 +368,9 @@ static int alloc_refcount_block(BlockDriverState *bs,
return new_block; 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 we're allocating the block at offset 0 then something is wrong */
if (new_block == 0) { if (new_block == 0) {
qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "

View File

@ -1671,6 +1671,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
/* From bdrv_co_create. */ /* From bdrv_co_create. */
qcow2_open_entry(&qoc); qcow2_open_entry(&qoc);
} else { } else {
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc)); qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
} }

View File

@ -50,11 +50,11 @@
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size /* 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) */ * (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 /* 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) */ * (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 /* Allow for an average of 1k per snapshot table entry, should be plenty of
* space for snapshot names and IDs */ * space for snapshot names and IDs */
@ -81,15 +81,15 @@
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
#ifdef CONFIG_LINUX #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 */ #define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */
#else #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 */ /* Cache clean interval is currently available only on Linux, so must be 0 */
#define DEFAULT_CACHE_CLEAN_INTERVAL 0 #define DEFAULT_CACHE_CLEAN_INTERVAL 0
#endif #endif
#define DEFAULT_CLUSTER_SIZE S_64KiB #define DEFAULT_CLUSTER_SIZE 65536
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"

View File

@ -559,6 +559,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
if (qemu_in_coroutine()) { if (qemu_in_coroutine()) {
bdrv_qed_open_entry(&qoc); bdrv_qed_open_entry(&qoc);
} else { } else {
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
} }

View File

@ -85,7 +85,8 @@
#define BLOCK_OPT_STATIC "static" #define BLOCK_OPT_STATIC "static"
#define SECTOR_SIZE 512 #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) #if defined(CONFIG_VDI_DEBUG)
#define VDI_DEBUG 1 #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->block_extra = le32_to_cpu(header->block_extra);
header->blocks_in_image = le32_to_cpu(header->blocks_in_image); header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
header->blocks_allocated = le32_to_cpu(header->blocks_allocated); header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
qemu_uuid_bswap(&header->uuid_image); header->uuid_image = qemu_uuid_bswap(header->uuid_image);
qemu_uuid_bswap(&header->uuid_last_snap); header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
qemu_uuid_bswap(&header->uuid_link); header->uuid_link = qemu_uuid_bswap(header->uuid_link);
qemu_uuid_bswap(&header->uuid_parent); header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
} }
static void vdi_header_to_le(VdiHeader *header) 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->block_extra = cpu_to_le32(header->block_extra);
header->blocks_in_image = cpu_to_le32(header->blocks_in_image); header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
header->blocks_allocated = cpu_to_le32(header->blocks_allocated); header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
qemu_uuid_bswap(&header->uuid_image); header->uuid_image = qemu_uuid_bswap(header->uuid_image);
qemu_uuid_bswap(&header->uuid_last_snap); header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap);
qemu_uuid_bswap(&header->uuid_link); header->uuid_link = qemu_uuid_bswap(header->uuid_link);
qemu_uuid_bswap(&header->uuid_parent); header->uuid_parent = qemu_uuid_bswap(header->uuid_parent);
} }
static void vdi_header_print(VdiHeader *header) static void vdi_header_print(VdiHeader *header)
{ {
char uuid[37]; char uuidstr[37];
QemuUUID uuid;
logout("text %s", header->text); logout("text %s", header->text);
logout("signature 0x%08x\n", header->signature); logout("signature 0x%08x\n", header->signature);
logout("header size 0x%04x\n", header->header_size); 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("block extra 0x%04x\n", header->block_extra);
logout("blocks tot. 0x%04x\n", header->blocks_in_image); logout("blocks tot. 0x%04x\n", header->blocks_in_image);
logout("blocks all. 0x%04x\n", header->blocks_allocated); logout("blocks all. 0x%04x\n", header->blocks_allocated);
qemu_uuid_unparse(&header->uuid_image, uuid); uuid = header->uuid_image;
logout("uuid image %s\n", uuid); qemu_uuid_unparse(&uuid, uuidstr);
qemu_uuid_unparse(&header->uuid_last_snap, uuid); logout("uuid image %s\n", uuidstr);
logout("uuid snap %s\n", uuid); uuid = header->uuid_last_snap;
qemu_uuid_unparse(&header->uuid_link, uuid); qemu_uuid_unparse(&uuid, uuidstr);
logout("uuid link %s\n", uuid); logout("uuid snap %s\n", uuidstr);
qemu_uuid_unparse(&header->uuid_parent, uuid); uuid = header->uuid_link;
logout("uuid parent %s\n", uuid); 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, 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; size_t bmap_size;
int ret; int ret;
Error *local_err = NULL; Error *local_err = NULL;
QemuUUID uuid_link, uuid_parent;
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
false, errp); false, errp);
@ -395,6 +402,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
uuid_link = header.uuid_link;
uuid_parent = header.uuid_parent;
if (header.disk_size % SECTOR_SIZE != 0) { if (header.disk_size % SECTOR_SIZE != 0) {
/* 'VBoxManage convertfromraw' can create images with odd disk sizes. /* 'VBoxManage convertfromraw' can create images with odd disk sizes.
We accept them but round the disk size to the next multiple of 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); (uint64_t)header.blocks_in_image * header.block_size);
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; 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)"); error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; 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)"); error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; goto fail;
@ -733,6 +743,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
BlockDriverState *bs_file = NULL; BlockDriverState *bs_file = NULL;
BlockBackend *blk = NULL; BlockBackend *blk = NULL;
uint32_t *bmap = NULL; uint32_t *bmap = NULL;
QemuUUID uuid;
assert(create_options->driver == BLOCKDEV_DRIVER_VDI); assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
vdi_opts = &create_options->u.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) { if (image_type == VDI_TYPE_STATIC) {
header.blocks_allocated = blocks; header.blocks_allocated = blocks;
} }
qemu_uuid_generate(&header.uuid_image); qemu_uuid_generate(&uuid);
qemu_uuid_generate(&header.uuid_last_snap); 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. */ /* There is no need to set header.uuid_link or header.uuid_parent here. */
if (VDI_DEBUG) { if (VDI_DEBUG) {
vdi_header_print(&header); vdi_header_print(&header);

View File

@ -1741,35 +1741,17 @@ static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs,
return ret; return ret;
} }
static int vmdk_create_extent(const char *filename, int64_t filesize, static int vmdk_init_extent(BlockBackend *blk,
bool flat, bool compress, bool zeroed_grain, int64_t filesize, bool flat,
QemuOpts *opts, Error **errp) bool compress, bool zeroed_grain,
Error **errp)
{ {
int ret, i; int ret, i;
BlockBackend *blk = NULL;
VMDK4Header header; VMDK4Header header;
Error *local_err = NULL;
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count; uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
uint32_t *gd_buf = NULL; uint32_t *gd_buf = NULL;
int gd_buf_size; 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) { if (flat) {
ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp); ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp);
goto exit; goto exit;
@ -1863,18 +1845,53 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
gd_buf, gd_buf_size, 0); gd_buf, gd_buf_size, 0);
if (ret < 0) { if (ret < 0) {
error_setg(errp, QERR_IO_ERROR); error_setg(errp, QERR_IO_ERROR);
goto exit;
} }
ret = 0; ret = 0;
exit: exit:
if (blk) {
blk_unref(blk);
}
g_free(gd_buf); g_free(gd_buf);
return ret; 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, static int filename_decompose(const char *filename, char *path, char *prefix,
char *postfix, size_t buf_len, Error **errp) 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; return VMDK_OK;
} }
static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts, /*
Error **errp) * idx == 0: get or create the descriptor file (also the image file if in a
* non-split format.
* idx >= 1: get the n-th extent if in a split subformat
*/
typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size,
int idx,
bool flat,
bool split,
bool compress,
bool zeroed_grain,
void *opaque,
Error **errp);
static void vmdk_desc_add_extent(GString *desc,
const char *extent_line_fmt,
int64_t size, const char *filename)
{ {
int idx = 0; char *basename = g_path_get_basename(filename);
BlockBackend *new_blk = NULL;
g_string_append_printf(desc, extent_line_fmt,
DIV_ROUND_UP(size, BDRV_SECTOR_SIZE), basename);
g_free(basename);
}
static int coroutine_fn vmdk_co_do_create(int64_t size,
BlockdevVmdkSubformat subformat,
BlockdevVmdkAdapterType adapter_type,
const char *backing_file,
const char *hw_version,
bool compat6,
bool zeroed_grain,
vmdk_create_extent_fn extent_fn,
void *opaque,
Error **errp)
{
int extent_idx;
BlockBackend *blk = NULL;
BlockBackend *extent_blk;
Error *local_err = NULL; Error *local_err = NULL;
char *desc = 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; int ret = 0;
bool flat, split, compress; bool flat, split, compress;
GString *ext_desc_lines; 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 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); char *parent_desc_line = g_malloc0(BUF_SIZE);
uint32_t parent_cid = 0xffffffff; uint32_t parent_cid = 0xffffffff;
uint32_t number_heads = 16; uint32_t number_heads = 16;
bool zeroed_grain = false;
uint32_t desc_offset = 0, desc_len; uint32_t desc_offset = 0, desc_len;
const char desc_template[] = const char desc_template[] =
"# Disk DescriptorFile\n" "# 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); ext_desc_lines = g_string_new(NULL);
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
ret = -EINVAL;
goto exit;
}
/* Read out options */ /* Read out options */
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), if (compat6) {
BDRV_SECTOR_SIZE); if (hw_version) {
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")) {
error_setg(errp, error_setg(errp,
"compat6 cannot be enabled with hwversion set"); "compat6 cannot be enabled with hwversion set");
ret = -EINVAL; ret = -EINVAL;
goto exit; goto exit;
} }
g_free(hw_version); hw_version = "6";
hw_version = g_strdup("6");
} }
if (strcmp(hw_version, "undefined") == 0) { if (!hw_version) {
g_free(hw_version); hw_version = "4";
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 (!adapter_type) { if (adapter_type != BLOCKDEV_VMDK_ADAPTER_TYPE_IDE) {
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) {
/* that's the number of heads with which vmware operates when /* that's the number of heads with which vmware operates when
creating, exporting, etc. vmdk files with a non-ide adapter type */ creating, exporting, etc. vmdk files with a non-ide adapter type */
number_heads = 255; number_heads = 255;
} }
if (!fmt) { split = (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT) ||
/* Default format to monolithicSparse */ (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE);
fmt = g_strdup("monolithicSparse"); flat = (subformat == BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT) ||
} else if (strcmp(fmt, "monolithicFlat") && (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT);
strcmp(fmt, "monolithicSparse") && compress = subformat == BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED;
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");
if (flat) { if (flat) {
desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n"; extent_line_fmt = "RW %" PRId64 " FLAT \"%s\" 0\n";
} else { } else {
desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n"; extent_line_fmt = "RW %" PRId64 " SPARSE \"%s\"\n";
} }
if (flat && backing_file) { if (flat && backing_file) {
error_setg(errp, "Flat image can't have 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; ret = -ENOTSUP;
goto exit; 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) { if (backing_file) {
BlockBackend *blk; BlockBackend *backing;
char *full_backing = g_new0(char, PATH_MAX); 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, full_backing, PATH_MAX,
&local_err); &local_err);
if (local_err) { if (local_err) {
@ -2054,93 +2083,74 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
goto exit; goto exit;
} }
blk = blk_new_open(full_backing, NULL, NULL, backing = blk_new_open(full_backing, NULL, NULL,
BDRV_O_NO_BACKING, errp); BDRV_O_NO_BACKING, errp);
g_free(full_backing); g_free(full_backing);
if (blk == NULL) { if (backing == NULL) {
ret = -EIO; ret = -EIO;
goto exit; goto exit;
} }
if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) { if (strcmp(blk_bs(backing)->drv->format_name, "vmdk")) {
blk_unref(blk); error_setg(errp, "Invalid backing file format: %s. Must be vmdk",
blk_bs(backing)->drv->format_name);
blk_unref(backing);
ret = -EINVAL; ret = -EINVAL;
goto exit; goto exit;
} }
ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid); ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid);
blk_unref(blk); blk_unref(backing);
if (ret) { if (ret) {
error_setg(errp, "Failed to read parent CID");
goto exit; goto exit;
} }
snprintf(parent_desc_line, BUF_SIZE, snprintf(parent_desc_line, BUF_SIZE,
"parentFileNameHint=\"%s\"", backing_file); "parentFileNameHint=\"%s\"", backing_file);
} }
extent_idx = 1;
/* Create extents */ while (created_size < size) {
filesize = total_size; int64_t cur_size = MIN(size - created_size, extent_size);
while (filesize > 0) { extent_blk = extent_fn(cur_size, extent_idx, flat, split, compress,
int64_t size = filesize; zeroed_grain, opaque, errp);
if (!extent_blk) {
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)) {
ret = -EINVAL; ret = -EINVAL;
goto exit; goto exit;
} }
filesize -= size; vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, cur_size,
blk_bs(extent_blk)->filename);
/* Format description line */ created_size += cur_size;
snprintf(desc_line, BUF_SIZE, extent_idx++;
desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename); blk_unref(extent_blk);
g_string_append(ext_desc_lines, desc_line);
} }
/* Check whether we got excess extents */
extent_blk = extent_fn(-1, extent_idx, flat, split, compress, zeroed_grain,
opaque, NULL);
if (extent_blk) {
blk_unref(extent_blk);
error_setg(errp, "List of extents contains unused extents");
ret = -EINVAL;
goto exit;
}
/* generate descriptor file */ /* generate descriptor file */
desc = g_strdup_printf(desc_template, desc = g_strdup_printf(desc_template,
g_random_int(), g_random_int(),
parent_cid, parent_cid,
fmt, BlockdevVmdkSubformat_str(subformat),
parent_desc_line, parent_desc_line,
ext_desc_lines->str, ext_desc_lines->str,
hw_version, hw_version,
total_size / size /
(int64_t)(63 * number_heads * BDRV_SECTOR_SIZE), (int64_t)(63 * number_heads * BDRV_SECTOR_SIZE),
number_heads, number_heads,
adapter_type); BlockdevVmdkAdapterType_str(adapter_type));
desc_len = strlen(desc); desc_len = strlen(desc);
/* the descriptor offset = 0x200 */ /* the descriptor offset = 0x200 */
if (!split && !flat) { if (!split && !flat) {
desc_offset = 0x200; 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, ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0);
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);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write description"); error_setg_errno(errp, -ret, "Could not write description");
goto exit; 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 /* bdrv_pwrite write padding zeros to align to sector, we don't need that
* for description file */ * for description file */
if (desc_offset == 0) { 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: exit:
if (new_blk) { if (blk) {
blk_unref(new_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(adapter_type);
g_free(backing_file); g_free(backing_file);
g_free(hw_version); g_free(hw_version);
@ -2166,7 +2316,86 @@ exit:
g_free(ext_filename); g_free(ext_filename);
g_free(desc_filename); g_free(desc_filename);
g_free(parent_desc_line); 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; return ret;
} }
@ -2434,6 +2663,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes,
.bdrv_close = vmdk_close, .bdrv_close = vmdk_close,
.bdrv_co_create_opts = vmdk_co_create_opts, .bdrv_co_create_opts = vmdk_co_create_opts,
.bdrv_co_create = vmdk_co_create,
.bdrv_co_flush_to_disk = vmdk_co_flush, .bdrv_co_flush_to_disk = vmdk_co_flush,
.bdrv_co_block_status = vmdk_co_block_status, .bdrv_co_block_status = vmdk_co_block_status,
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,

View File

@ -979,6 +979,7 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
int64_t total_size; int64_t total_size;
int disk_type; int disk_type;
int ret = -EIO; int ret = -EIO;
QemuUUID uuid;
assert(opts->driver == BLOCKDEV_DRIVER_VPC); assert(opts->driver == BLOCKDEV_DRIVER_VPC);
vpc_opts = &opts->u.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); 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)); footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE));

View File

@ -30,8 +30,7 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid,
* first, since that's what the guest expects * first, since that's what the guest expects
*/ */
g_array_set_size(guid, VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data)); g_array_set_size(guid, VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data));
guid_le = vms->guid; guid_le = qemu_uuid_bswap(vms->guid);
qemu_uuid_bswap(&guid_le);
/* The GUID is written at a fixed offset into the fw_cfg file /* The GUID is written at a fixed offset into the fw_cfg file
* in order to implement the "OVMF SDT Header probe suppressor" * in order to implement the "OVMF SDT Header probe suppressor"
* see docs/specs/vmgenid.txt for more details * 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. * however, will expect the fields to be little-endian.
* Perform a byte swap immediately before writing. * Perform a byte swap immediately before writing.
*/ */
guid_le = vms->guid; guid_le = qemu_uuid_bswap(vms->guid);
qemu_uuid_bswap(&guid_le);
/* The GUID is written at a fixed offset into the fw_cfg file /* The GUID is written at a fixed offset into the fw_cfg file
* in order to implement the "OVMF SDT Header probe suppressor" * in order to implement the "OVMF SDT Header probe suppressor"
* see docs/specs/vmgenid.txt for more details. * see docs/specs/vmgenid.txt for more details.

View File

@ -104,6 +104,7 @@ typedef struct SCSIDiskState
char *serial; char *serial;
char *vendor; char *vendor;
char *product; char *product;
char *device_id;
bool tray_open; bool tray_open;
bool tray_locked; 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 */ case 0x83: /* Device identification page, mandatory */
{ {
const char *str = s->serial ?: blk_name(s->qdev.conf.blk); int id_len = s->device_id ? MIN(strlen(s->device_id), 255 - 8) : 0;
int max_len = s->serial ? 20 : 255 - 8;
int id_len = strlen(str);
if (id_len > max_len) {
id_len = max_len;
}
DPRINTF("Inquiry EVPD[Device identification] " DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %zd\n", req->cmd.xfer); "buffer size %zd\n", req->cmd.xfer);
outbuf[buflen++] = 0x2; /* ASCII */ if (id_len) {
outbuf[buflen++] = 0; /* not officially assigned */ outbuf[buflen++] = 0x2; /* ASCII */
outbuf[buflen++] = 0; /* reserved */ outbuf[buflen++] = 0; /* not officially assigned */
outbuf[buflen++] = id_len; /* length of data following */ outbuf[buflen++] = 0; /* reserved */
memcpy(outbuf + buflen, str, id_len); outbuf[buflen++] = id_len; /* length of data following */
buflen += id_len; memcpy(outbuf + buflen, s->device_id, id_len);
buflen += id_len;
}
if (s->qdev.wwn) { if (s->qdev.wwn) {
outbuf[buflen++] = 0x1; /* Binary */ outbuf[buflen++] = 0x1; /* Binary */
@ -2361,6 +2359,16 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
if (!s->vendor) { if (!s->vendor) {
s->vendor = g_strdup("QEMU"); 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)) { if (blk_is_sg(s->qdev.conf.blk)) {
error_setg(errp, "unwanted /dev/sg*"); 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) static void scsi_hd_realize(SCSIDevice *dev, Error **errp)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
AioContext *ctx = NULL;
/* can happen for devices without drive. The error message for missing /* can happen for devices without drive. The error message for missing
* backend will be issued in scsi_realize * backend will be issued in scsi_realize
*/ */
if (s->qdev.conf.blk) { if (s->qdev.conf.blk) {
ctx = blk_get_aio_context(s->qdev.conf.blk);
aio_context_acquire(ctx);
blkconf_blocksizes(&s->qdev.conf); blkconf_blocksizes(&s->qdev.conf);
} }
s->qdev.blocksize = s->qdev.conf.logical_block_size; 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"); s->product = g_strdup("QEMU HARDDISK");
} }
scsi_realize(&s->qdev, errp); scsi_realize(&s->qdev, errp);
if (ctx) {
aio_context_release(ctx);
}
} }
static void scsi_cd_realize(SCSIDevice *dev, Error **errp) static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
AioContext *ctx;
int ret; int ret;
if (!dev->conf.blk) { if (!dev->conf.blk) {
@ -2408,6 +2423,8 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
assert(ret == 0); assert(ret == 0);
} }
ctx = blk_get_aio_context(dev->conf.blk);
aio_context_acquire(ctx);
s->qdev.blocksize = 2048; s->qdev.blocksize = 2048;
s->qdev.type = TYPE_ROM; s->qdev.type = TYPE_ROM;
s->features |= 1 << SCSI_DISK_F_REMOVABLE; 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"); s->product = g_strdup("QEMU CD-ROM");
} }
scsi_realize(&s->qdev, errp); scsi_realize(&s->qdev, errp);
aio_context_release(ctx);
} }
static void scsi_disk_realize(SCSIDevice *dev, Error **errp) 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) static void scsi_block_realize(SCSIDevice *dev, Error **errp)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
AioContext *ctx;
int sg_version; int sg_version;
int rc; int rc;
@ -2567,6 +2586,9 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
"be removed in a future version"); "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) */ /* 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); rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version);
if (rc < 0) { if (rc < 0) {
@ -2574,18 +2596,18 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
if (rc != -EPERM) { if (rc != -EPERM) {
error_append_hint(errp, "Is this a SCSI device?\n"); error_append_hint(errp, "Is this a SCSI device?\n");
} }
return; goto out;
} }
if (sg_version < 30000) { if (sg_version < 30000) {
error_setg(errp, "scsi generic interface too old"); error_setg(errp, "scsi generic interface too old");
return; goto out;
} }
/* get device type from INQUIRY data */ /* get device type from INQUIRY data */
rc = get_device_type(s); rc = get_device_type(s);
if (rc < 0) { if (rc < 0) {
error_setg(errp, "INQUIRY failed"); error_setg(errp, "INQUIRY failed");
return; goto out;
} }
/* Make a guess for the block size, we'll fix it when the guest sends. /* 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_realize(&s->qdev, errp);
scsi_generic_read_device_inquiry(&s->qdev); scsi_generic_read_device_inquiry(&s->qdev);
out:
aio_context_release(ctx);
} }
typedef struct SCSIBlockReq { typedef struct SCSIBlockReq {
@ -2902,7 +2927,9 @@ static const TypeInfo scsi_disk_base_info = {
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \
DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ 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[] = { static Property scsi_hd_properties[] = {
DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_SCSI_DISK_PROPERTIES(),

View File

@ -791,9 +791,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
SCSIDevice *sd = SCSI_DEVICE(dev); SCSIDevice *sd = SCSI_DEVICE(dev);
if (s->ctx && !s->dataplane_fenced) { if (s->ctx && !s->dataplane_fenced) {
AioContext *ctx;
if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
return; 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); virtio_scsi_acquire(s);
blk_set_aio_context(sd->conf.blk, s->ctx); blk_set_aio_context(sd->conf.blk, s->ctx);
virtio_scsi_release(s); virtio_scsi_release(s);
@ -824,6 +831,12 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
virtio_scsi_release(s); 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); qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
} }

View File

@ -17,77 +17,4 @@
#define PiB (INT64_C(1) << 50) #define PiB (INT64_C(1) << 50)
#define EiB (INT64_C(1) << 60) #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 #endif

View File

@ -56,6 +56,6 @@ char *qemu_uuid_unparse_strdup(const QemuUUID *uuid);
int qemu_uuid_parse(const char *str, QemuUUID *uuid); int qemu_uuid_parse(const char *str, QemuUUID *uuid);
void qemu_uuid_bswap(QemuUUID *uuid); QemuUUID qemu_uuid_bswap(QemuUUID uuid);
#endif #endif

View File

@ -110,9 +110,8 @@ void blk_iostatus_disable(BlockBackend *blk);
void blk_iostatus_reset(BlockBackend *blk); void blk_iostatus_reset(BlockBackend *blk);
void blk_iostatus_set_err(BlockBackend *blk, int error); void blk_iostatus_set_err(BlockBackend *blk, int error);
int blk_attach_dev(BlockBackend *blk, DeviceState *dev); int blk_attach_dev(BlockBackend *blk, DeviceState *dev);
void blk_attach_dev_legacy(BlockBackend *blk, void *dev); void blk_detach_dev(BlockBackend *blk, DeviceState *dev);
void blk_detach_dev(BlockBackend *blk, void *dev); DeviceState *blk_get_attached_dev(BlockBackend *blk);
void *blk_get_attached_dev(BlockBackend *blk);
char *blk_get_attached_dev_id(BlockBackend *blk); char *blk_get_attached_dev_id(BlockBackend *blk);
BlockBackend *blk_by_dev(void *dev); BlockBackend *blk_by_dev(void *dev);
BlockBackend *blk_by_qdev_id(const char *id, Error **errp); BlockBackend *blk_by_qdev_id(const char *id, Error **errp);

View File

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

View File

@ -63,13 +63,15 @@
'query-tpm-types', 'query-tpm-types',
'ringbuf-read' ], 'ringbuf-read' ],
'name-case-whitelist': [ 'name-case-whitelist': [
'ACPISlotType', # DIMM, visible through query-acpi-ospm-status 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
'CpuInfoMIPS', # PC, visible through query-cpu 'CpuInfoMIPS', # PC, visible through query-cpu
'CpuInfoTricore', # PC, visible through query-cpu 'CpuInfoTricore', # PC, visible through query-cpu
'QapiErrorClass', # all members, visible through errors 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings
'UuidInfo', # UUID, visible through query-uuid 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings
'X86CPURegister32', # all members, visible indirectly through qom-get 'QapiErrorClass', # all members, visible through errors
'q_obj_CpuInfo-base' # CPU, visible through query-cpu 'UuidInfo', # UUID, visible through query-uuid
'X86CPURegister32', # all members, visible indirectly through qom-get
'q_obj_CpuInfo-base' # CPU, visible through query-cpu
] } } ] } }
# Documentation generated with qapi-gen.py is in source order, with # Documentation generated with qapi-gen.py is in source order, with

View File

@ -31,6 +31,7 @@ class QEMUQtestProtocol(object):
""" """
self._address = address self._address = address
self._sock = self._get_sock() self._sock = self._get_sock()
self._sockfile = None
if server: if server:
self._sock.bind(self._address) self._sock.bind(self._address)
self._sock.listen(1) self._sock.listen(1)
@ -49,6 +50,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors @raise socket.error on socket connection errors
""" """
self._sock.connect(self._address) self._sock.connect(self._address)
self._sockfile = self._sock.makefile()
def accept(self): def accept(self):
""" """
@ -57,6 +59,7 @@ class QEMUQtestProtocol(object):
@raise socket.error on socket connection errors @raise socket.error on socket connection errors
""" """
self._sock, _ = self._sock.accept() self._sock, _ = self._sock.accept()
self._sockfile = self._sock.makefile()
def cmd(self, qtest_cmd): def cmd(self, qtest_cmd):
""" """
@ -65,9 +68,12 @@ class QEMUQtestProtocol(object):
@param qtest_cmd: qtest command text to be sent @param qtest_cmd: qtest command text to be sent
""" """
self._sock.sendall((qtest_cmd + "\n").encode('utf-8')) self._sock.sendall((qtest_cmd + "\n").encode('utf-8'))
resp = self._sockfile.readline()
return resp
def close(self): def close(self):
self._sock.close() self._sock.close()
self._sockfile.close()
def settimeout(self, timeout): def settimeout(self, timeout):
self._sock.settimeout(timeout) self._sock.settimeout(timeout)

View File

@ -73,6 +73,7 @@ check-unit-y += tests/test-bdrv-drain$(EXESUF)
check-unit-y += tests/test-blockjob$(EXESUF) check-unit-y += tests/test-blockjob$(EXESUF)
check-unit-y += tests/test-blockjob-txn$(EXESUF) check-unit-y += tests/test-blockjob-txn$(EXESUF)
check-unit-y += tests/test-block-backend$(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-image-locking$(EXESUF)
check-unit-y += tests/test-x86-cpuid$(EXESUF) check-unit-y += tests/test-x86-cpuid$(EXESUF)
# all code tested by test-x86-cpuid is inside topology.h # 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$(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-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-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-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-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)

View File

@ -28,7 +28,7 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "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"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
{"return": {}} {"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": {}} {"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": "waiting", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "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": "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"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
{"return": {}} {"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": {}} {"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": "waiting", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}

View File

@ -81,11 +81,15 @@ echo
echo '=== Force cancel job paused in error state ===' echo '=== Force cancel job paused in error state ==='
echo 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 \ success_or_failure="y" _send_qemu_cmd $QEMU_HANDLE \
"{'execute': 'block-job-cancel', "{'execute': 'block-job-cancel',
'arguments': { 'device': 'testdisk', 'arguments': { 'device': 'testdisk',
'force': true}}" \ 'force': true}}" \
"BLOCK_JOB_CANCELLED" "Assertion" "BLOCK_JOB_CANCELLED" "Assertion" \
| grep -v '"BLOCK_JOB_ERROR"'
# success, all done # success, all done
echo "*** done" echo "*** done"

View File

@ -17,7 +17,6 @@ wrote 2097152/2097152 bytes at offset 0
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}}
{"return": {}} {"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": "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"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "testdisk", "len": 2097152, "offset": 1048576, "speed": 0, "type": "mirror"}}
*** done *** done

View File

@ -26,6 +26,22 @@ import os
iotests.verify_image_format(supported_fmts=['qcow2']) iotests.verify_image_format(supported_fmts=['qcow2'])
iotests.verify_platform(['linux']) 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, \ with iotests.FilePath('img') as img_path, \
iotests.FilePath('backing') as backing_path, \ iotests.FilePath('backing') as backing_path, \
iotests.FilePath('mig_fifo_a') as fifo_a, \ 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)) .add_blockdev('%s,file=drive0-backing-file,node-name=drive0-backing' % (iotests.imgfmt))
.launch()) .launch())
enable_migration_events(vm_a, 'A')
iotests.log('Launching destination VM...') iotests.log('Launching destination VM...')
(vm_b.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path)) (vm_b.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path))
.add_blockdev('%s,file=drive0-file,node-name=drive0' % (iotests.imgfmt)) .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)) .add_incoming("exec: cat '%s'" % (fifo_a))
.launch()) .launch())
enable_migration_events(vm_b, 'B')
# Add a child node that was created after the parent node. The reverse case # Add a child node that was created after the parent node. The reverse case
# is covered by the -blockdev options above. # is covered by the -blockdev options above.
iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing', 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', iotests.log(vm_b.qmp('blockdev-snapshot', node='drive0-backing',
overlay='drive0')) 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('Starting migration to B...')
iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo_a))) iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo_a)))
with iotests.Timeout(3, 'Migration does not complete'): with iotests.Timeout(3, 'Migration does not complete'):
while True: # Wait for the source first (which includes setup=setup)
event = vm_a.event_wait('MIGRATION') wait_migration(vm_a)
iotests.log(event, filters=[iotests.filter_qmp_event]) # Wait for the destination second (which does not)
if event['data']['status'] == 'completed': wait_migration(vm_b)
break
iotests.log(vm_a.qmp('query-migrate')['return']['status']) iotests.log(vm_a.qmp('query-migrate')['return']['status'])
iotests.log(vm_b.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)) .add_incoming("exec: cat '%s'" % (fifo_b))
.launch()) .launch())
enable_migration_events(vm_a, 'A')
iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing', iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing',
overlay='drive0')) 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('Starting migration back to A...')
iotests.log(vm_b.qmp('migrate', uri='exec:cat >%s' % (fifo_b))) iotests.log(vm_b.qmp('migrate', uri='exec:cat >%s' % (fifo_b)))
with iotests.Timeout(3, 'Migration does not complete'): with iotests.Timeout(3, 'Migration does not complete'):
while True: # Wait for the source first (which includes setup=setup)
event = vm_b.event_wait('MIGRATION') wait_migration(vm_b)
iotests.log(event, filters=[iotests.filter_qmp_event]) # Wait for the destination second (which does not)
if event['data']['status'] == 'completed': wait_migration(vm_a)
break
iotests.log(vm_a.qmp('query-migrate')['return']['status']) iotests.log(vm_a.qmp('query-migrate')['return']['status'])
iotests.log(vm_b.qmp('query-migrate')['return']['status']) iotests.log(vm_b.qmp('query-migrate')['return']['status'])

View File

@ -1,14 +1,18 @@
Launching source VM... Launching source VM...
Launching destination VM...
{"return": {}}
{"return": {}}
Enabling migration QMP events on A... Enabling migration QMP events on A...
{"return": {}} {"return": {}}
Launching destination VM...
Enabling migration QMP events on B...
{"return": {}}
{"return": {}}
{"return": {}}
Starting migration to B... Starting migration to B...
{"return": {}} {"return": {}}
{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"status": "active"}, "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": "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
completed completed
{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} {"return": {"running": false, "singlestep": false, "status": "postmigrate"}}
@ -16,14 +20,16 @@ completed
Add a second parent to drive0-file... Add a second parent to drive0-file...
{"return": {}} {"return": {}}
Restart A with -incoming and second parent... Restart A with -incoming and second parent...
Enabling migration QMP events on A...
{"return": {}} {"return": {}}
Enabling migration QMP events on B...
{"return": {}} {"return": {}}
Starting migration back to A... Starting migration back to A...
{"return": {}} {"return": {}}
{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
{"data": {"status": "active"}, "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": "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
completed completed
{"return": {"running": true, "singlestep": false, "status": "running"}} {"return": {"running": true, "singlestep": false, "status": "running"}}

View File

@ -45,23 +45,23 @@ write -P0xcd 0x3ff0000 64k
"actions": [ "actions": [
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapB",
"name": "bitmapB" "node": "drive0"
}, },
"type": "block-dirty-bitmap-disable" "type": "block-dirty-bitmap-disable"
}, },
{ {
"data": { "data": {
"node": "drive0", "granularity": 65536,
"name": "bitmapC", "name": "bitmapC",
"granularity": 65536 "node": "drive0"
}, },
"type": "block-dirty-bitmap-add" "type": "block-dirty-bitmap-add"
}, },
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapA",
"name": "bitmapA" "node": "drive0"
}, },
"type": "block-dirty-bitmap-clear" "type": "block-dirty-bitmap-clear"
}, },
@ -105,30 +105,30 @@ write -P0xcd 0x3ff0000 64k
"actions": [ "actions": [
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapB",
"name": "bitmapB" "node": "drive0"
}, },
"type": "block-dirty-bitmap-disable" "type": "block-dirty-bitmap-disable"
}, },
{ {
"data": { "data": {
"node": "drive0", "granularity": 65536,
"name": "bitmapC", "name": "bitmapC",
"granularity": 65536 "node": "drive0"
}, },
"type": "block-dirty-bitmap-add" "type": "block-dirty-bitmap-add"
}, },
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapC",
"name": "bitmapC" "node": "drive0"
}, },
"type": "block-dirty-bitmap-disable" "type": "block-dirty-bitmap-disable"
}, },
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapC",
"name": "bitmapC" "node": "drive0"
}, },
"type": "block-dirty-bitmap-enable" "type": "block-dirty-bitmap-enable"
} }
@ -158,15 +158,15 @@ write -P0xea 0x3fe0000 64k
"actions": [ "actions": [
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapA",
"name": "bitmapA" "node": "drive0"
}, },
"type": "block-dirty-bitmap-disable" "type": "block-dirty-bitmap-disable"
}, },
{ {
"data": { "data": {
"node": "drive0", "name": "bitmapC",
"name": "bitmapC" "node": "drive0"
}, },
"type": "block-dirty-bitmap-disable" "type": "block-dirty-bitmap-disable"
} }
@ -209,21 +209,21 @@ write -P0xea 0x3fe0000 64k
"actions": [ "actions": [
{ {
"data": { "data": {
"node": "drive0",
"disabled": true, "disabled": true,
"granularity": 65536,
"name": "bitmapD", "name": "bitmapD",
"granularity": 65536 "node": "drive0"
}, },
"type": "block-dirty-bitmap-add" "type": "block-dirty-bitmap-add"
}, },
{ {
"data": { "data": {
"node": "drive0",
"target": "bitmapD",
"bitmaps": [ "bitmaps": [
"bitmapB", "bitmapB",
"bitmapC" "bitmapC"
] ],
"node": "drive0",
"target": "bitmapD"
}, },
"type": "block-dirty-bitmap-merge" "type": "block-dirty-bitmap-merge"
}, },
@ -273,21 +273,21 @@ write -P0xea 0x3fe0000 64k
"actions": [ "actions": [
{ {
"data": { "data": {
"node": "drive0",
"disabled": true, "disabled": true,
"granularity": 65536,
"name": "bitmapD", "name": "bitmapD",
"granularity": 65536 "node": "drive0"
}, },
"type": "block-dirty-bitmap-add" "type": "block-dirty-bitmap-add"
}, },
{ {
"data": { "data": {
"node": "drive0",
"target": "bitmapD",
"bitmaps": [ "bitmaps": [
"bitmapB", "bitmapB",
"bitmapC" "bitmapC"
] ],
"node": "drive0",
"target": "bitmapD"
}, },
"type": "block-dirty-bitmap-merge" "type": "block-dirty-bitmap-merge"
} }

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

@ -0,0 +1,237 @@
#!/usr/bin/env python
#
# Test vmdk and file image creation
#
# Copyright (C) 2018 Red Hat, Inc.
#
# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import math
import iotests
from iotests import imgfmt
iotests.verify_image_format(supported_fmts=['vmdk'])
def blockdev_create(vm, options):
result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
if 'return' in result:
assert result['return'] == {}
vm.run_job('job0')
iotests.log("")
with iotests.FilePath('t.vmdk') as disk_path, \
iotests.FilePath('t.vmdk.1') as extent1_path, \
iotests.FilePath('t.vmdk.2') as extent2_path, \
iotests.FilePath('t.vmdk.3') as extent3_path, \
iotests.VM() as vm:
#
# Successful image creation (defaults)
#
iotests.log("=== Successful image creation (defaults) ===")
iotests.log("")
size = 5 * 1024 * 1024 * 1024
vm.launch()
blockdev_create(vm, { 'driver': 'file',
'filename': disk_path,
'size': 0 })
vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
node_name='imgfile')
blockdev_create(vm, { 'driver': imgfmt,
'file': 'imgfile',
'size': size })
vm.shutdown()
iotests.img_info_log(disk_path)
#
# Successful image creation (inline blockdev-add, explicit defaults)
#
iotests.log("=== Successful image creation (inline blockdev-add, explicit defaults) ===")
iotests.log("")
# Choose a different size to show that we got a new image
size = 64 * 1024 * 1024
vm.launch()
blockdev_create(vm, { 'driver': 'file',
'filename': disk_path,
'size': 0 })
blockdev_create(vm, { 'driver': imgfmt,
'file': {
'driver': 'file',
'filename': disk_path,
},
'size': size,
'extents': [],
'subformat': 'monolithicSparse',
'adapter-type': 'ide',
'hwversion': '4',
'zeroed-grain': False })
vm.shutdown()
iotests.img_info_log(disk_path)
#
# Successful image creation (non-default options)
#
iotests.log("=== Successful image creation (with non-default options) ===")
iotests.log("")
# Choose a different size to show that we got a new image
size = 32 * 1024 * 1024
vm.launch()
blockdev_create(vm, { 'driver': 'file',
'filename': disk_path,
'size': 0 })
blockdev_create(vm, { 'driver': imgfmt,
'file': {
'driver': 'file',
'filename': disk_path,
},
'size': size,
'extents': [],
'subformat': 'monolithicSparse',
'adapter-type': 'buslogic',
'zeroed-grain': True })
vm.shutdown()
iotests.img_info_log(disk_path)
#
# Invalid BlockdevRef
#
iotests.log("=== Invalid BlockdevRef ===")
iotests.log("")
vm.launch()
blockdev_create(vm, { 'driver': imgfmt,
'file': "this doesn't exist",
'size': size })
vm.shutdown()
#
# Adapter types
#
iotests.log("=== Adapter types ===")
iotests.log("")
vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path))
# Valid
iotests.log("== Valid adapter types ==")
iotests.log("")
vm.launch()
for adapter_type in [ 'ide', 'buslogic', 'lsilogic', 'legacyESX' ]:
blockdev_create(vm, { 'driver': imgfmt,
'file': 'node0',
'size': size,
'adapter-type': adapter_type })
vm.shutdown()
# Invalid
iotests.log("== Invalid adapter types ==")
iotests.log("")
vm.launch()
for adapter_type in [ 'foo', 'IDE', 'legacyesx', 1 ]:
blockdev_create(vm, { 'driver': imgfmt,
'file': 'node0',
'size': size,
'adapter-type': adapter_type })
vm.shutdown()
#
# Other subformats
#
iotests.log("=== Other subformats ===")
iotests.log("")
for path in [ extent1_path, extent2_path, extent3_path ]:
msg = iotests.qemu_img_pipe('create', '-f', imgfmt, path, '0')
iotests.log(msg, [iotests.filter_testfiles])
vm.add_blockdev('driver=file,filename=%s,node-name=ext1' % (extent1_path))
vm.add_blockdev('driver=file,filename=%s,node-name=ext2' % (extent2_path))
vm.add_blockdev('driver=file,filename=%s,node-name=ext3' % (extent3_path))
# Missing extent
iotests.log("== Missing extent ==")
iotests.log("")
vm.launch()
blockdev_create(vm, { 'driver': imgfmt,
'file': 'node0',
'size': size,
'subformat': 'monolithicFlat' })
vm.shutdown()
# Correct extent
iotests.log("== Correct extent ==")
iotests.log("")
vm.launch()
blockdev_create(vm, { 'driver': imgfmt,
'file': 'node0',
'size': size,
'subformat': 'monolithicFlat',
'extents': ['ext1'] })
vm.shutdown()
# Extra extent
iotests.log("== Extra extent ==")
iotests.log("")
vm.launch()
blockdev_create(vm, { 'driver': imgfmt,
'file': 'node0',
'size': 512,
'subformat': 'monolithicFlat',
'extents': ['ext1', 'ext2', 'ext3'] })
vm.shutdown()
# Split formats
iotests.log("== Split formats ==")
iotests.log("")
for size in [ 512, 1073741824, 2147483648, 5368709120 ]:
for subfmt in [ 'twoGbMaxExtentFlat', 'twoGbMaxExtentSparse' ]:
iotests.log("= %s %d =" % (subfmt, size))
iotests.log("")
num_extents = math.ceil(size / 2.0**31)
extents = [ "ext%d" % (i) for i in range(1, num_extents + 1) ]
vm.launch()
blockdev_create(vm, { 'driver': imgfmt,
'file': 'node0',
'size': size,
'subformat': subfmt,
'extents': extents })
vm.shutdown()
iotests.img_info_log(disk_path)

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

@ -0,0 +1,348 @@
=== Successful image creation (defaults) ===
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node_name": "imgfile"}}
{"return": {}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 5.0G (5368709120 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: monolithicSparse
extents:
[0]:
virtual size: 5368709120
filename: TEST_IMG
cluster size: 65536
format:
=== Successful image creation (inline blockdev-add, explicit defaults) ===
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 64M (67108864 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: monolithicSparse
extents:
[0]:
virtual size: 67108864
filename: TEST_IMG
cluster size: 65536
format:
=== Successful image creation (with non-default options) ===
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 32M (33554432 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: monolithicSparse
extents:
[0]:
virtual size: 33554432
filename: TEST_IMG
cluster size: 65536
format:
=== Invalid BlockdevRef ===
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
=== Adapter types ===
== Valid adapter types ==
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
== Invalid adapter types ==
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}}
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}}
=== Other subformats ===
Formatting 'TEST_DIR/PID-t.vmdk.1', fmt=vmdk size=0 compat6=off hwversion=undefined
Formatting 'TEST_DIR/PID-t.vmdk.2', fmt=vmdk size=0 compat6=off hwversion=undefined
Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefined
== Missing extent ==
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
{"return": {}}
Job failed: Extent [0] not specified
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
== Correct extent ==
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
== Extra extent ==
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}}
{"return": {}}
Job failed: List of extents contains unused extents
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
== Split formats ==
= twoGbMaxExtentFlat 512 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 512 (512 bytes)
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentFlat
extents:
[0]:
virtual size: 512
filename: TEST_IMG.1
format: FLAT
= twoGbMaxExtentSparse 512 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 512 (512 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentSparse
extents:
[0]:
virtual size: 512
filename: TEST_IMG.1
cluster size: 65536
format: SPARSE
= twoGbMaxExtentFlat 1073741824 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 1.0G (1073741824 bytes)
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentFlat
extents:
[0]:
virtual size: 1073741824
filename: TEST_IMG.1
format: FLAT
= twoGbMaxExtentSparse 1073741824 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 1.0G (1073741824 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentSparse
extents:
[0]:
virtual size: 1073741824
filename: TEST_IMG.1
cluster size: 65536
format: SPARSE
= twoGbMaxExtentFlat 2147483648 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 2.0G (2147483648 bytes)
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentFlat
extents:
[0]:
virtual size: 2147483648
filename: TEST_IMG.1
format: FLAT
= twoGbMaxExtentSparse 2147483648 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 2.0G (2147483648 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentSparse
extents:
[0]:
virtual size: 2147483648
filename: TEST_IMG.1
cluster size: 65536
format: SPARSE
= twoGbMaxExtentFlat 5368709120 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 5.0G (5368709120 bytes)
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentFlat
extents:
[0]:
virtual size: 2147483648
filename: TEST_IMG.1
format: FLAT
[1]:
virtual size: 2147483648
filename: TEST_IMG.2
format: FLAT
[2]:
virtual size: 1073741824
filename: TEST_IMG.3
format: FLAT
= twoGbMaxExtentSparse 5368709120 =
{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
image: TEST_IMG
file format: IMGFMT
virtual size: 5.0G (5368709120 bytes)
cluster_size: 65536
Format specific information:
cid: XXXXXXXXXX
parent cid: XXXXXXXXXX
create type: twoGbMaxExtentSparse
extents:
[0]:
virtual size: 2147483648
filename: TEST_IMG.1
cluster size: 65536
format: SPARSE
[1]:
virtual size: 2147483648
filename: TEST_IMG.2
cluster size: 65536
format: SPARSE
[2]:
virtual size: 1073741824
filename: TEST_IMG.3
cluster size: 65536
format: SPARSE

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

@ -0,0 +1,53 @@
#!/bin/bash
#
# Test case for dmg
#
# Copyright (C) 2019 yuchenlin <npes87184@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=npes87184@gmail.com
seq=`basename $0`
echo "QA output created by $seq"
status=1 # failure is the default!
_cleanup()
{
rm -f "$TEST_IMG.raw"
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
_supported_fmt dmg
_supported_proto file
_supported_os Linux
echo
echo "== Testing conversion to raw should success =="
_use_sample_img simple-dmg.dmg.bz2
if ! $QEMU_IMG convert -f $IMGFMT -O raw "$TEST_IMG" "$TEST_IMG.raw" ; then
exit 1
fi
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View File

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

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

@ -0,0 +1,129 @@
#!/bin/bash
#
# Test hot plugging and unplugging with iothreads
#
# Copyright (C) 2019 Igalia, S.L.
# Author: Alberto Garcia <berto@igalia.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=berto@igalia.com
seq=`basename $0`
echo "QA output created by $seq"
status=1 # failure is the default!
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt generic
_supported_proto generic
_supported_os Linux
do_run_qemu()
{
echo Testing: "$@"
$QEMU -nographic -qmp stdio -serial none "$@"
echo
}
# Remove QMP events from (pretty-printed) output. Doesn't handle
# nested dicts correctly, but we don't get any of those in this test.
_filter_qmp_events()
{
tr '\n' '\t' | sed -e \
's/{\s*"timestamp":\s*{[^}]*},\s*"event":[^,}]*\(,\s*"data":\s*{[^}]*}\)\?\s*}\s*//g' \
| tr '\t' '\n'
}
run_qemu()
{
do_run_qemu "$@" 2>&1 | _filter_qmp | _filter_qmp_events
}
case "$QEMU_DEFAULT_MACHINE" in
s390-ccw-virtio)
virtio_scsi=virtio-scsi-ccw
;;
*)
virtio_scsi=virtio-scsi-pci
;;
esac
echo
echo === Unplug a SCSI disk and then plug it again ===
echo
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0"}}
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
{ "execute": "device_del", "arguments": {"id": "scsi0"}}
{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}}
{ "execute": "quit"}
EOF
echo
echo === Attach two SCSI disks using the same block device and the same iothread ===
echo
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0"}}
{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
{ "execute": "device_del", "arguments": {"id": "scsi-hd1"}}
{ "execute": "device_del", "arguments": {"id": "scsi0"}}
{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}}
{ "execute": "quit"}
EOF
echo
echo === Attach two SCSI disks using the same block device but different iothreads ===
echo
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "hd0", "read-only": true}}
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread0"}}
{ "execute": "object-add", "arguments": {"qom-type": "iothread", "id": "iothread1"}}
{ "execute": "device_add", "arguments": {"id": "scsi0", "driver": "${virtio_scsi}", "iothread": "iothread0"}}
{ "execute": "device_add", "arguments": {"id": "scsi1", "driver": "${virtio_scsi}", "iothread": "iothread1"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd0", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi0.0"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi1.0"}}
{ "execute": "device_del", "arguments": {"id": "scsi-hd0"}}
{ "execute": "device_add", "arguments": {"id": "scsi-hd1", "driver": "scsi-hd", "drive": "hd0", "bus": "scsi1.0"}}
{ "execute": "device_del", "arguments": {"id": "scsi-hd1"}}
{ "execute": "device_del", "arguments": {"id": "scsi0"}}
{ "execute": "device_del", "arguments": {"id": "scsi1"}}
{ "execute": "blockdev-del", "arguments": {"node-name": "hd0"}}
{ "execute": "quit"}
EOF
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View File

@ -0,0 +1,54 @@
QA output created by 240
=== Unplug a SCSI disk and then plug it again ===
Testing:
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
=== Attach two SCSI disks using the same block device and the same iothread ===
Testing:
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
=== Attach two SCSI disks using the same block device but different iothreads ===
Testing:
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"error": {"class": "GenericError", "desc": "Cannot attach a blockdev that is using a different iothread"}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
*** done

View File

@ -237,6 +237,7 @@ image format options
-vhdx test vhdx -vhdx test vhdx
-vmdk test vmdk -vmdk test vmdk
-luks test luks -luks test luks
-dmg test dmg
image protocol options image protocol options
-file test file (default) -file test file (default)
@ -304,6 +305,12 @@ testlist options
xpand=false xpand=false
;; ;;
-dmg)
IMGFMT=dmg
IMGFMT_GENERIC=false
xpand=false
;;
-qed) -qed)
IMGFMT=qed IMGFMT=qed
xpand=false xpand=false

View File

@ -165,6 +165,7 @@ _filter_img_info()
-e "/table_size: [0-9]\\+/d" \ -e "/table_size: [0-9]\\+/d" \
-e "/compat: '[^']*'/d" \ -e "/compat: '[^']*'/d" \
-e "/compat6: \\(on\\|off\\)/d" \ -e "/compat6: \\(on\\|off\\)/d" \
-e "s/cid: [0-9]\+/cid: XXXXXXXXXX/" \
-e "/static: \\(on\\|off\\)/d" \ -e "/static: \\(on\\|off\\)/d" \
-e "/zeroed_grain: \\(on\\|off\\)/d" \ -e "/zeroed_grain: \\(on\\|off\\)/d" \
-e "/subformat: '[^']*'/d" \ -e "/subformat: '[^']*'/d" \

View File

@ -234,4 +234,7 @@
234 auto quick migration 234 auto quick migration
235 auto quick 235 auto quick
236 auto quick 236 auto quick
237 rw auto quick
238 auto quick 238 auto quick
239 rw auto quick
240 auto quick

View File

@ -76,15 +76,16 @@ def qemu_img(*args):
sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
return exitcode return exitcode
def ordered_kwargs(kwargs): def ordered_qmp(qmsg):
# kwargs prior to 3.6 are not ordered, so: # Dictionaries are not ordered prior to 3.6, therefore:
od = OrderedDict() if isinstance(qmsg, list):
for k, v in sorted(kwargs.items()): return [ordered_qmp(atom) for atom in qmsg]
if isinstance(v, dict): if isinstance(qmsg, dict):
od[k] = ordered_kwargs(v) od = OrderedDict()
else: for k, v in sorted(qmsg.items()):
od[k] = v od[k] = ordered_qmp(v)
return od return od
return qmsg
def qemu_img_create(*args): def qemu_img_create(*args):
args = list(args) args = list(args)
@ -299,6 +300,7 @@ def filter_img_info(output, filename):
.replace(imgfmt, 'IMGFMT') .replace(imgfmt, 'IMGFMT')
line = re.sub('iters: [0-9]+', 'iters: XXX', line) 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('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line)
line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line)
lines.append(line) lines.append(line)
return '\n'.join(lines) return '\n'.join(lines)
@ -505,7 +507,7 @@ class VM(qtest.QEMUQtestMachine):
def qmp_log(self, cmd, filters=[], indent=None, **kwargs): def qmp_log(self, cmd, filters=[], indent=None, **kwargs):
full_cmd = OrderedDict(( full_cmd = OrderedDict((
("execute", cmd), ("execute", cmd),
("arguments", ordered_kwargs(kwargs)) ("arguments", ordered_qmp(kwargs))
)) ))
log(full_cmd, filters, indent=indent) log(full_cmd, filters, indent=indent)
result = self.qmp(cmd, **kwargs) result = self.qmp(cmd, **kwargs)

Binary file not shown.

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

@ -0,0 +1,372 @@
/*
* Block tests for iothreads
*
* Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "block/block.h"
#include "block/blockjob_int.h"
#include "sysemu/block-backend.h"
#include "qapi/error.h"
#include "iothread.h"
static int coroutine_fn bdrv_test_co_prwv(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return 0;
}
static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes)
{
return 0;
}
static int coroutine_fn
bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
return 0;
}
static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs,
bool want_zero,
int64_t offset, int64_t count,
int64_t *pnum, int64_t *map,
BlockDriverState **file)
{
*pnum = count;
return 0;
}
static BlockDriver bdrv_test = {
.format_name = "test",
.instance_size = 1,
.bdrv_co_preadv = bdrv_test_co_prwv,
.bdrv_co_pwritev = bdrv_test_co_prwv,
.bdrv_co_pdiscard = bdrv_test_co_pdiscard,
.bdrv_co_truncate = bdrv_test_co_truncate,
.bdrv_co_block_status = bdrv_test_co_block_status,
};
static void test_sync_op_pread(BdrvChild *c)
{
uint8_t buf[512];
int ret;
/* Success */
ret = bdrv_pread(c, 0, buf, sizeof(buf));
g_assert_cmpint(ret, ==, 512);
/* Early error: Negative offset */
ret = bdrv_pread(c, -2, buf, sizeof(buf));
g_assert_cmpint(ret, ==, -EIO);
}
static void test_sync_op_pwrite(BdrvChild *c)
{
uint8_t buf[512];
int ret;
/* Success */
ret = bdrv_pwrite(c, 0, buf, sizeof(buf));
g_assert_cmpint(ret, ==, 512);
/* Early error: Negative offset */
ret = bdrv_pwrite(c, -2, buf, sizeof(buf));
g_assert_cmpint(ret, ==, -EIO);
}
static void test_sync_op_blk_pread(BlockBackend *blk)
{
uint8_t buf[512];
int ret;
/* Success */
ret = blk_pread(blk, 0, buf, sizeof(buf));
g_assert_cmpint(ret, ==, 512);
/* Early error: Negative offset */
ret = blk_pread(blk, -2, buf, sizeof(buf));
g_assert_cmpint(ret, ==, -EIO);
}
static void test_sync_op_blk_pwrite(BlockBackend *blk)
{
uint8_t buf[512];
int ret;
/* Success */
ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0);
g_assert_cmpint(ret, ==, 512);
/* Early error: Negative offset */
ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0);
g_assert_cmpint(ret, ==, -EIO);
}
static void test_sync_op_load_vmstate(BdrvChild *c)
{
uint8_t buf[512];
int ret;
/* Error: Driver does not support snapshots */
ret = bdrv_load_vmstate(c->bs, buf, 0, sizeof(buf));
g_assert_cmpint(ret, ==, -ENOTSUP);
}
static void test_sync_op_save_vmstate(BdrvChild *c)
{
uint8_t buf[512];
int ret;
/* Error: Driver does not support snapshots */
ret = bdrv_save_vmstate(c->bs, buf, 0, sizeof(buf));
g_assert_cmpint(ret, ==, -ENOTSUP);
}
static void test_sync_op_pdiscard(BdrvChild *c)
{
int ret;
/* Normal success path */
c->bs->open_flags |= BDRV_O_UNMAP;
ret = bdrv_pdiscard(c, 0, 512);
g_assert_cmpint(ret, ==, 0);
/* Early success: UNMAP not supported */
c->bs->open_flags &= ~BDRV_O_UNMAP;
ret = bdrv_pdiscard(c, 0, 512);
g_assert_cmpint(ret, ==, 0);
/* Early error: Negative offset */
ret = bdrv_pdiscard(c, -2, 512);
g_assert_cmpint(ret, ==, -EIO);
}
static void test_sync_op_blk_pdiscard(BlockBackend *blk)
{
int ret;
/* Early success: UNMAP not supported */
ret = blk_pdiscard(blk, 0, 512);
g_assert_cmpint(ret, ==, 0);
/* Early error: Negative offset */
ret = blk_pdiscard(blk, -2, 512);
g_assert_cmpint(ret, ==, -EIO);
}
static void test_sync_op_truncate(BdrvChild *c)
{
int ret;
/* Normal success path */
ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL);
g_assert_cmpint(ret, ==, 0);
/* Early error: Negative offset */
ret = bdrv_truncate(c, -2, PREALLOC_MODE_OFF, NULL);
g_assert_cmpint(ret, ==, -EINVAL);
/* Error: Read-only image */
c->bs->read_only = true;
c->bs->open_flags &= ~BDRV_O_RDWR;
ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL);
g_assert_cmpint(ret, ==, -EACCES);
c->bs->read_only = false;
c->bs->open_flags |= BDRV_O_RDWR;
}
static void test_sync_op_block_status(BdrvChild *c)
{
int ret;
int64_t n;
/* Normal success path */
ret = bdrv_is_allocated(c->bs, 0, 65536, &n);
g_assert_cmpint(ret, ==, 0);
/* Early success: No driver support */
bdrv_test.bdrv_co_block_status = NULL;
ret = bdrv_is_allocated(c->bs, 0, 65536, &n);
g_assert_cmpint(ret, ==, 1);
/* Early success: bytes = 0 */
ret = bdrv_is_allocated(c->bs, 0, 0, &n);
g_assert_cmpint(ret, ==, 0);
/* Early success: Offset > image size*/
ret = bdrv_is_allocated(c->bs, 0x1000000, 0x1000000, &n);
g_assert_cmpint(ret, ==, 0);
}
static void test_sync_op_flush(BdrvChild *c)
{
int ret;
/* Normal success path */
ret = bdrv_flush(c->bs);
g_assert_cmpint(ret, ==, 0);
/* Early success: Read-only image */
c->bs->read_only = true;
c->bs->open_flags &= ~BDRV_O_RDWR;
ret = bdrv_flush(c->bs);
g_assert_cmpint(ret, ==, 0);
c->bs->read_only = false;
c->bs->open_flags |= BDRV_O_RDWR;
}
static void test_sync_op_blk_flush(BlockBackend *blk)
{
BlockDriverState *bs = blk_bs(blk);
int ret;
/* Normal success path */
ret = blk_flush(blk);
g_assert_cmpint(ret, ==, 0);
/* Early success: Read-only image */
bs->read_only = true;
bs->open_flags &= ~BDRV_O_RDWR;
ret = blk_flush(blk);
g_assert_cmpint(ret, ==, 0);
bs->read_only = false;
bs->open_flags |= BDRV_O_RDWR;
}
static void test_sync_op_check(BdrvChild *c)
{
BdrvCheckResult result;
int ret;
/* Error: Driver does not implement check */
ret = bdrv_check(c->bs, &result, 0);
g_assert_cmpint(ret, ==, -ENOTSUP);
}
static void test_sync_op_invalidate_cache(BdrvChild *c)
{
/* Early success: Image is not inactive */
bdrv_invalidate_cache(c->bs, NULL);
}
typedef struct SyncOpTest {
const char *name;
void (*fn)(BdrvChild *c);
void (*blkfn)(BlockBackend *blk);
} SyncOpTest;
const SyncOpTest sync_op_tests[] = {
{
.name = "/sync-op/pread",
.fn = test_sync_op_pread,
.blkfn = test_sync_op_blk_pread,
}, {
.name = "/sync-op/pwrite",
.fn = test_sync_op_pwrite,
.blkfn = test_sync_op_blk_pwrite,
}, {
.name = "/sync-op/load_vmstate",
.fn = test_sync_op_load_vmstate,
}, {
.name = "/sync-op/save_vmstate",
.fn = test_sync_op_save_vmstate,
}, {
.name = "/sync-op/pdiscard",
.fn = test_sync_op_pdiscard,
.blkfn = test_sync_op_blk_pdiscard,
}, {
.name = "/sync-op/truncate",
.fn = test_sync_op_truncate,
}, {
.name = "/sync-op/block_status",
.fn = test_sync_op_block_status,
}, {
.name = "/sync-op/flush",
.fn = test_sync_op_flush,
.blkfn = test_sync_op_blk_flush,
}, {
.name = "/sync-op/check",
.fn = test_sync_op_check,
}, {
.name = "/sync-op/invalidate_cache",
.fn = test_sync_op_invalidate_cache,
},
};
/* Test synchronous operations that run in a different iothread, so we have to
* poll for the coroutine there to return. */
static void test_sync_op(const void *opaque)
{
const SyncOpTest *t = opaque;
IOThread *iothread = iothread_new();
AioContext *ctx = iothread_get_aio_context(iothread);
BlockBackend *blk;
BlockDriverState *bs;
BdrvChild *c;
blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort);
bs->total_sectors = 65536 / BDRV_SECTOR_SIZE;
blk_insert_bs(blk, bs, &error_abort);
c = QLIST_FIRST(&bs->parents);
blk_set_aio_context(blk, ctx);
aio_context_acquire(ctx);
t->fn(c);
if (t->blkfn) {
t->blkfn(blk);
}
aio_context_release(ctx);
blk_set_aio_context(blk, qemu_get_aio_context());
bdrv_unref(bs);
blk_unref(blk);
}
int main(int argc, char **argv)
{
int i;
bdrv_init();
qemu_init_main_loop(&error_abort);
g_test_init(&argc, &argv, NULL);
for (i = 0; i < ARRAY_SIZE(sync_op_tests); i++) {
const SyncOpTest *t = &sync_op_tests[i];
g_test_add_data_func(t->name, t, test_sync_op);
}
return g_test_run();
}

View File

@ -88,7 +88,7 @@ static void read_guid_from_memory(QTestState *qts, QemuUUID *guid)
/* The GUID is in little-endian format in the guest, while QEMU /* The GUID is in little-endian format in the guest, while QEMU
* uses big-endian. Swap after reading. * 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) static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid)

View File

@ -110,10 +110,10 @@ int qemu_uuid_parse(const char *str, QemuUUID *uuid)
/* Swap from UUID format endian (BE) to the opposite or vice versa. /* 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);
bswap32s(&uuid->fields.time_low); bswap16s(&uuid.fields.time_mid);
bswap16s(&uuid->fields.time_mid); bswap16s(&uuid.fields.time_high_and_version);
bswap16s(&uuid->fields.time_high_and_version); return uuid;
} }