mirror of https://github.com/xemu-project/xemu.git
Block layer patches
- qemu-storage-daemon: Add vhost-user-blk help - block-backend: Fix use-after-free for BDS pointers after aio_poll() - qemu-img: Fix sparseness of output image with unaligned ranges - vvfat: Fix crashes in read-write mode - Fix device deletion events with -device JSON syntax - Code cleanups -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmHhf5gRHGt3b2xmQHJl ZGhhdC5jb20ACgkQfwmycsiPL9YBMA//ZkaIigVsfjRoeUh2MccgOuvYpXZtq4po q7l6AwGLbBpTt5Fy468gYhwmXuwHCapTMRmvWf6mpb86jtJ6vdbE16L0Z4/Z9iiW C0w69fsAAP9XyI+f7Q5FNtzz3jWztKowgyhkU33izbwYM7dm5Xw1q5bDkOiIBNoO d8cdxLC1oQGEWJmGLgmbaM/ow0iDogFpT8zU5j0VE3uK01si8pblWlXm1SM3nOK9 b4uROqKYsTzTny/zX7KxD4SX3UGKYK393rQxr5HdmTiW14uGfB+EVfBxJmn07Qch lWM/v9tYoP1aVbR6IL5osAQdmbDYX0zsRMq5UA+dQ6OqnE3GpluVrYIFoaUSoShf S704hYdWgO0sKfpAYgJgGo6y0mglnp9Z7xO4Ng3XUNj0gvfgnOe3CdCdXIOeTFwC eP+KlFvbUT2xpTqI6ttBgKCcwKHA3hgWCnlo39C80bL1ZVKWSqh6zORfwmptouQ3 BmuhEqZRyoYrknrTELN+lIKK2gP6MLup/ymeXWOOOE58KSpmrdeBAXmgJNXX3ucx lAWGsIz0CxdaKQoZpKpikho4rhrGkqZ33B3H7mdcsKS6zYzmsDIqa9FzUjtpvN2V K/jXlK7dv58Y+LLzpcuJAf8HNnitA107WD5RA1s5nTw0ahD2GwR4UPzEhnSO9/nT yZ3dGUysj7Q= =dnBv -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches - qemu-storage-daemon: Add vhost-user-blk help - block-backend: Fix use-after-free for BDS pointers after aio_poll() - qemu-img: Fix sparseness of output image with unaligned ranges - vvfat: Fix crashes in read-write mode - Fix device deletion events with -device JSON syntax - Code cleanups # gpg: Signature made Fri 14 Jan 2022 13:50:16 GMT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # 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: iotests/testrunner.py: refactor test_field_width block: drop BLK_PERM_GRAPH_MOD qemu-img: make is_allocated_sectors() more efficient iotests: Test qemu-img convert of zeroed data cluster vvfat: Fix vvfat_write() for writes before the root directory vvfat: Fix size of temporary qcow file iotests/308: Fix for CAP_DAC_OVERRIDE iotests/stream-error-on-reset: New test block-backend: prevent dangling BDS pointers across aio_poll() qapi/block: Restrict vhost-user-blk to CONFIG_VHOST_USER_BLK_SERVER qemu-storage-daemon: Add vhost-user-blk help docs: Correct 'vhost-user-blk' spelling softmmu: fix device deletion events with -device JSON syntax include/sysemu/blockdev.h: remove drive_get_max_devs include/sysemu/blockdev.h: remove drive_mark_claimed_by_board and inline drive_def block_int: make bdrv_backing_overridden static Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1cd2ad11d3
11
block.c
11
block.c
|
@ -103,6 +103,8 @@ static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
|
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
|
||||||
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
|
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
|
||||||
|
|
||||||
|
static bool bdrv_backing_overridden(BlockDriverState *bs);
|
||||||
|
|
||||||
/* If non-zero, use only whitelisted block drivers */
|
/* If non-zero, use only whitelisted block drivers */
|
||||||
static int use_bdrv_whitelist;
|
static int use_bdrv_whitelist;
|
||||||
|
|
||||||
|
@ -2483,7 +2485,6 @@ char *bdrv_perm_names(uint64_t perm)
|
||||||
{ BLK_PERM_WRITE, "write" },
|
{ BLK_PERM_WRITE, "write" },
|
||||||
{ BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
|
{ BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
|
||||||
{ BLK_PERM_RESIZE, "resize" },
|
{ BLK_PERM_RESIZE, "resize" },
|
||||||
{ BLK_PERM_GRAPH_MOD, "change children" },
|
|
||||||
{ 0, NULL }
|
{ 0, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2599,8 +2600,7 @@ static void bdrv_default_perms_for_cow(BlockDriverState *bs, BdrvChild *c,
|
||||||
shared = 0;
|
shared = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
|
shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
|
||||||
BLK_PERM_WRITE_UNCHANGED;
|
|
||||||
|
|
||||||
if (bs->open_flags & BDRV_O_INACTIVE) {
|
if (bs->open_flags & BDRV_O_INACTIVE) {
|
||||||
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
|
||||||
|
@ -2718,7 +2718,6 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
|
||||||
[BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE,
|
[BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE,
|
||||||
[BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED,
|
[BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED,
|
||||||
[BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE,
|
[BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE,
|
||||||
[BLOCK_PERMISSION_GRAPH_MOD] = BLK_PERM_GRAPH_MOD,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX);
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX);
|
||||||
|
@ -5544,8 +5543,6 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||||
update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
|
update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
|
||||||
|
|
||||||
/* success - we can delete the intermediate states, and link top->base */
|
/* success - we can delete the intermediate states, and link top->base */
|
||||||
/* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
|
|
||||||
* we've figured out how they should work. */
|
|
||||||
if (!backing_file_str) {
|
if (!backing_file_str) {
|
||||||
bdrv_refresh_filename(base);
|
bdrv_refresh_filename(base);
|
||||||
backing_file_str = base->filename;
|
backing_file_str = base->filename;
|
||||||
|
@ -7475,7 +7472,7 @@ static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs)
|
||||||
/* Note: This function may return false positives; it may return true
|
/* Note: This function may return false positives; it may return true
|
||||||
* even if opening the backing file specified by bs's image header
|
* even if opening the backing file specified by bs's image header
|
||||||
* would result in exactly bs->backing. */
|
* would result in exactly bs->backing. */
|
||||||
bool bdrv_backing_overridden(BlockDriverState *bs)
|
static bool bdrv_backing_overridden(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
if (bs->backing) {
|
if (bs->backing) {
|
||||||
return strcmp(bs->auto_backing_file,
|
return strcmp(bs->auto_backing_file,
|
||||||
|
|
|
@ -822,16 +822,22 @@ BlockBackend *blk_by_public(BlockBackendPublic *public)
|
||||||
void blk_remove_bs(BlockBackend *blk)
|
void blk_remove_bs(BlockBackend *blk)
|
||||||
{
|
{
|
||||||
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
||||||
BlockDriverState *bs;
|
|
||||||
BdrvChild *root;
|
BdrvChild *root;
|
||||||
|
|
||||||
notifier_list_notify(&blk->remove_bs_notifiers, blk);
|
notifier_list_notify(&blk->remove_bs_notifiers, blk);
|
||||||
if (tgm->throttle_state) {
|
if (tgm->throttle_state) {
|
||||||
bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a ref in case blk_bs() changes across bdrv_drained_begin(), for
|
||||||
|
* example, if a temporary filter node is removed by a blockjob.
|
||||||
|
*/
|
||||||
|
bdrv_ref(bs);
|
||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
throttle_group_detach_aio_context(tgm);
|
throttle_group_detach_aio_context(tgm);
|
||||||
throttle_group_attach_aio_context(tgm, qemu_get_aio_context());
|
throttle_group_attach_aio_context(tgm, qemu_get_aio_context());
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
blk_update_root_state(blk);
|
blk_update_root_state(blk);
|
||||||
|
@ -1705,6 +1711,7 @@ void blk_drain(BlockBackend *blk)
|
||||||
BlockDriverState *bs = blk_bs(blk);
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
|
bdrv_ref(bs);
|
||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1714,6 +1721,7 @@ void blk_drain(BlockBackend *blk)
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2044,10 +2052,13 @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
|
bdrv_ref(bs);
|
||||||
|
|
||||||
if (update_root_node) {
|
if (update_root_node) {
|
||||||
ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root,
|
ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root,
|
||||||
errp);
|
errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
bdrv_unref(bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2057,6 +2068,8 @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
|
||||||
throttle_group_attach_aio_context(tgm, new_context);
|
throttle_group_attach_aio_context(tgm, new_context);
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
blk->ctx = new_context;
|
blk->ctx = new_context;
|
||||||
|
@ -2326,11 +2339,13 @@ void blk_io_limits_disable(BlockBackend *blk)
|
||||||
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
|
||||||
assert(tgm->throttle_state);
|
assert(tgm->throttle_state);
|
||||||
if (bs) {
|
if (bs) {
|
||||||
|
bdrv_ref(bs);
|
||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
}
|
}
|
||||||
throttle_group_unregister_tgm(tgm);
|
throttle_group_unregister_tgm(tgm);
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -370,7 +370,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||||
s->base = blk_new(s->common.job.aio_context,
|
s->base = blk_new(s->common.job.aio_context,
|
||||||
base_perms,
|
base_perms,
|
||||||
BLK_PERM_CONSISTENT_READ
|
BLK_PERM_CONSISTENT_READ
|
||||||
| BLK_PERM_GRAPH_MOD
|
|
||||||
| BLK_PERM_WRITE_UNCHANGED);
|
| BLK_PERM_WRITE_UNCHANGED);
|
||||||
ret = blk_insert_bs(s->base, base, errp);
|
ret = blk_insert_bs(s->base, base, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
|
@ -1139,10 +1139,7 @@ static void mirror_complete(Job *job, Error **errp)
|
||||||
replace_aio_context = bdrv_get_aio_context(s->to_replace);
|
replace_aio_context = bdrv_get_aio_context(s->to_replace);
|
||||||
aio_context_acquire(replace_aio_context);
|
aio_context_acquire(replace_aio_context);
|
||||||
|
|
||||||
/* TODO Translate this into permission system. Current definition of
|
/* TODO Translate this into child freeze system. */
|
||||||
* GRAPH_MOD would require to request it for the parents; they might
|
|
||||||
* not even be BlockDriverStates, however, so a BdrvChild can't address
|
|
||||||
* them. May need redefinition of GRAPH_MOD. */
|
|
||||||
error_setg(&s->replace_blocker,
|
error_setg(&s->replace_blocker,
|
||||||
"block device is in use by block-job-complete");
|
"block device is in use by block-job-complete");
|
||||||
bdrv_op_block_all(s->to_replace, s->replace_blocker);
|
bdrv_op_block_all(s->to_replace, s->replace_blocker);
|
||||||
|
@ -1666,7 +1663,7 @@ static BlockJob *mirror_start_job(
|
||||||
s = block_job_create(job_id, driver, NULL, mirror_top_bs,
|
s = block_job_create(job_id, driver, NULL, mirror_top_bs,
|
||||||
BLK_PERM_CONSISTENT_READ,
|
BLK_PERM_CONSISTENT_READ,
|
||||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||||
BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
|
BLK_PERM_WRITE, speed,
|
||||||
creation_flags, cb, opaque, errp);
|
creation_flags, cb, opaque, errp);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1710,9 +1707,7 @@ static BlockJob *mirror_start_job(
|
||||||
target_perms |= BLK_PERM_RESIZE;
|
target_perms |= BLK_PERM_RESIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_shared_perms |= BLK_PERM_CONSISTENT_READ
|
target_shared_perms |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
||||||
| BLK_PERM_WRITE
|
|
||||||
| BLK_PERM_GRAPH_MOD;
|
|
||||||
} else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) {
|
} else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) {
|
||||||
/*
|
/*
|
||||||
* We may want to allow this in the future, but it would
|
* We may want to allow this in the future, but it would
|
||||||
|
@ -1723,10 +1718,6 @@ static BlockJob *mirror_start_job(
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backing_mode != MIRROR_LEAVE_BACKING_CHAIN) {
|
|
||||||
target_perms |= BLK_PERM_GRAPH_MOD;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->target = blk_new(s->common.job.aio_context,
|
s->target = blk_new(s->common.job.aio_context,
|
||||||
target_perms, target_shared_perms);
|
target_perms, target_shared_perms);
|
||||||
ret = blk_insert_bs(s->target, target, errp);
|
ret = blk_insert_bs(s->target, target, errp);
|
||||||
|
|
|
@ -101,7 +101,7 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = drive_def(optstr);
|
opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
|
||||||
if (!opts)
|
if (!opts)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -882,7 +882,7 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
|
static inline int32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
|
||||||
{
|
{
|
||||||
return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
|
return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
|
||||||
}
|
}
|
||||||
|
@ -1230,6 +1230,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
dirname, cyls, heads, secs));
|
dirname, cyls, heads, secs));
|
||||||
|
|
||||||
s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
|
s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
|
||||||
|
bs->total_sectors = cyls * heads * secs;
|
||||||
|
|
||||||
if (qemu_opt_get_bool(opts, "rw", false)) {
|
if (qemu_opt_get_bool(opts, "rw", false)) {
|
||||||
if (!bdrv_is_read_only(bs)) {
|
if (!bdrv_is_read_only(bs)) {
|
||||||
|
@ -1250,8 +1251,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->total_sectors = cyls * heads * secs;
|
|
||||||
|
|
||||||
if (init_directories(s, dirname, heads, secs, errp)) {
|
if (init_directories(s, dirname, heads, secs, errp)) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -2982,6 +2981,7 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
|
||||||
{
|
{
|
||||||
BDRVVVFATState *s = bs->opaque;
|
BDRVVVFATState *s = bs->opaque;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
int first_cluster, last_cluster;
|
||||||
|
|
||||||
DLOG(checkpoint());
|
DLOG(checkpoint());
|
||||||
|
|
||||||
|
@ -3000,9 +3000,20 @@ DLOG(checkpoint());
|
||||||
if (sector_num < s->offset_to_fat)
|
if (sector_num < s->offset_to_fat)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (i = sector2cluster(s, sector_num);
|
/*
|
||||||
i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
|
* Values will be negative for writes to the FAT, which is located before
|
||||||
mapping_t* mapping = find_mapping_for_cluster(s, i);
|
* the root directory.
|
||||||
|
*/
|
||||||
|
first_cluster = sector2cluster(s, sector_num);
|
||||||
|
last_cluster = sector2cluster(s, sector_num + nb_sectors - 1);
|
||||||
|
|
||||||
|
for (i = first_cluster; i <= last_cluster;) {
|
||||||
|
mapping_t *mapping = NULL;
|
||||||
|
|
||||||
|
if (i >= 0) {
|
||||||
|
mapping = find_mapping_for_cluster(s, i);
|
||||||
|
}
|
||||||
|
|
||||||
if (mapping) {
|
if (mapping) {
|
||||||
if (mapping->read_only) {
|
if (mapping->read_only) {
|
||||||
fprintf(stderr, "Tried to write to write-protected file %s\n",
|
fprintf(stderr, "Tried to write to write-protected file %s\n",
|
||||||
|
@ -3042,8 +3053,9 @@ DLOG(checkpoint());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = mapping->end;
|
i = mapping->end;
|
||||||
} else
|
} else {
|
||||||
i++;
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3057,10 +3069,11 @@ DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sec
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = sector2cluster(s, sector_num);
|
for (i = first_cluster; i <= last_cluster; i++) {
|
||||||
i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
|
if (i >= 0) {
|
||||||
if (i >= 0)
|
|
||||||
s->used_clusters[i] |= USED_ALLOCATED;
|
s->used_clusters[i] |= USED_ALLOCATED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DLOG(checkpoint());
|
DLOG(checkpoint());
|
||||||
/* TODO: add timeout */
|
/* TODO: add timeout */
|
||||||
|
@ -3147,8 +3160,8 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
|
opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
|
||||||
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512,
|
qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
|
||||||
&error_abort);
|
bs->total_sectors * BDRV_SECTOR_SIZE, &error_abort);
|
||||||
qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
|
qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
|
||||||
|
|
||||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
|
ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
|
||||||
|
|
24
blockdev.c
24
blockdev.c
|
@ -168,23 +168,6 @@ void blockdev_auto_del(BlockBackend *blk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current mapping of how many units per bus
|
|
||||||
* a particular interface can support.
|
|
||||||
*
|
|
||||||
* A positive integer indicates n units per bus.
|
|
||||||
* 0 implies the mapping has not been established.
|
|
||||||
* -1 indicates an invalid BlockInterfaceType was given.
|
|
||||||
*/
|
|
||||||
int drive_get_max_devs(BlockInterfaceType type)
|
|
||||||
{
|
|
||||||
if (type >= IF_IDE && type < IF_COUNT) {
|
|
||||||
return if_max_devs[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
|
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
|
||||||
{
|
{
|
||||||
int max_devs = if_max_devs[type];
|
int max_devs = if_max_devs[type];
|
||||||
|
@ -197,17 +180,12 @@ static int drive_index_to_unit_id(BlockInterfaceType type, int index)
|
||||||
return max_devs ? index % max_devs : index;
|
return max_devs ? index % max_devs : index;
|
||||||
}
|
}
|
||||||
|
|
||||||
QemuOpts *drive_def(const char *optstr)
|
|
||||||
{
|
|
||||||
return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
||||||
const char *optstr)
|
const char *optstr)
|
||||||
{
|
{
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
|
|
||||||
opts = drive_def(optstr);
|
opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
|
||||||
if (!opts) {
|
if (!opts) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ Export raw image file ``disk.img`` over NBD UNIX domain socket ``nbd.sock``::
|
||||||
--nbd-server addr.type=unix,addr.path=nbd.sock \
|
--nbd-server addr.type=unix,addr.path=nbd.sock \
|
||||||
--export type=nbd,id=export,node-name=disk,writable=on
|
--export type=nbd,id=export,node-name=disk,writable=on
|
||||||
|
|
||||||
Export a qcow2 image file ``disk.qcow2`` as a vhosts-user-blk device over UNIX
|
Export a qcow2 image file ``disk.qcow2`` as a vhost-user-blk device over UNIX
|
||||||
domain socket ``vhost-user-blk.sock``::
|
domain socket ``vhost-user-blk.sock``::
|
||||||
|
|
||||||
$ qemu-storage-daemon \
|
$ qemu-storage-daemon \
|
||||||
|
|
|
@ -171,8 +171,7 @@ bool blkconf_apply_backend_options(BlockConf *conf, bool readonly,
|
||||||
perm |= BLK_PERM_WRITE;
|
perm |= BLK_PERM_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
|
||||||
BLK_PERM_GRAPH_MOD;
|
|
||||||
if (resizable) {
|
if (resizable) {
|
||||||
shared_perm |= BLK_PERM_RESIZE;
|
shared_perm |= BLK_PERM_RESIZE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,12 +269,13 @@ enum {
|
||||||
BLK_PERM_RESIZE = 0x08,
|
BLK_PERM_RESIZE = 0x08,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This permission is required to change the node that this BdrvChild
|
* There was a now-removed bit BLK_PERM_GRAPH_MOD, with value of 0x10. QEMU
|
||||||
* points to.
|
* 6.1 and earlier may still lock the corresponding byte in block/file-posix
|
||||||
|
* locking. So, implementing some new permission should be very careful to
|
||||||
|
* not interfere with this old unused thing.
|
||||||
*/
|
*/
|
||||||
BLK_PERM_GRAPH_MOD = 0x10,
|
|
||||||
|
|
||||||
BLK_PERM_ALL = 0x1f,
|
BLK_PERM_ALL = 0x0f,
|
||||||
|
|
||||||
DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ
|
DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ
|
||||||
| BLK_PERM_WRITE
|
| BLK_PERM_WRITE
|
||||||
|
|
|
@ -1122,9 +1122,6 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
|
||||||
void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
|
void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
|
||||||
QDict *options);
|
QDict *options);
|
||||||
|
|
||||||
bool bdrv_backing_overridden(BlockDriverState *bs);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bdrv_add_aio_context_notifier:
|
* bdrv_add_aio_context_notifier:
|
||||||
*
|
*
|
||||||
|
|
|
@ -45,13 +45,10 @@ BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo);
|
||||||
void override_max_devs(BlockInterfaceType type, int max_devs);
|
void override_max_devs(BlockInterfaceType type, int max_devs);
|
||||||
|
|
||||||
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
|
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
|
||||||
void drive_mark_claimed_by_board(void);
|
|
||||||
void drive_check_orphaned(void);
|
void drive_check_orphaned(void);
|
||||||
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
|
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
|
||||||
int drive_get_max_bus(BlockInterfaceType type);
|
int drive_get_max_bus(BlockInterfaceType type);
|
||||||
int drive_get_max_devs(BlockInterfaceType type);
|
|
||||||
|
|
||||||
QemuOpts *drive_def(const char *optstr);
|
|
||||||
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
||||||
const char *optstr);
|
const char *optstr);
|
||||||
DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type,
|
DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type,
|
||||||
|
|
|
@ -1878,14 +1878,11 @@
|
||||||
#
|
#
|
||||||
# @resize: This permission is required to change the size of a block node.
|
# @resize: This permission is required to change the size of a block node.
|
||||||
#
|
#
|
||||||
# @graph-mod: This permission is required to change the node that this
|
|
||||||
# BdrvChild points to.
|
|
||||||
#
|
|
||||||
# Since: 4.0
|
# Since: 4.0
|
||||||
##
|
##
|
||||||
{ 'enum': 'BlockPermission',
|
{ 'enum': 'BlockPermission',
|
||||||
'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize',
|
'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize' ] }
|
||||||
'graph-mod' ] }
|
|
||||||
##
|
##
|
||||||
# @XDbgBlockGraphEdge:
|
# @XDbgBlockGraphEdge:
|
||||||
#
|
#
|
||||||
|
|
|
@ -277,7 +277,8 @@
|
||||||
# Since: 4.2
|
# Since: 4.2
|
||||||
##
|
##
|
||||||
{ 'enum': 'BlockExportType',
|
{ 'enum': 'BlockExportType',
|
||||||
'data': [ 'nbd', 'vhost-user-blk',
|
'data': [ 'nbd',
|
||||||
|
{ 'name': 'vhost-user-blk', 'if': 'CONFIG_VHOST_USER_BLK_SERVER' },
|
||||||
{ 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] }
|
{ 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -319,7 +320,8 @@
|
||||||
'discriminator': 'type',
|
'discriminator': 'type',
|
||||||
'data': {
|
'data': {
|
||||||
'nbd': 'BlockExportOptionsNbd',
|
'nbd': 'BlockExportOptionsNbd',
|
||||||
'vhost-user-blk': 'BlockExportOptionsVhostUserBlk',
|
'vhost-user-blk': { 'type': 'BlockExportOptionsVhostUserBlk',
|
||||||
|
'if': 'CONFIG_VHOST_USER_BLK_SERVER' },
|
||||||
'fuse': { 'type': 'BlockExportOptionsFuse',
|
'fuse': { 'type': 'BlockExportOptionsFuse',
|
||||||
'if': 'CONFIG_FUSE' }
|
'if': 'CONFIG_FUSE' }
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
# @json-cli: If present, the "-device" command line option supports JSON
|
# @json-cli: If present, the "-device" command line option supports JSON
|
||||||
# syntax with a structure identical to the arguments of this
|
# syntax with a structure identical to the arguments of this
|
||||||
# command.
|
# command.
|
||||||
|
# @json-cli-hotplug: If present, the "-device" command line option supports JSON
|
||||||
|
# syntax without the reference counting leak that broke
|
||||||
|
# hot-unplug
|
||||||
#
|
#
|
||||||
# Notes:
|
# Notes:
|
||||||
#
|
#
|
||||||
|
@ -74,7 +77,7 @@
|
||||||
{ 'command': 'device_add',
|
{ 'command': 'device_add',
|
||||||
'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
|
'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
|
||||||
'gen': false, # so we can get the additional arguments
|
'gen': false, # so we can get the additional arguments
|
||||||
'features': ['json-cli'] }
|
'features': ['json-cli', 'json-cli-hotplug'] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @device_del:
|
# @device_del:
|
||||||
|
|
23
qemu-img.c
23
qemu-img.c
|
@ -1171,19 +1171,34 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i == n) {
|
||||||
|
/*
|
||||||
|
* The whole buf is the same.
|
||||||
|
* No reason to split it into chunks, so return now.
|
||||||
|
*/
|
||||||
|
*pnum = i;
|
||||||
|
return !is_zero;
|
||||||
|
}
|
||||||
|
|
||||||
tail = (sector_num + i) & (alignment - 1);
|
tail = (sector_num + i) & (alignment - 1);
|
||||||
if (tail) {
|
if (tail) {
|
||||||
if (is_zero && i <= tail) {
|
if (is_zero && i <= tail) {
|
||||||
/* treat unallocated areas which only consist
|
/*
|
||||||
* of a small tail as allocated. */
|
* For sure next sector after i is data, and it will rewrite this
|
||||||
|
* tail anyway due to RMW. So, let's just write data now.
|
||||||
|
*/
|
||||||
is_zero = false;
|
is_zero = false;
|
||||||
}
|
}
|
||||||
if (!is_zero) {
|
if (!is_zero) {
|
||||||
/* align up end offset of allocated areas. */
|
/* If possible, align up end offset of allocated areas. */
|
||||||
i += alignment - tail;
|
i += alignment - tail;
|
||||||
i = MIN(i, n);
|
i = MIN(i, n);
|
||||||
} else {
|
} else {
|
||||||
/* align down end offset of zero areas. */
|
/*
|
||||||
|
* For sure next sector after i is data, and it will rewrite this
|
||||||
|
* tail anyway due to RMW. Better is avoid RMW and write zeroes up
|
||||||
|
* to aligned bound.
|
||||||
|
*/
|
||||||
i -= tail;
|
i -= tail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ def perm(arr):
|
||||||
s = 'w' if 'write' in arr else '_'
|
s = 'w' if 'write' in arr else '_'
|
||||||
s += 'r' if 'consistent-read' in arr else '_'
|
s += 'r' if 'consistent-read' in arr else '_'
|
||||||
s += 'u' if 'write-unchanged' in arr else '_'
|
s += 'u' if 'write-unchanged' in arr else '_'
|
||||||
s += 'g' if 'graph-mod' in arr else '_'
|
|
||||||
s += 's' if 'resize' in arr else '_'
|
s += 's' if 'resize' in arr else '_'
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
|
@ -2684,6 +2684,7 @@ static void qemu_create_cli_devices(void)
|
||||||
qemu_opts_foreach(qemu_find_opts("device"),
|
qemu_opts_foreach(qemu_find_opts("device"),
|
||||||
device_init_func, NULL, &error_fatal);
|
device_init_func, NULL, &error_fatal);
|
||||||
QTAILQ_FOREACH(opt, &device_opts, next) {
|
QTAILQ_FOREACH(opt, &device_opts, next) {
|
||||||
|
DeviceState *dev;
|
||||||
loc_push_restore(&opt->loc);
|
loc_push_restore(&opt->loc);
|
||||||
/*
|
/*
|
||||||
* TODO Eventually we should call qmp_device_add() here to make sure it
|
* TODO Eventually we should call qmp_device_add() here to make sure it
|
||||||
|
@ -2692,7 +2693,8 @@ static void qemu_create_cli_devices(void)
|
||||||
* from the start, so call qdev_device_add_from_qdict() directly for
|
* from the start, so call qdev_device_add_from_qdict() directly for
|
||||||
* now.
|
* now.
|
||||||
*/
|
*/
|
||||||
qdev_device_add_from_qdict(opt->opts, true, &error_fatal);
|
dev = qdev_device_add_from_qdict(opt->opts, true, &error_fatal);
|
||||||
|
object_unref(OBJECT(dev));
|
||||||
loc_pop(&opt->loc);
|
loc_pop(&opt->loc);
|
||||||
}
|
}
|
||||||
rom_reset_order_override();
|
rom_reset_order_override();
|
||||||
|
@ -2887,7 +2889,9 @@ void qemu_init(int argc, char **argv, char **envp)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QEMU_OPTION_drive:
|
case QEMU_OPTION_drive:
|
||||||
if (drive_def(optarg) == NULL) {
|
opts = qemu_opts_parse_noisily(qemu_find_opts("drive"),
|
||||||
|
optarg, false);
|
||||||
|
if (opts == NULL) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -104,6 +104,19 @@ static void help(void)
|
||||||
" export the specified block node over FUSE\n"
|
" export the specified block node over FUSE\n"
|
||||||
"\n"
|
"\n"
|
||||||
#endif /* CONFIG_FUSE */
|
#endif /* CONFIG_FUSE */
|
||||||
|
#ifdef CONFIG_VHOST_USER_BLK_SERVER
|
||||||
|
" --export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,\n"
|
||||||
|
" addr.type=unix,addr.path=<socket-path>[,writable=on|off]\n"
|
||||||
|
" [,logical-block-size=<block-size>][,num-queues=<num-queues>]\n"
|
||||||
|
" export the specified block node as a\n"
|
||||||
|
" vhost-user-blk device over UNIX domain socket\n"
|
||||||
|
" --export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,\n"
|
||||||
|
" fd,addr.str=<fd>[,writable=on|off]\n"
|
||||||
|
" [,logical-block-size=<block-size>][,num-queues=<num-queues>]\n"
|
||||||
|
" export the specified block node as a\n"
|
||||||
|
" vhost-user-blk device over file descriptor\n"
|
||||||
|
"\n"
|
||||||
|
#endif /* CONFIG_VHOST_USER_BLK_SERVER */
|
||||||
" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n"
|
" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n"
|
||||||
" configure a QMP monitor\n"
|
" configure a QMP monitor\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
|
@ -251,6 +251,7 @@ $QEMU_IO -c "write -P 0 0 64k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_test
|
||||||
$QEMU_IO -c "write 0 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
$QEMU_IO -c "write 0 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
$QEMU_IO -c "write 8k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
$QEMU_IO -c "write 8k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
$QEMU_IO -c "write 17k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
$QEMU_IO -c "write 17k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
$QEMU_IO -c "write -P 0 65k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
|
||||||
for min_sparse in 4k 8k; do
|
for min_sparse in 4k 8k; do
|
||||||
echo
|
echo
|
||||||
|
|
|
@ -192,6 +192,8 @@ wrote 1024/1024 bytes at offset 8192
|
||||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
wrote 1024/1024 bytes at offset 17408
|
wrote 1024/1024 bytes at offset 17408
|
||||||
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 1024/1024 bytes at offset 66560
|
||||||
|
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
convert -S 4k
|
convert -S 4k
|
||||||
[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET},
|
[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET},
|
||||||
|
|
|
@ -204,7 +204,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
|
||||||
"name": "file",
|
"name": "file",
|
||||||
"parent": 5,
|
"parent": 5,
|
||||||
"shared-perm": [
|
"shared-perm": [
|
||||||
"graph-mod",
|
|
||||||
"write-unchanged",
|
"write-unchanged",
|
||||||
"consistent-read"
|
"consistent-read"
|
||||||
],
|
],
|
||||||
|
@ -219,7 +218,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
|
||||||
"name": "backing",
|
"name": "backing",
|
||||||
"parent": 5,
|
"parent": 5,
|
||||||
"shared-perm": [
|
"shared-perm": [
|
||||||
"graph-mod",
|
|
||||||
"resize",
|
"resize",
|
||||||
"write-unchanged",
|
"write-unchanged",
|
||||||
"write",
|
"write",
|
||||||
|
@ -233,7 +231,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
|
||||||
"name": "file",
|
"name": "file",
|
||||||
"parent": 3,
|
"parent": 3,
|
||||||
"shared-perm": [
|
"shared-perm": [
|
||||||
"graph-mod",
|
|
||||||
"write-unchanged",
|
"write-unchanged",
|
||||||
"consistent-read"
|
"consistent-read"
|
||||||
],
|
],
|
||||||
|
@ -246,7 +243,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev
|
||||||
"name": "backing",
|
"name": "backing",
|
||||||
"parent": 3,
|
"parent": 3,
|
||||||
"shared-perm": [
|
"shared-perm": [
|
||||||
"graph-mod",
|
|
||||||
"resize",
|
"resize",
|
||||||
"write-unchanged",
|
"write-unchanged",
|
||||||
"write",
|
"write",
|
||||||
|
|
|
@ -230,8 +230,29 @@ echo '=== Writable export ==='
|
||||||
fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
|
fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
|
||||||
|
|
||||||
# Check that writing to the read-only export fails
|
# Check that writing to the read-only export fails
|
||||||
$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
|
output=$($QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
|
||||||
| _filter_qemu_io | _filter_testdir | _filter_imgfmt
|
| _filter_qemu_io | _filter_testdir | _filter_imgfmt)
|
||||||
|
|
||||||
|
# Expected reference output: Opening the file fails because it has no
|
||||||
|
# write permission
|
||||||
|
reference="Could not open 'TEST_DIR/t.IMGFMT': Permission denied"
|
||||||
|
|
||||||
|
if echo "$output" | grep -q "$reference"; then
|
||||||
|
echo "Writing to read-only export failed: OK"
|
||||||
|
elif echo "$output" | grep -q "write failed: Permission denied"; then
|
||||||
|
# With CAP_DAC_OVERRIDE (e.g. when running this test as root), the export
|
||||||
|
# can be opened regardless of its file permissions, but writing will then
|
||||||
|
# fail. This is not the result for which we want to test, so count this as
|
||||||
|
# a SKIP.
|
||||||
|
_casenotrun "Opening RO export as R/W succeeded, perhaps because of" \
|
||||||
|
"CAP_DAC_OVERRIDE"
|
||||||
|
|
||||||
|
# Still, write this to the reference output to make the test pass
|
||||||
|
echo "Writing to read-only export failed: OK"
|
||||||
|
else
|
||||||
|
echo "Writing to read-only export failed: ERROR"
|
||||||
|
echo "$output"
|
||||||
|
fi
|
||||||
|
|
||||||
# But here it should work
|
# But here it should work
|
||||||
$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
|
$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
|
||||||
|
|
|
@ -95,7 +95,7 @@ virtual size: 0 B (0 bytes)
|
||||||
'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true
|
'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true
|
||||||
} }
|
} }
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
|
Writing to read-only export failed: OK
|
||||||
wrote 65536/65536 bytes at offset 1048576
|
wrote 65536/65536 bytes at offset 1048576
|
||||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
wrote 65536/65536 bytes at offset 1048576
|
wrote 65536/65536 bytes at offset 1048576
|
||||||
|
|
|
@ -174,19 +174,17 @@ class TestRunner(ContextManager['TestRunner']):
|
||||||
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
||||||
self._stack.close()
|
self._stack.close()
|
||||||
|
|
||||||
def test_print_one_line(self, test: str, starttime: str,
|
def test_print_one_line(self, test: str,
|
||||||
|
test_field_width: int,
|
||||||
|
starttime: str,
|
||||||
endtime: Optional[str] = None, status: str = '...',
|
endtime: Optional[str] = None, status: str = '...',
|
||||||
lasttime: Optional[float] = None,
|
lasttime: Optional[float] = None,
|
||||||
thistime: Optional[float] = None,
|
thistime: Optional[float] = None,
|
||||||
description: str = '',
|
description: str = '',
|
||||||
test_field_width: Optional[int] = None,
|
|
||||||
end: str = '\n') -> None:
|
end: str = '\n') -> None:
|
||||||
""" Print short test info before/after test run """
|
""" Print short test info before/after test run """
|
||||||
test = os.path.basename(test)
|
test = os.path.basename(test)
|
||||||
|
|
||||||
if test_field_width is None:
|
|
||||||
test_field_width = 8
|
|
||||||
|
|
||||||
if self.makecheck and status != '...':
|
if self.makecheck and status != '...':
|
||||||
if status and status != 'pass':
|
if status and status != 'pass':
|
||||||
status = f' [{status}]'
|
status = f' [{status}]'
|
||||||
|
@ -328,7 +326,7 @@ class TestRunner(ContextManager['TestRunner']):
|
||||||
casenotrun=casenotrun)
|
casenotrun=casenotrun)
|
||||||
|
|
||||||
def run_test(self, test: str,
|
def run_test(self, test: str,
|
||||||
test_field_width: Optional[int] = None,
|
test_field_width: int,
|
||||||
mp: bool = False) -> TestResult:
|
mp: bool = False) -> TestResult:
|
||||||
"""
|
"""
|
||||||
Run one test and print short status
|
Run one test and print short status
|
||||||
|
@ -347,20 +345,21 @@ class TestRunner(ContextManager['TestRunner']):
|
||||||
|
|
||||||
if not self.makecheck:
|
if not self.makecheck:
|
||||||
self.test_print_one_line(test=test,
|
self.test_print_one_line(test=test,
|
||||||
|
test_field_width=test_field_width,
|
||||||
status = 'started' if mp else '...',
|
status = 'started' if mp else '...',
|
||||||
starttime=start,
|
starttime=start,
|
||||||
lasttime=last_el,
|
lasttime=last_el,
|
||||||
end = '\n' if mp else '\r',
|
end = '\n' if mp else '\r')
|
||||||
test_field_width=test_field_width)
|
|
||||||
|
|
||||||
res = self.do_run_test(test, mp)
|
res = self.do_run_test(test, mp)
|
||||||
|
|
||||||
end = datetime.datetime.now().strftime('%H:%M:%S')
|
end = datetime.datetime.now().strftime('%H:%M:%S')
|
||||||
self.test_print_one_line(test=test, status=res.status,
|
self.test_print_one_line(test=test,
|
||||||
|
test_field_width=test_field_width,
|
||||||
|
status=res.status,
|
||||||
starttime=start, endtime=end,
|
starttime=start, endtime=end,
|
||||||
lasttime=last_el, thistime=res.elapsed,
|
lasttime=last_el, thistime=res.elapsed,
|
||||||
description=res.description,
|
description=res.description)
|
||||||
test_field_width=test_field_width)
|
|
||||||
|
|
||||||
if res.casenotrun:
|
if res.casenotrun:
|
||||||
print(res.casenotrun)
|
print(res.casenotrun)
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# group: rw quick
|
||||||
|
#
|
||||||
|
# Test what happens when a stream job completes in a blk_drain().
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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 os
|
||||||
|
import iotests
|
||||||
|
from iotests import imgfmt, qemu_img_create, qemu_io_silent, QMPTestCase
|
||||||
|
|
||||||
|
|
||||||
|
image_size = 1 * 1024 * 1024
|
||||||
|
data_size = 64 * 1024
|
||||||
|
base = os.path.join(iotests.test_dir, 'base.img')
|
||||||
|
top = os.path.join(iotests.test_dir, 'top.img')
|
||||||
|
|
||||||
|
|
||||||
|
# We want to test completing a stream job in a blk_drain().
|
||||||
|
#
|
||||||
|
# The blk_drain() we are going to use is a virtio-scsi device resetting,
|
||||||
|
# which we can trigger by resetting the system.
|
||||||
|
#
|
||||||
|
# In order to have the block job complete on drain, we (1) throttle its
|
||||||
|
# base image so we can start the drain after it has begun, but before it
|
||||||
|
# completes, and (2) make it encounter an I/O error on the ensuing write.
|
||||||
|
# (If it completes regularly, the completion happens after the drain for
|
||||||
|
# some reason.)
|
||||||
|
|
||||||
|
class TestStreamErrorOnReset(QMPTestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
Create two images:
|
||||||
|
- base image {base} with {data_size} bytes allocated
|
||||||
|
- top image {top} without any data allocated
|
||||||
|
|
||||||
|
And the following VM configuration:
|
||||||
|
- base image throttled to {data_size}
|
||||||
|
- top image with a blkdebug configuration so the first write access
|
||||||
|
to it will result in an error
|
||||||
|
- top image is attached to a virtio-scsi device
|
||||||
|
"""
|
||||||
|
assert qemu_img_create('-f', imgfmt, base, str(image_size)) == 0
|
||||||
|
assert qemu_io_silent('-c', f'write 0 {data_size}', base) == 0
|
||||||
|
assert qemu_img_create('-f', imgfmt, top, str(image_size)) == 0
|
||||||
|
|
||||||
|
self.vm = iotests.VM()
|
||||||
|
self.vm.add_args('-accel', 'tcg') # Make throttling work properly
|
||||||
|
self.vm.add_object(self.vm.qmp_to_opts({
|
||||||
|
'qom-type': 'throttle-group',
|
||||||
|
'id': 'thrgr',
|
||||||
|
'x-bps-total': str(data_size)
|
||||||
|
}))
|
||||||
|
self.vm.add_blockdev(self.vm.qmp_to_opts({
|
||||||
|
'driver': imgfmt,
|
||||||
|
'node-name': 'base',
|
||||||
|
'file': {
|
||||||
|
'driver': 'throttle',
|
||||||
|
'throttle-group': 'thrgr',
|
||||||
|
'file': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
self.vm.add_blockdev(self.vm.qmp_to_opts({
|
||||||
|
'driver': imgfmt,
|
||||||
|
'node-name': 'top',
|
||||||
|
'file': {
|
||||||
|
'driver': 'blkdebug',
|
||||||
|
'node-name': 'top-blkdebug',
|
||||||
|
'inject-error': [{
|
||||||
|
'event': 'pwritev',
|
||||||
|
'immediately': 'true',
|
||||||
|
'once': 'true'
|
||||||
|
}],
|
||||||
|
'image': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': top
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'backing': 'base'
|
||||||
|
}))
|
||||||
|
self.vm.add_device(self.vm.qmp_to_opts({
|
||||||
|
'driver': 'virtio-scsi',
|
||||||
|
'id': 'vscsi'
|
||||||
|
}))
|
||||||
|
self.vm.add_device(self.vm.qmp_to_opts({
|
||||||
|
'driver': 'scsi-hd',
|
||||||
|
'bus': 'vscsi.0',
|
||||||
|
'drive': 'top'
|
||||||
|
}))
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
self.vm.shutdown()
|
||||||
|
os.remove(top)
|
||||||
|
os.remove(base)
|
||||||
|
|
||||||
|
def test_stream_error_on_reset(self) -> None:
|
||||||
|
# Launch a stream job, which will take at least a second to
|
||||||
|
# complete, because the base image is throttled (so we can
|
||||||
|
# get in between it having started and it having completed)
|
||||||
|
res = self.vm.qmp('block-stream', job_id='stream', device='top')
|
||||||
|
self.assert_qmp(res, 'return', {})
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ev = self.vm.event_wait('JOB_STATUS_CHANGE')
|
||||||
|
if ev['data']['status'] == 'running':
|
||||||
|
# Once the stream job is running, reset the system, which
|
||||||
|
# forces the virtio-scsi device to be reset, thus draining
|
||||||
|
# the stream job, and making it complete. Completing
|
||||||
|
# inside of that drain should not result in a segfault.
|
||||||
|
res = self.vm.qmp('system_reset')
|
||||||
|
self.assert_qmp(res, 'return', {})
|
||||||
|
elif ev['data']['status'] == 'null':
|
||||||
|
# The test is done once the job is gone
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Passes with any format with backing file support, but qed and
|
||||||
|
# qcow1 do not seem to exercise the used-to-be problematic code
|
||||||
|
# path, so there is no point in having them in this list
|
||||||
|
iotests.main(supported_fmts=['qcow2', 'vmdk'],
|
||||||
|
supported_protocols=['file'])
|
|
@ -0,0 +1,5 @@
|
||||||
|
.
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 1 tests
|
||||||
|
|
||||||
|
OK
|
|
@ -77,6 +77,23 @@ static void test_pci_unplug_request(void)
|
||||||
qtest_quit(qtest);
|
qtest_quit(qtest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_pci_unplug_json_request(void)
|
||||||
|
{
|
||||||
|
QTestState *qtest = qtest_initf(
|
||||||
|
"-device '{\"driver\": \"virtio-mouse-pci\", \"id\": \"dev0\"}'");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request device removal. As the guest is not running, the request won't
|
||||||
|
* be processed. However during system reset, the removal will be
|
||||||
|
* handled, removing the device.
|
||||||
|
*/
|
||||||
|
device_del(qtest, "dev0");
|
||||||
|
system_reset(qtest);
|
||||||
|
wait_device_deleted_event(qtest, "dev0");
|
||||||
|
|
||||||
|
qtest_quit(qtest);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_ccw_unplug(void)
|
static void test_ccw_unplug(void)
|
||||||
{
|
{
|
||||||
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
|
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
|
||||||
|
@ -145,6 +162,8 @@ int main(int argc, char **argv)
|
||||||
*/
|
*/
|
||||||
qtest_add_func("/device-plug/pci-unplug-request",
|
qtest_add_func("/device-plug/pci-unplug-request",
|
||||||
test_pci_unplug_request);
|
test_pci_unplug_request);
|
||||||
|
qtest_add_func("/device-plug/pci-unplug-json-request",
|
||||||
|
test_pci_unplug_json_request);
|
||||||
|
|
||||||
if (!strcmp(arch, "s390x")) {
|
if (!strcmp(arch, "s390x")) {
|
||||||
qtest_add_func("/device-plug/ccw-unplug",
|
qtest_add_func("/device-plug/ccw-unplug",
|
||||||
|
|
Loading…
Reference in New Issue