mirror: Error out when a BDS would get two BBs

bdrv_replace_in_backing_chain() asserts that not both old and new
BlockDdriverState have a BlockBackend attached to them because both
would have to end up pointing to the new BDS and we don't support more
than one BB per BDS yet.

Before we can safely allow references to existing nodes as backing
files, we need to make sure that even if a backing file has a BB on it,
this doesn't crash qemu.

There are probably also some cases with the 'replaces' option set where
drive-mirror could fail this assertion today. They are fixed with this
error check as well.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Kevin Wolf 2015-10-28 13:24:26 +01:00
parent cddff5bae1
commit 40365552c2

View File

@ -18,6 +18,7 @@
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h" #include "qemu/ratelimit.h"
#include "qemu/bitmap.h" #include "qemu/bitmap.h"
#include "qemu/error-report.h"
#define SLICE_TIME 100000000ULL /* ns */ #define SLICE_TIME 100000000ULL /* ns */
#define MAX_IN_FLIGHT 16 #define MAX_IN_FLIGHT 16
@ -370,11 +371,22 @@ static void mirror_exit(BlockJob *job, void *opaque)
if (s->to_replace) { if (s->to_replace) {
to_replace = s->to_replace; to_replace = s->to_replace;
} }
/* This was checked in mirror_start_job(), but meanwhile one of the
* nodes could have been newly attached to a BlockBackend. */
if (to_replace->blk && s->target->blk) {
error_report("block job: Can't create node with two BlockBackends");
data->ret = -EINVAL;
goto out;
}
if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) { if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL); bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
} }
bdrv_replace_in_backing_chain(to_replace, s->target); bdrv_replace_in_backing_chain(to_replace, s->target);
} }
out:
if (s->to_replace) { if (s->to_replace) {
bdrv_op_unblock_all(s->to_replace, s->replace_blocker); bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
error_free(s->replace_blocker); error_free(s->replace_blocker);
@ -705,6 +717,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
bool is_none_mode, BlockDriverState *base) bool is_none_mode, BlockDriverState *base)
{ {
MirrorBlockJob *s; MirrorBlockJob *s;
BlockDriverState *replaced_bs;
if (granularity == 0) { if (granularity == 0) {
granularity = bdrv_get_default_bitmap_granularity(target); granularity = bdrv_get_default_bitmap_granularity(target);
@ -728,6 +741,21 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
buf_size = DEFAULT_MIRROR_BUF_SIZE; buf_size = DEFAULT_MIRROR_BUF_SIZE;
} }
/* We can't support this case as long as the block layer can't handle
* multiple BlockBackends per BlockDriverState. */
if (replaces) {
replaced_bs = bdrv_lookup_bs(replaces, replaces, errp);
if (replaced_bs == NULL) {
return;
}
} else {
replaced_bs = bs;
}
if (replaced_bs->blk && target->blk) {
error_setg(errp, "Can't create node with two BlockBackends");
return;
}
s = block_job_create(driver, bs, speed, cb, opaque, errp); s = block_job_create(driver, bs, speed, cb, opaque, errp);
if (!s) { if (!s) {
return; return;