Block layer patches
- mirror: Fix active mirror deadlock - replication: Fix crashes due to operations on wrong BdrvChild - configure: Add option to use driver whitelist even in tools - vvfat: Fix crash when opening image read-write - export: Fix crash in error path with fixed-iothread=false -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmD25yMRHGt3b2xmQHJl ZGhhdC5jb20ACgkQfwmycsiPL9aABA//d3/0gZjgQu6/cyH6y1CL/xMTIv4W+vq2 3J/jrzEFpP0Jfx/8RrrUcS8kp0MOQ7xyU1CtN/lkM7l7LKoKmV0uSe1pav/sK2xv qVCOikAIouMZB1Mc2QTbGMgJA3aVUPod76f3YR4f0fGpsr9jODowpYAQ+coHzx/N 51nL57A8VXqEjIQnV5+k3sCHiXI5Sdkwp+FFqTs9CGNOr4qyKMZKKrMWbIrlm5bn rs/b6mzUsfVP2y61wtDQLIomxY/VHE5oix6eEMvmvIq3tT5fICYoQSS2J6c7Qj2J l83P/NwAxu7EzGBAEvbr0nriu2NVRrKYkUso9/+0D4Y0xboOwTwgvKzK4sbdXPuH +XIY3S3LIcnX0UMKAdYYAdPf9UJBVQLTq58fBpPKvEcWNQwuKEezaBBZ19+rbBWm bC3Z8sxMrFla7/Elecv5PcJSXAsbK9+6m4ljhQMuN05KSABV8BbnU/6r+iNsoo+E K187Y4J4GPYED44CRyMcm6pTxJGKs13zMUSfTFpDiWOdx17/qZCdOSwCafvsap5b 3VvMsfOgUfDrJ+ChaVAnMZpB556Nt83SuczqmEkZvq3xoH6t/NKPnH+rVGIll3Cc VyAAb7Sf9PADRKg/HPYUjt3axkM3ErEPpK7AUu+a756Ln+TAhMpARwjhPrpemkxQ sOJNFe3Ay+I= =3G16 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches - mirror: Fix active mirror deadlock - replication: Fix crashes due to operations on wrong BdrvChild - configure: Add option to use driver whitelist even in tools - vvfat: Fix crash when opening image read-write - export: Fix crash in error path with fixed-iothread=false # gpg: Signature made Tue 20 Jul 2021 16:09:23 BST # 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/307: Test iothread conflict for exports block/export: Conditionally ignore set-context error block/vvfat: fix: drop backing replication: Remove workaround replication: Properly attach children replication: Reduce usage of s->hidden_disk and s->secondary_disk replication: Remove s->active_disk block: Add option to use driver whitelist even in tools block/mirror: fix active mirror dead-lock in mirror_wait_on_conflicts iotest 151: add test-case that shows active mirror dead-lock block/mirror: set .co for active-write MirrorOp objects Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c04b4d9e6b
3
block.c
3
block.c
|
@ -6162,6 +6162,9 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
|||
|
||||
void bdrv_init(void)
|
||||
{
|
||||
#ifdef CONFIG_BDRV_WHITELIST_TOOLS
|
||||
use_bdrv_whitelist = 1;
|
||||
#endif
|
||||
module_call_init(MODULE_INIT_BLOCK);
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
|
|||
if (export->has_iothread) {
|
||||
IOThread *iothread;
|
||||
AioContext *new_ctx;
|
||||
Error **set_context_errp;
|
||||
|
||||
iothread = iothread_by_id(export->iothread);
|
||||
if (!iothread) {
|
||||
|
@ -120,7 +121,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
|
|||
|
||||
new_ctx = iothread_get_aio_context(iothread);
|
||||
|
||||
ret = bdrv_try_set_aio_context(bs, new_ctx, errp);
|
||||
/* Ignore errors with fixed-iothread=false */
|
||||
set_context_errp = fixed_iothread ? errp : NULL;
|
||||
ret = bdrv_try_set_aio_context(bs, new_ctx, set_context_errp);
|
||||
if (ret == 0) {
|
||||
aio_context_release(ctx);
|
||||
aio_context_acquire(new_ctx);
|
||||
|
|
|
@ -107,6 +107,7 @@ struct MirrorOp {
|
|||
bool is_in_flight;
|
||||
CoQueue waiting_requests;
|
||||
Coroutine *co;
|
||||
MirrorOp *waiting_for_op;
|
||||
|
||||
QTAILQ_ENTRY(MirrorOp) next;
|
||||
};
|
||||
|
@ -159,7 +160,18 @@ static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self,
|
|||
if (ranges_overlap(self_start_chunk, self_nb_chunks,
|
||||
op_start_chunk, op_nb_chunks))
|
||||
{
|
||||
/*
|
||||
* If the operation is already (indirectly) waiting for us, or
|
||||
* will wait for us as soon as it wakes up, then just go on
|
||||
* (instead of producing a deadlock in the former case).
|
||||
*/
|
||||
if (op->waiting_for_op) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self->waiting_for_op = op;
|
||||
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
||||
self->waiting_for_op = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1343,6 +1355,7 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s,
|
|||
.bytes = bytes,
|
||||
.is_active_write = true,
|
||||
.is_in_flight = true,
|
||||
.co = qemu_coroutine_self(),
|
||||
};
|
||||
qemu_co_queue_init(&op->waiting_requests);
|
||||
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
|
||||
|
|
|
@ -35,7 +35,6 @@ typedef enum {
|
|||
typedef struct BDRVReplicationState {
|
||||
ReplicationMode mode;
|
||||
ReplicationStage stage;
|
||||
BdrvChild *active_disk;
|
||||
BlockJob *commit_job;
|
||||
BdrvChild *hidden_disk;
|
||||
BdrvChild *secondary_disk;
|
||||
|
@ -166,7 +165,12 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
|
|||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
*nperm = BLK_PERM_CONSISTENT_READ;
|
||||
if (role & BDRV_CHILD_PRIMARY) {
|
||||
*nperm = BLK_PERM_CONSISTENT_READ;
|
||||
} else {
|
||||
*nperm = 0;
|
||||
}
|
||||
|
||||
if ((bs->open_flags & (BDRV_O_INACTIVE | BDRV_O_RDWR)) == BDRV_O_RDWR) {
|
||||
*nperm |= BLK_PERM_WRITE;
|
||||
}
|
||||
|
@ -307,8 +311,10 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||
static void secondary_do_checkpoint(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
BdrvChild *active_disk = bs->file;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
|
@ -323,13 +329,13 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!s->active_disk->bs->drv) {
|
||||
if (!active_disk->bs->drv) {
|
||||
error_setg(errp, "Active disk %s is ejected",
|
||||
s->active_disk->bs->node_name);
|
||||
active_disk->bs->node_name);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bdrv_make_empty(s->active_disk, errp);
|
||||
ret = bdrv_make_empty(active_disk, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -340,17 +346,7 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
BlockBackend *blk = blk_new(qemu_get_current_aio_context(),
|
||||
BLK_PERM_WRITE, BLK_PERM_ALL);
|
||||
blk_insert_bs(blk, s->hidden_disk->bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
blk_unref(blk);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = blk_make_empty(blk, errp);
|
||||
blk_unref(blk);
|
||||
ret = bdrv_make_empty(s->hidden_disk, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -365,27 +361,35 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
|||
Error **errp)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
BdrvChild *hidden_disk, *secondary_disk;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
|
||||
/*
|
||||
* s->hidden_disk and s->secondary_disk may not be set yet, as they will
|
||||
* only be set after the children are writable.
|
||||
*/
|
||||
hidden_disk = bs->file->bs->backing;
|
||||
secondary_disk = hidden_disk->bs->backing;
|
||||
|
||||
if (writable) {
|
||||
s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs);
|
||||
s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs);
|
||||
s->orig_hidden_read_only = bdrv_is_read_only(hidden_disk->bs);
|
||||
s->orig_secondary_read_only = bdrv_is_read_only(secondary_disk->bs);
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_begin(s->hidden_disk->bs);
|
||||
bdrv_subtree_drained_begin(s->secondary_disk->bs);
|
||||
bdrv_subtree_drained_begin(hidden_disk->bs);
|
||||
bdrv_subtree_drained_begin(secondary_disk->bs);
|
||||
|
||||
if (s->orig_hidden_read_only) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, hidden_disk->bs,
|
||||
opts, true);
|
||||
}
|
||||
|
||||
if (s->orig_secondary_read_only) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, secondary_disk->bs,
|
||||
opts, true);
|
||||
}
|
||||
|
||||
|
@ -400,8 +404,8 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
|||
}
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_end(s->hidden_disk->bs);
|
||||
bdrv_subtree_drained_end(s->secondary_disk->bs);
|
||||
bdrv_subtree_drained_end(hidden_disk->bs);
|
||||
bdrv_subtree_drained_end(secondary_disk->bs);
|
||||
}
|
||||
|
||||
static void backup_job_cleanup(BlockDriverState *bs)
|
||||
|
@ -458,6 +462,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
BlockDriverState *bs = rs->opaque;
|
||||
BDRVReplicationState *s;
|
||||
BlockDriverState *top_bs;
|
||||
BdrvChild *active_disk, *hidden_disk, *secondary_disk;
|
||||
int64_t active_length, hidden_length, disk_length;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
@ -495,32 +500,31 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
case REPLICATION_MODE_PRIMARY:
|
||||
break;
|
||||
case REPLICATION_MODE_SECONDARY:
|
||||
s->active_disk = bs->file;
|
||||
if (!s->active_disk || !s->active_disk->bs ||
|
||||
!s->active_disk->bs->backing) {
|
||||
active_disk = bs->file;
|
||||
if (!active_disk || !active_disk->bs || !active_disk->bs->backing) {
|
||||
error_setg(errp, "Active disk doesn't have backing file");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
s->hidden_disk = s->active_disk->bs->backing;
|
||||
if (!s->hidden_disk->bs || !s->hidden_disk->bs->backing) {
|
||||
hidden_disk = active_disk->bs->backing;
|
||||
if (!hidden_disk->bs || !hidden_disk->bs->backing) {
|
||||
error_setg(errp, "Hidden disk doesn't have backing file");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
s->secondary_disk = s->hidden_disk->bs->backing;
|
||||
if (!s->secondary_disk->bs || !bdrv_has_blk(s->secondary_disk->bs)) {
|
||||
secondary_disk = hidden_disk->bs->backing;
|
||||
if (!secondary_disk->bs || !bdrv_has_blk(secondary_disk->bs)) {
|
||||
error_setg(errp, "The secondary disk doesn't have block backend");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify the length */
|
||||
active_length = bdrv_getlength(s->active_disk->bs);
|
||||
hidden_length = bdrv_getlength(s->hidden_disk->bs);
|
||||
disk_length = bdrv_getlength(s->secondary_disk->bs);
|
||||
active_length = bdrv_getlength(active_disk->bs);
|
||||
hidden_length = bdrv_getlength(hidden_disk->bs);
|
||||
disk_length = bdrv_getlength(secondary_disk->bs);
|
||||
if (active_length < 0 || hidden_length < 0 || disk_length < 0 ||
|
||||
active_length != hidden_length || hidden_length != disk_length) {
|
||||
error_setg(errp, "Active disk, hidden disk, secondary disk's length"
|
||||
|
@ -530,10 +534,10 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
}
|
||||
|
||||
/* Must be true, or the bdrv_getlength() calls would have failed */
|
||||
assert(s->active_disk->bs->drv && s->hidden_disk->bs->drv);
|
||||
assert(active_disk->bs->drv && hidden_disk->bs->drv);
|
||||
|
||||
if (!s->active_disk->bs->drv->bdrv_make_empty ||
|
||||
!s->hidden_disk->bs->drv->bdrv_make_empty) {
|
||||
if (!active_disk->bs->drv->bdrv_make_empty ||
|
||||
!hidden_disk->bs->drv->bdrv_make_empty) {
|
||||
error_setg(errp,
|
||||
"Active disk or hidden disk doesn't support make_empty");
|
||||
aio_context_release(aio_context);
|
||||
|
@ -548,6 +552,26 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
return;
|
||||
}
|
||||
|
||||
bdrv_ref(hidden_disk->bs);
|
||||
s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk",
|
||||
&child_of_bds, BDRV_CHILD_DATA,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_ref(secondary_disk->bs);
|
||||
s->secondary_disk = bdrv_attach_child(bs, secondary_disk->bs,
|
||||
"secondary disk", &child_of_bds,
|
||||
BDRV_CHILD_DATA, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
/* start backup job now */
|
||||
error_setg(&s->blocker,
|
||||
"Block device is in use by internal backup job");
|
||||
|
@ -586,7 +610,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
s->stage = BLOCK_REPLICATION_RUNNING;
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
secondary_do_checkpoint(bs, errp);
|
||||
}
|
||||
|
||||
s->error = 0;
|
||||
|
@ -615,7 +639,7 @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp)
|
|||
}
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
secondary_do_checkpoint(bs, errp);
|
||||
}
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
@ -652,8 +676,9 @@ static void replication_done(void *opaque, int ret)
|
|||
if (ret == 0) {
|
||||
s->stage = BLOCK_REPLICATION_DONE;
|
||||
|
||||
s->active_disk = NULL;
|
||||
bdrv_unref_child(bs, s->secondary_disk);
|
||||
s->secondary_disk = NULL;
|
||||
bdrv_unref_child(bs, s->hidden_disk);
|
||||
s->hidden_disk = NULL;
|
||||
s->error = 0;
|
||||
} else {
|
||||
|
@ -705,7 +730,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||
}
|
||||
|
||||
if (!failover) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
secondary_do_checkpoint(bs, errp);
|
||||
s->stage = BLOCK_REPLICATION_DONE;
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
|
@ -713,7 +738,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||
|
||||
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||
s->commit_job = commit_active_start(
|
||||
NULL, s->active_disk->bs, s->secondary_disk->bs,
|
||||
NULL, bs->file->bs, s->secondary_disk->bs,
|
||||
JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||
NULL, replication_done, bs, true, errp);
|
||||
break;
|
||||
|
|
|
@ -3098,26 +3098,6 @@ static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
|
|||
return BDRV_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = try_commit(s);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockDriver vvfat_write_target = {
|
||||
.format_name = "vvfat_write_target",
|
||||
.instance_size = sizeof(void*),
|
||||
.bdrv_co_pwritev = write_target_commit,
|
||||
};
|
||||
|
||||
static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
|
||||
int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
|
@ -3133,7 +3113,6 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
|||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
BlockDriver *bdrv_qcow = NULL;
|
||||
BlockDriverState *backing;
|
||||
QemuOpts *opts = NULL;
|
||||
int ret;
|
||||
int size = sector2cluster(s, s->sector_count);
|
||||
|
@ -3184,13 +3163,6 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
|||
unlink(s->qcow_filename);
|
||||
#endif
|
||||
|
||||
backing = bdrv_new_open_driver(&vvfat_write_target, NULL, BDRV_O_ALLOW_RDWR,
|
||||
&error_abort);
|
||||
*(void**) backing->opaque = s;
|
||||
|
||||
bdrv_set_backing_hd(s->bs, backing, &error_abort);
|
||||
bdrv_unref(backing);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
@ -3205,17 +3177,10 @@ static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
|
|||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (role & BDRV_CHILD_DATA) {
|
||||
/* This is a private node, nobody should try to attach to it */
|
||||
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
||||
*nshared = BLK_PERM_WRITE_UNCHANGED;
|
||||
} else {
|
||||
assert(role & BDRV_CHILD_COW);
|
||||
/* The backing file is there so 'commit' can use it. vvfat doesn't
|
||||
* access it in any way. */
|
||||
*nperm = 0;
|
||||
*nshared = BLK_PERM_ALL;
|
||||
}
|
||||
assert(role & BDRV_CHILD_DATA);
|
||||
/* This is a private node, nobody should try to attach to it */
|
||||
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
||||
*nshared = BLK_PERM_WRITE_UNCHANGED;
|
||||
}
|
||||
|
||||
static void vvfat_close(BlockDriverState *bs)
|
||||
|
|
14
configure
vendored
14
configure
vendored
|
@ -243,6 +243,7 @@ cross_prefix=""
|
|||
audio_drv_list=""
|
||||
block_drv_rw_whitelist=""
|
||||
block_drv_ro_whitelist=""
|
||||
block_drv_whitelist_tools="no"
|
||||
host_cc="cc"
|
||||
audio_win_int=""
|
||||
libs_qga=""
|
||||
|
@ -1016,6 +1017,10 @@ for opt do
|
|||
;;
|
||||
--block-drv-ro-whitelist=*) block_drv_ro_whitelist=$(echo "$optarg" | sed -e 's/,/ /g')
|
||||
;;
|
||||
--enable-block-drv-whitelist-in-tools) block_drv_whitelist_tools="yes"
|
||||
;;
|
||||
--disable-block-drv-whitelist-in-tools) block_drv_whitelist_tools="no"
|
||||
;;
|
||||
--enable-debug-tcg) debug_tcg="yes"
|
||||
;;
|
||||
--disable-debug-tcg) debug_tcg="no"
|
||||
|
@ -1800,10 +1805,12 @@ Advanced options (experts only):
|
|||
--block-drv-whitelist=L Same as --block-drv-rw-whitelist=L
|
||||
--block-drv-rw-whitelist=L
|
||||
set block driver read-write whitelist
|
||||
(affects only QEMU, not qemu-img)
|
||||
(by default affects only QEMU, not tools like qemu-img)
|
||||
--block-drv-ro-whitelist=L
|
||||
set block driver read-only whitelist
|
||||
(affects only QEMU, not qemu-img)
|
||||
(by default affects only QEMU, not tools like qemu-img)
|
||||
--enable-block-drv-whitelist-in-tools
|
||||
use block whitelist also in tools instead of only QEMU
|
||||
--enable-trace-backends=B Set trace backend
|
||||
Available backends: $trace_backend_list
|
||||
--with-trace-file=NAME Full PATH,NAME of file to store traces
|
||||
|
@ -4583,6 +4590,9 @@ if test "$audio_win_int" = "yes" ; then
|
|||
fi
|
||||
echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak
|
||||
echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak
|
||||
if test "$block_drv_whitelist_tools" = "yes" ; then
|
||||
echo "CONFIG_BDRV_WHITELIST_TOOLS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$xfs" = "yes" ; then
|
||||
echo "CONFIG_XFS=y" >> $config_host_mak
|
||||
fi
|
||||
|
|
|
@ -2996,6 +2996,7 @@ summary_info += {'coroutine pool': config_host['CONFIG_COROUTINE_POOL'] == '1
|
|||
if have_block
|
||||
summary_info += {'Block whitelist (rw)': config_host['CONFIG_BDRV_RW_WHITELIST']}
|
||||
summary_info += {'Block whitelist (ro)': config_host['CONFIG_BDRV_RO_WHITELIST']}
|
||||
summary_info += {'Use block whitelist in tools': config_host.has_key('CONFIG_BDRV_WHITELIST_TOOLS')}
|
||||
summary_info += {'VirtFS support': have_virtfs}
|
||||
summary_info += {'build virtiofs daemon': have_virtiofsd}
|
||||
summary_info += {'Live block migration': config_host.has_key('CONFIG_LIVE_BLOCK_MIGRATION')}
|
||||
|
|
|
@ -38,8 +38,9 @@ class TestActiveMirror(iotests.QMPTestCase):
|
|||
'if': 'none',
|
||||
'node-name': 'source-node',
|
||||
'driver': iotests.imgfmt,
|
||||
'file': {'driver': 'file',
|
||||
'filename': source_img}}
|
||||
'file': {'driver': 'blkdebug',
|
||||
'image': {'driver': 'file',
|
||||
'filename': source_img}}}
|
||||
|
||||
blk_target = {'node-name': 'target-node',
|
||||
'driver': iotests.imgfmt,
|
||||
|
@ -141,6 +142,55 @@ class TestActiveMirror(iotests.QMPTestCase):
|
|||
|
||||
self.potential_writes_in_flight = False
|
||||
|
||||
def testIntersectingActiveIO(self):
|
||||
# Fill the source image
|
||||
result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M')
|
||||
|
||||
# Start the block job (very slowly)
|
||||
result = self.vm.qmp('blockdev-mirror',
|
||||
job_id='mirror',
|
||||
filter_node_name='mirror-node',
|
||||
device='source-node',
|
||||
target='target-node',
|
||||
sync='full',
|
||||
copy_mode='write-blocking',
|
||||
speed=1)
|
||||
|
||||
self.vm.hmp_qemu_io('source', 'break write_aio A')
|
||||
self.vm.hmp_qemu_io('source', 'aio_write 0 1M') # 1
|
||||
self.vm.hmp_qemu_io('source', 'wait_break A')
|
||||
self.vm.hmp_qemu_io('source', 'aio_write 0 2M') # 2
|
||||
self.vm.hmp_qemu_io('source', 'aio_write 0 2M') # 3
|
||||
|
||||
# Now 2 and 3 are in mirror_wait_on_conflicts, waiting for 1
|
||||
|
||||
self.vm.hmp_qemu_io('source', 'break write_aio B')
|
||||
self.vm.hmp_qemu_io('source', 'aio_write 1M 2M') # 4
|
||||
self.vm.hmp_qemu_io('source', 'wait_break B')
|
||||
|
||||
# 4 doesn't wait for 2 and 3, because they didn't yet set
|
||||
# in_flight_bitmap. So, nothing prevents 4 to go except for our
|
||||
# break-point B.
|
||||
|
||||
self.vm.hmp_qemu_io('source', 'resume A')
|
||||
|
||||
# Now we resumed 1, so 2 and 3 goes to the next iteration of while loop
|
||||
# in mirror_wait_on_conflicts(). They don't exit, as bitmap is dirty
|
||||
# due to request 4.
|
||||
# In the past at that point 2 and 3 would wait for each other producing
|
||||
# a dead-lock. Now this is fixed and they will wait for request 4.
|
||||
|
||||
self.vm.hmp_qemu_io('source', 'resume B')
|
||||
|
||||
# After resuming 4, one of 2 and 3 goes first and set in_flight_bitmap,
|
||||
# so the other will wait for it.
|
||||
|
||||
result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
self.complete_and_wait(drive='mirror')
|
||||
|
||||
self.potential_writes_in_flight = False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2', 'raw'],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
...
|
||||
....
|
||||
----------------------------------------------------------------------
|
||||
Ran 3 tests
|
||||
Ran 4 tests
|
||||
|
||||
OK
|
||||
|
|
|
@ -41,9 +41,11 @@ with iotests.FilePath('image') as img, \
|
|||
iotests.log('=== Launch VM ===')
|
||||
|
||||
vm.add_object('iothread,id=iothread0')
|
||||
vm.add_object('iothread,id=iothread1')
|
||||
vm.add_blockdev(f'file,filename={img},node-name=file')
|
||||
vm.add_blockdev(f'{iotests.imgfmt},file=file,node-name=fmt')
|
||||
vm.add_blockdev('raw,file=file,node-name=ro,read-only=on')
|
||||
vm.add_blockdev('null-co,node-name=null')
|
||||
vm.add_device(f'id=scsi0,driver=virtio-scsi,iothread=iothread0')
|
||||
vm.launch()
|
||||
|
||||
|
@ -74,6 +76,19 @@ with iotests.FilePath('image') as img, \
|
|||
vm.qmp_log('query-block-exports')
|
||||
iotests.qemu_nbd_list_log('-k', socket)
|
||||
|
||||
iotests.log('\n=== Add export with conflicting iothread ===')
|
||||
|
||||
vm.qmp_log('device_add', id='sdb', driver='scsi-hd', drive='null')
|
||||
|
||||
# Should fail because of fixed-iothread
|
||||
vm.qmp_log('block-export-add', id='export1', type='nbd', node_name='null',
|
||||
iothread='iothread1', fixed_iothread=True, writable=True)
|
||||
|
||||
# Should ignore the iothread conflict, but then fail because of the
|
||||
# permission conflict (and not crash)
|
||||
vm.qmp_log('block-export-add', id='export1', type='nbd', node_name='null',
|
||||
iothread='iothread1', fixed_iothread=False, writable=True)
|
||||
|
||||
iotests.log('\n=== Add a writable export ===')
|
||||
|
||||
# This fails because share-rw=off
|
||||
|
|
|
@ -51,6 +51,14 @@ exports available: 1
|
|||
base:allocation
|
||||
|
||||
|
||||
=== Add export with conflicting iothread ===
|
||||
{"execute": "device_add", "arguments": {"drive": "null", "driver": "scsi-hd", "id": "sdb"}}
|
||||
{"return": {}}
|
||||
{"execute": "block-export-add", "arguments": {"fixed-iothread": true, "id": "export1", "iothread": "iothread1", "node-name": "null", "type": "nbd", "writable": true}}
|
||||
{"error": {"class": "GenericError", "desc": "Cannot change iothread of active block backend"}}
|
||||
{"execute": "block-export-add", "arguments": {"fixed-iothread": false, "id": "export1", "iothread": "iothread1", "node-name": "null", "type": "nbd", "writable": true}}
|
||||
{"error": {"class": "GenericError", "desc": "Permission conflict on node 'null': permissions 'write' are both required by an unnamed block device (uses node 'null' as 'root' child) and unshared by block device 'sdb' (uses node 'null' as 'root' child)."}}
|
||||
|
||||
=== Add a writable export ===
|
||||
{"execute": "block-export-add", "arguments": {"description": "This is the writable second export", "id": "export1", "name": "export1", "node-name": "fmt", "type": "nbd", "writable": true, "writethrough": true}}
|
||||
{"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt': permissions 'write' are both required by an unnamed block device (uses node 'fmt' as 'root' child) and unshared by block device 'sda' (uses node 'fmt' as 'root' child)."}}
|
||||
|
|
Loading…
Reference in a new issue