mirror of https://github.com/xemu-project/xemu.git
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)
|
void bdrv_init(void)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_BDRV_WHITELIST_TOOLS
|
||||||
|
use_bdrv_whitelist = 1;
|
||||||
|
#endif
|
||||||
module_call_init(MODULE_INIT_BLOCK);
|
module_call_init(MODULE_INIT_BLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
|
||||||
if (export->has_iothread) {
|
if (export->has_iothread) {
|
||||||
IOThread *iothread;
|
IOThread *iothread;
|
||||||
AioContext *new_ctx;
|
AioContext *new_ctx;
|
||||||
|
Error **set_context_errp;
|
||||||
|
|
||||||
iothread = iothread_by_id(export->iothread);
|
iothread = iothread_by_id(export->iothread);
|
||||||
if (!iothread) {
|
if (!iothread) {
|
||||||
|
@ -120,7 +121,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
|
||||||
|
|
||||||
new_ctx = iothread_get_aio_context(iothread);
|
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) {
|
if (ret == 0) {
|
||||||
aio_context_release(ctx);
|
aio_context_release(ctx);
|
||||||
aio_context_acquire(new_ctx);
|
aio_context_acquire(new_ctx);
|
||||||
|
|
|
@ -107,6 +107,7 @@ struct MirrorOp {
|
||||||
bool is_in_flight;
|
bool is_in_flight;
|
||||||
CoQueue waiting_requests;
|
CoQueue waiting_requests;
|
||||||
Coroutine *co;
|
Coroutine *co;
|
||||||
|
MirrorOp *waiting_for_op;
|
||||||
|
|
||||||
QTAILQ_ENTRY(MirrorOp) next;
|
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,
|
if (ranges_overlap(self_start_chunk, self_nb_chunks,
|
||||||
op_start_chunk, op_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);
|
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
||||||
|
self->waiting_for_op = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1343,6 +1355,7 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s,
|
||||||
.bytes = bytes,
|
.bytes = bytes,
|
||||||
.is_active_write = true,
|
.is_active_write = true,
|
||||||
.is_in_flight = true,
|
.is_in_flight = true,
|
||||||
|
.co = qemu_coroutine_self(),
|
||||||
};
|
};
|
||||||
qemu_co_queue_init(&op->waiting_requests);
|
qemu_co_queue_init(&op->waiting_requests);
|
||||||
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
|
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
|
||||||
|
|
|
@ -35,7 +35,6 @@ typedef enum {
|
||||||
typedef struct BDRVReplicationState {
|
typedef struct BDRVReplicationState {
|
||||||
ReplicationMode mode;
|
ReplicationMode mode;
|
||||||
ReplicationStage stage;
|
ReplicationStage stage;
|
||||||
BdrvChild *active_disk;
|
|
||||||
BlockJob *commit_job;
|
BlockJob *commit_job;
|
||||||
BdrvChild *hidden_disk;
|
BdrvChild *hidden_disk;
|
||||||
BdrvChild *secondary_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 perm, uint64_t shared,
|
||||||
uint64_t *nperm, uint64_t *nshared)
|
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) {
|
if ((bs->open_flags & (BDRV_O_INACTIVE | BDRV_O_RDWR)) == BDRV_O_RDWR) {
|
||||||
*nperm |= BLK_PERM_WRITE;
|
*nperm |= BLK_PERM_WRITE;
|
||||||
}
|
}
|
||||||
|
@ -307,8 +311,10 @@ out:
|
||||||
return ret;
|
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;
|
Error *local_err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -323,13 +329,13 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s->active_disk->bs->drv) {
|
if (!active_disk->bs->drv) {
|
||||||
error_setg(errp, "Active disk %s is ejected",
|
error_setg(errp, "Active disk %s is ejected",
|
||||||
s->active_disk->bs->node_name);
|
active_disk->bs->node_name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_make_empty(s->active_disk, errp);
|
ret = bdrv_make_empty(active_disk, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -340,17 +346,7 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockBackend *blk = blk_new(qemu_get_current_aio_context(),
|
ret = bdrv_make_empty(s->hidden_disk, errp);
|
||||||
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);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -365,27 +361,35 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVReplicationState *s = bs->opaque;
|
BDRVReplicationState *s = bs->opaque;
|
||||||
|
BdrvChild *hidden_disk, *secondary_disk;
|
||||||
BlockReopenQueue *reopen_queue = NULL;
|
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) {
|
if (writable) {
|
||||||
s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs);
|
s->orig_hidden_read_only = bdrv_is_read_only(hidden_disk->bs);
|
||||||
s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_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(hidden_disk->bs);
|
||||||
bdrv_subtree_drained_begin(s->secondary_disk->bs);
|
bdrv_subtree_drained_begin(secondary_disk->bs);
|
||||||
|
|
||||||
if (s->orig_hidden_read_only) {
|
if (s->orig_hidden_read_only) {
|
||||||
QDict *opts = qdict_new();
|
QDict *opts = qdict_new();
|
||||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
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);
|
opts, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->orig_secondary_read_only) {
|
if (s->orig_secondary_read_only) {
|
||||||
QDict *opts = qdict_new();
|
QDict *opts = qdict_new();
|
||||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
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);
|
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(hidden_disk->bs);
|
||||||
bdrv_subtree_drained_end(s->secondary_disk->bs);
|
bdrv_subtree_drained_end(secondary_disk->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void backup_job_cleanup(BlockDriverState *bs)
|
static void backup_job_cleanup(BlockDriverState *bs)
|
||||||
|
@ -458,6 +462,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||||
BlockDriverState *bs = rs->opaque;
|
BlockDriverState *bs = rs->opaque;
|
||||||
BDRVReplicationState *s;
|
BDRVReplicationState *s;
|
||||||
BlockDriverState *top_bs;
|
BlockDriverState *top_bs;
|
||||||
|
BdrvChild *active_disk, *hidden_disk, *secondary_disk;
|
||||||
int64_t active_length, hidden_length, disk_length;
|
int64_t active_length, hidden_length, disk_length;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
@ -495,32 +500,31 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||||
case REPLICATION_MODE_PRIMARY:
|
case REPLICATION_MODE_PRIMARY:
|
||||||
break;
|
break;
|
||||||
case REPLICATION_MODE_SECONDARY:
|
case REPLICATION_MODE_SECONDARY:
|
||||||
s->active_disk = bs->file;
|
active_disk = bs->file;
|
||||||
if (!s->active_disk || !s->active_disk->bs ||
|
if (!active_disk || !active_disk->bs || !active_disk->bs->backing) {
|
||||||
!s->active_disk->bs->backing) {
|
|
||||||
error_setg(errp, "Active disk doesn't have backing file");
|
error_setg(errp, "Active disk doesn't have backing file");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->hidden_disk = s->active_disk->bs->backing;
|
hidden_disk = active_disk->bs->backing;
|
||||||
if (!s->hidden_disk->bs || !s->hidden_disk->bs->backing) {
|
if (!hidden_disk->bs || !hidden_disk->bs->backing) {
|
||||||
error_setg(errp, "Hidden disk doesn't have backing file");
|
error_setg(errp, "Hidden disk doesn't have backing file");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->secondary_disk = s->hidden_disk->bs->backing;
|
secondary_disk = hidden_disk->bs->backing;
|
||||||
if (!s->secondary_disk->bs || !bdrv_has_blk(s->secondary_disk->bs)) {
|
if (!secondary_disk->bs || !bdrv_has_blk(secondary_disk->bs)) {
|
||||||
error_setg(errp, "The secondary disk doesn't have block backend");
|
error_setg(errp, "The secondary disk doesn't have block backend");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* verify the length */
|
/* verify the length */
|
||||||
active_length = bdrv_getlength(s->active_disk->bs);
|
active_length = bdrv_getlength(active_disk->bs);
|
||||||
hidden_length = bdrv_getlength(s->hidden_disk->bs);
|
hidden_length = bdrv_getlength(hidden_disk->bs);
|
||||||
disk_length = bdrv_getlength(s->secondary_disk->bs);
|
disk_length = bdrv_getlength(secondary_disk->bs);
|
||||||
if (active_length < 0 || hidden_length < 0 || disk_length < 0 ||
|
if (active_length < 0 || hidden_length < 0 || disk_length < 0 ||
|
||||||
active_length != hidden_length || hidden_length != disk_length) {
|
active_length != hidden_length || hidden_length != disk_length) {
|
||||||
error_setg(errp, "Active disk, hidden disk, secondary disk's 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 */
|
/* 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 ||
|
if (!active_disk->bs->drv->bdrv_make_empty ||
|
||||||
!s->hidden_disk->bs->drv->bdrv_make_empty) {
|
!hidden_disk->bs->drv->bdrv_make_empty) {
|
||||||
error_setg(errp,
|
error_setg(errp,
|
||||||
"Active disk or hidden disk doesn't support make_empty");
|
"Active disk or hidden disk doesn't support make_empty");
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
|
@ -548,6 +552,26 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||||
return;
|
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 */
|
/* start backup job now */
|
||||||
error_setg(&s->blocker,
|
error_setg(&s->blocker,
|
||||||
"Block device is in use by internal backup job");
|
"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;
|
s->stage = BLOCK_REPLICATION_RUNNING;
|
||||||
|
|
||||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||||
secondary_do_checkpoint(s, errp);
|
secondary_do_checkpoint(bs, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->error = 0;
|
s->error = 0;
|
||||||
|
@ -615,7 +639,7 @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||||
secondary_do_checkpoint(s, errp);
|
secondary_do_checkpoint(bs, errp);
|
||||||
}
|
}
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
}
|
}
|
||||||
|
@ -652,8 +676,9 @@ static void replication_done(void *opaque, int ret)
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
s->stage = BLOCK_REPLICATION_DONE;
|
s->stage = BLOCK_REPLICATION_DONE;
|
||||||
|
|
||||||
s->active_disk = NULL;
|
bdrv_unref_child(bs, s->secondary_disk);
|
||||||
s->secondary_disk = NULL;
|
s->secondary_disk = NULL;
|
||||||
|
bdrv_unref_child(bs, s->hidden_disk);
|
||||||
s->hidden_disk = NULL;
|
s->hidden_disk = NULL;
|
||||||
s->error = 0;
|
s->error = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -705,7 +730,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!failover) {
|
if (!failover) {
|
||||||
secondary_do_checkpoint(s, errp);
|
secondary_do_checkpoint(bs, errp);
|
||||||
s->stage = BLOCK_REPLICATION_DONE;
|
s->stage = BLOCK_REPLICATION_DONE;
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
return;
|
return;
|
||||||
|
@ -713,7 +738,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||||
|
|
||||||
s->stage = BLOCK_REPLICATION_FAILOVER;
|
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||||
s->commit_job = commit_active_start(
|
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,
|
JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||||
NULL, replication_done, bs, true, errp);
|
NULL, replication_done, bs, true, errp);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -3098,26 +3098,6 @@ static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
|
||||||
return BDRV_BLOCK_DATA;
|
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,
|
static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
|
||||||
int *child_flags, QDict *child_options,
|
int *child_flags, QDict *child_options,
|
||||||
int parent_flags, QDict *parent_options)
|
int parent_flags, QDict *parent_options)
|
||||||
|
@ -3133,7 +3113,6 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVVVFATState *s = bs->opaque;
|
BDRVVVFATState *s = bs->opaque;
|
||||||
BlockDriver *bdrv_qcow = NULL;
|
BlockDriver *bdrv_qcow = NULL;
|
||||||
BlockDriverState *backing;
|
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
int size = sector2cluster(s, s->sector_count);
|
int size = sector2cluster(s, s->sector_count);
|
||||||
|
@ -3184,13 +3163,6 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||||
unlink(s->qcow_filename);
|
unlink(s->qcow_filename);
|
||||||
#endif
|
#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;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
@ -3205,17 +3177,10 @@ static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||||
uint64_t perm, uint64_t shared,
|
uint64_t perm, uint64_t shared,
|
||||||
uint64_t *nperm, uint64_t *nshared)
|
uint64_t *nperm, uint64_t *nshared)
|
||||||
{
|
{
|
||||||
if (role & BDRV_CHILD_DATA) {
|
assert(role & BDRV_CHILD_DATA);
|
||||||
/* This is a private node, nobody should try to attach to it */
|
/* This is a private node, nobody should try to attach to it */
|
||||||
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
||||||
*nshared = BLK_PERM_WRITE_UNCHANGED;
|
*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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vvfat_close(BlockDriverState *bs)
|
static void vvfat_close(BlockDriverState *bs)
|
||||||
|
|
|
@ -243,6 +243,7 @@ cross_prefix=""
|
||||||
audio_drv_list=""
|
audio_drv_list=""
|
||||||
block_drv_rw_whitelist=""
|
block_drv_rw_whitelist=""
|
||||||
block_drv_ro_whitelist=""
|
block_drv_ro_whitelist=""
|
||||||
|
block_drv_whitelist_tools="no"
|
||||||
host_cc="cc"
|
host_cc="cc"
|
||||||
audio_win_int=""
|
audio_win_int=""
|
||||||
libs_qga=""
|
libs_qga=""
|
||||||
|
@ -1016,6 +1017,10 @@ for opt do
|
||||||
;;
|
;;
|
||||||
--block-drv-ro-whitelist=*) block_drv_ro_whitelist=$(echo "$optarg" | sed -e 's/,/ /g')
|
--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"
|
--enable-debug-tcg) debug_tcg="yes"
|
||||||
;;
|
;;
|
||||||
--disable-debug-tcg) debug_tcg="no"
|
--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-whitelist=L Same as --block-drv-rw-whitelist=L
|
||||||
--block-drv-rw-whitelist=L
|
--block-drv-rw-whitelist=L
|
||||||
set block driver read-write whitelist
|
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
|
--block-drv-ro-whitelist=L
|
||||||
set block driver read-only whitelist
|
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
|
--enable-trace-backends=B Set trace backend
|
||||||
Available backends: $trace_backend_list
|
Available backends: $trace_backend_list
|
||||||
--with-trace-file=NAME Full PATH,NAME of file to store traces
|
--with-trace-file=NAME Full PATH,NAME of file to store traces
|
||||||
|
@ -4583,6 +4590,9 @@ if test "$audio_win_int" = "yes" ; then
|
||||||
fi
|
fi
|
||||||
echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak
|
echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak
|
||||||
echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_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
|
if test "$xfs" = "yes" ; then
|
||||||
echo "CONFIG_XFS=y" >> $config_host_mak
|
echo "CONFIG_XFS=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -2996,6 +2996,7 @@ summary_info += {'coroutine pool': config_host['CONFIG_COROUTINE_POOL'] == '1
|
||||||
if have_block
|
if have_block
|
||||||
summary_info += {'Block whitelist (rw)': config_host['CONFIG_BDRV_RW_WHITELIST']}
|
summary_info += {'Block whitelist (rw)': config_host['CONFIG_BDRV_RW_WHITELIST']}
|
||||||
summary_info += {'Block whitelist (ro)': config_host['CONFIG_BDRV_RO_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 += {'VirtFS support': have_virtfs}
|
||||||
summary_info += {'build virtiofs daemon': have_virtiofsd}
|
summary_info += {'build virtiofs daemon': have_virtiofsd}
|
||||||
summary_info += {'Live block migration': config_host.has_key('CONFIG_LIVE_BLOCK_MIGRATION')}
|
summary_info += {'Live block migration': config_host.has_key('CONFIG_LIVE_BLOCK_MIGRATION')}
|
||||||
|
|
|
@ -38,8 +38,9 @@ class TestActiveMirror(iotests.QMPTestCase):
|
||||||
'if': 'none',
|
'if': 'none',
|
||||||
'node-name': 'source-node',
|
'node-name': 'source-node',
|
||||||
'driver': iotests.imgfmt,
|
'driver': iotests.imgfmt,
|
||||||
'file': {'driver': 'file',
|
'file': {'driver': 'blkdebug',
|
||||||
'filename': source_img}}
|
'image': {'driver': 'file',
|
||||||
|
'filename': source_img}}}
|
||||||
|
|
||||||
blk_target = {'node-name': 'target-node',
|
blk_target = {'node-name': 'target-node',
|
||||||
'driver': iotests.imgfmt,
|
'driver': iotests.imgfmt,
|
||||||
|
@ -141,6 +142,55 @@ class TestActiveMirror(iotests.QMPTestCase):
|
||||||
|
|
||||||
self.potential_writes_in_flight = False
|
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__':
|
if __name__ == '__main__':
|
||||||
iotests.main(supported_fmts=['qcow2', 'raw'],
|
iotests.main(supported_fmts=['qcow2', 'raw'],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
...
|
....
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 3 tests
|
Ran 4 tests
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
|
@ -41,9 +41,11 @@ with iotests.FilePath('image') as img, \
|
||||||
iotests.log('=== Launch VM ===')
|
iotests.log('=== Launch VM ===')
|
||||||
|
|
||||||
vm.add_object('iothread,id=iothread0')
|
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'file,filename={img},node-name=file')
|
||||||
vm.add_blockdev(f'{iotests.imgfmt},file=file,node-name=fmt')
|
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('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.add_device(f'id=scsi0,driver=virtio-scsi,iothread=iothread0')
|
||||||
vm.launch()
|
vm.launch()
|
||||||
|
|
||||||
|
@ -74,6 +76,19 @@ with iotests.FilePath('image') as img, \
|
||||||
vm.qmp_log('query-block-exports')
|
vm.qmp_log('query-block-exports')
|
||||||
iotests.qemu_nbd_list_log('-k', socket)
|
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 ===')
|
iotests.log('\n=== Add a writable export ===')
|
||||||
|
|
||||||
# This fails because share-rw=off
|
# This fails because share-rw=off
|
||||||
|
|
|
@ -51,6 +51,14 @@ exports available: 1
|
||||||
base:allocation
|
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 ===
|
=== 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}}
|
{"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)."}}
|
{"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 New Issue