Block patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQEcBAABCAAGBQJXYqffAAoJEDuxQgLoOKytiuoH+wWkxRsuRvuNZf2feQOyyznd
 XJdycJKNnJp5PscryaHqJzc1tAapEKDE257URYkXI+hF7Vue1r6jNrfgfR6AXysK
 gVfJ0BbELYWly7ID04Q8C9P1RUmEjbYqQRnB7nua33wq9P/92RIR373p/kGVJBix
 RM4e+xYfvGYOgNODF9jJKw4R5Sw2ZVmchWlwjcYjyRW8gOiS8OaFwX7FIB3+kj+P
 ew4hsZkZmK8uroMmfC3Oe5iZfvLXzKBaMT89XiL6lUXhDizYvSkPOJoIyLrfeQ3e
 5AAv0AnQhrSfG2YNjOA3SsFiIIUEjLf8jr05Cr0YLXqr4OHk3Zoc7vsKDnY3ai8=
 =QRX6
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'mreitz/tags/pull-block-for-kevin-2016-06-16' into queue-block

Block patches

# gpg: Signature made Thu Jun 16 15:21:35 2016 CEST
# gpg:                using RSA key 0x3BB14202E838ACAD
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>"
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1  1829 F407 DB00 61D5 CF40
#      Subkey fingerprint: 58B3 81CE 2DC8 9CF9 9730  EE64 3BB1 4202 E838 ACAD

* mreitz/tags/pull-block-for-kevin-2016-06-16:
  hbitmap: add 'pos < size' asserts
  iotests: Add test for oVirt-like storage migration
  iotests: Add test for post-mirror backing chains
  block/null: Implement bdrv_refresh_filename()
  block/mirror: Fix target backing BDS
  block: Allow replacement of a BDS by its overlay
  rbd:change error_setg() to error_setg_errno()
  iotests: 095: Clean up QEMU before showing image info
  block: Create the commit block job before reopening any image
  block: Prevent sleeping jobs from resuming if they have been paused
  block: use the block job list in qmp_query_block_jobs()
  block: use the block job list in bdrv_drain_all()

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Kevin Wolf 2016-06-16 15:22:18 +02:00
commit 60251f4d3e
16 changed files with 646 additions and 64 deletions

24
block.c
View file

@ -2226,9 +2226,23 @@ void bdrv_close_all(void)
static void change_parent_backing_link(BlockDriverState *from, static void change_parent_backing_link(BlockDriverState *from,
BlockDriverState *to) BlockDriverState *to)
{ {
BdrvChild *c, *next; BdrvChild *c, *next, *to_c;
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
if (c->role == &child_backing) {
/* @from is generally not allowed to be a backing file, except for
* when @to is the overlay. In that case, @from may not be replaced
* by @to as @to's backing node. */
QLIST_FOREACH(to_c, &to->children, next) {
if (to_c == c) {
break;
}
}
if (to_c) {
continue;
}
}
assert(c->role != &child_backing); assert(c->role != &child_backing);
bdrv_ref(to); bdrv_ref(to);
bdrv_replace_child(c, to); bdrv_replace_child(c, to);
@ -2277,14 +2291,6 @@ void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
change_parent_backing_link(old, new); change_parent_backing_link(old, new);
/* Change backing files if a previously independent node is added to the
* chain. For active commit, we replace top by its own (indirect) backing
* file and don't do anything here so we don't build a loop. */
if (new->backing == NULL && !bdrv_chain_contains(backing_bs(old), new)) {
bdrv_set_backing_hd(new, backing_bs(old));
bdrv_set_backing_hd(old, NULL);
}
bdrv_unref(old); bdrv_unref(old);
} }

View file

@ -236,6 +236,11 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
return; return;
} }
s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
orig_base_flags = bdrv_get_flags(base); orig_base_flags = bdrv_get_flags(base);
orig_overlay_flags = bdrv_get_flags(overlay_bs); orig_overlay_flags = bdrv_get_flags(overlay_bs);
@ -252,16 +257,12 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
bdrv_reopen_multiple(reopen_queue, &local_err); bdrv_reopen_multiple(reopen_queue, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
block_job_unref(&s->common);
return; return;
} }
} }
s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
s->base = blk_new(); s->base = blk_new();
blk_insert_bs(s->base, base); blk_insert_bs(s->base, base);

View file

@ -289,15 +289,21 @@ void bdrv_drain_all(void)
bool busy = true; bool busy = true;
BlockDriverState *bs; BlockDriverState *bs;
BdrvNextIterator it; BdrvNextIterator it;
BlockJob *job = NULL;
GSList *aio_ctxs = NULL, *ctx; GSList *aio_ctxs = NULL, *ctx;
while ((job = block_job_next(job))) {
AioContext *aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context);
block_job_pause(job);
aio_context_release(aio_context);
}
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs); AioContext *aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
if (bs->job) {
block_job_pause(bs->job);
}
bdrv_parent_drained_begin(bs); bdrv_parent_drained_begin(bs);
bdrv_io_unplugged_begin(bs); bdrv_io_unplugged_begin(bs);
bdrv_drain_recurse(bs); bdrv_drain_recurse(bs);
@ -340,12 +346,18 @@ void bdrv_drain_all(void)
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
bdrv_io_unplugged_end(bs); bdrv_io_unplugged_end(bs);
bdrv_parent_drained_end(bs); bdrv_parent_drained_end(bs);
if (bs->job) {
block_job_resume(bs->job);
}
aio_context_release(aio_context); aio_context_release(aio_context);
} }
g_slist_free(aio_ctxs); g_slist_free(aio_ctxs);
job = NULL;
while ((job = block_job_next(job))) {
AioContext *aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context);
block_job_resume(job);
aio_context_release(aio_context);
}
} }
/** /**

View file

@ -44,6 +44,7 @@ typedef struct MirrorBlockJob {
/* Used to block operations on the drive-mirror-replace target */ /* Used to block operations on the drive-mirror-replace target */
Error *replace_blocker; Error *replace_blocker;
bool is_none_mode; bool is_none_mode;
BlockMirrorBackingMode backing_mode;
BlockdevOnError on_source_error, on_target_error; BlockdevOnError on_source_error, on_target_error;
bool synced; bool synced;
bool should_complete; bool should_complete;
@ -742,20 +743,26 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
static void mirror_complete(BlockJob *job, Error **errp) static void mirror_complete(BlockJob *job, Error **errp)
{ {
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
Error *local_err = NULL; BlockDriverState *src, *target;
int ret;
src = blk_bs(job->blk);
target = blk_bs(s->target);
ret = bdrv_open_backing_file(blk_bs(s->target), NULL, "backing",
&local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return;
}
if (!s->synced) { if (!s->synced) {
error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id); error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
return; return;
} }
if (s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) {
int ret;
assert(!target->backing);
ret = bdrv_open_backing_file(target, NULL, "backing", errp);
if (ret < 0) {
return;
}
}
/* check the target bs is not blocked and block all operations on it */ /* check the target bs is not blocked and block all operations on it */
if (s->replaces) { if (s->replaces) {
AioContext *replace_aio_context; AioContext *replace_aio_context;
@ -777,6 +784,13 @@ static void mirror_complete(BlockJob *job, Error **errp)
aio_context_release(replace_aio_context); aio_context_release(replace_aio_context);
} }
if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
BlockDriverState *backing = s->is_none_mode ? src : s->base;
if (backing_bs(target) != backing) {
bdrv_set_backing_hd(target, backing);
}
}
s->should_complete = true; s->should_complete = true;
block_job_enter(&s->common); block_job_enter(&s->common);
} }
@ -799,6 +813,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
const char *replaces, const char *replaces,
int64_t speed, uint32_t granularity, int64_t speed, uint32_t granularity,
int64_t buf_size, int64_t buf_size,
BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap, bool unmap,
@ -836,6 +851,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
s->on_source_error = on_source_error; s->on_source_error = on_source_error;
s->on_target_error = on_target_error; s->on_target_error = on_target_error;
s->is_none_mode = is_none_mode; s->is_none_mode = is_none_mode;
s->backing_mode = backing_mode;
s->base = base; s->base = base;
s->granularity = granularity; s->granularity = granularity;
s->buf_size = ROUND_UP(buf_size, granularity); s->buf_size = ROUND_UP(buf_size, granularity);
@ -859,7 +875,8 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
void mirror_start(BlockDriverState *bs, BlockDriverState *target, void mirror_start(BlockDriverState *bs, BlockDriverState *target,
const char *replaces, const char *replaces,
int64_t speed, uint32_t granularity, int64_t buf_size, int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap, bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
@ -875,7 +892,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
is_none_mode = mode == MIRROR_SYNC_MODE_NONE; is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
mirror_start_job(bs, target, replaces, mirror_start_job(bs, target, replaces,
speed, granularity, buf_size, speed, granularity, buf_size, backing_mode,
on_source_error, on_target_error, unmap, cb, opaque, errp, on_source_error, on_target_error, unmap, cb, opaque, errp,
&mirror_job_driver, is_none_mode, base); &mirror_job_driver, is_none_mode, base);
} }
@ -922,7 +939,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
} }
} }
mirror_start_job(bs, base, NULL, speed, 0, 0, mirror_start_job(bs, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN,
on_error, on_error, false, cb, opaque, &local_err, on_error, on_error, false, cb, opaque, &local_err,
&commit_active_job_driver, false, base); &commit_active_job_driver, false, base);
if (local_err) { if (local_err) {

View file

@ -12,6 +12,8 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "block/block_int.h" #include "block/block_int.h"
#define NULL_OPT_LATENCY "latency-ns" #define NULL_OPT_LATENCY "latency-ns"
@ -223,6 +225,20 @@ static int64_t coroutine_fn null_co_get_block_status(BlockDriverState *bs,
} }
} }
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
{
QINCREF(opts);
qdict_del(opts, "filename");
if (!qdict_size(opts)) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
bs->drv->format_name);
}
qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
bs->full_open_options = opts;
}
static BlockDriver bdrv_null_co = { static BlockDriver bdrv_null_co = {
.format_name = "null-co", .format_name = "null-co",
.protocol_name = "null-co", .protocol_name = "null-co",
@ -238,6 +254,8 @@ static BlockDriver bdrv_null_co = {
.bdrv_reopen_prepare = null_reopen_prepare, .bdrv_reopen_prepare = null_reopen_prepare,
.bdrv_co_get_block_status = null_co_get_block_status, .bdrv_co_get_block_status = null_co_get_block_status,
.bdrv_refresh_filename = null_refresh_filename,
}; };
static BlockDriver bdrv_null_aio = { static BlockDriver bdrv_null_aio = {
@ -255,6 +273,8 @@ static BlockDriver bdrv_null_aio = {
.bdrv_reopen_prepare = null_reopen_prepare, .bdrv_reopen_prepare = null_reopen_prepare,
.bdrv_co_get_block_status = null_co_get_block_status, .bdrv_co_get_block_status = null_co_get_block_status,
.bdrv_refresh_filename = null_refresh_filename,
}; };
static void bdrv_null_init(void) static void bdrv_null_init(void)

View file

@ -290,7 +290,8 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
if (only_read_conf_file) { if (only_read_conf_file) {
ret = rados_conf_read_file(cluster, value); ret = rados_conf_read_file(cluster, value);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "error reading conf file %s", value); error_setg_errno(errp, -ret, "error reading conf file %s",
value);
break; break;
} }
} }
@ -299,7 +300,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
} else if (!only_read_conf_file) { } else if (!only_read_conf_file) {
ret = rados_conf_set(cluster, name, value); ret = rados_conf_set(cluster, name, value);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "invalid conf option %s", name); error_setg_errno(errp, -ret, "invalid conf option %s", name);
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
@ -354,9 +355,10 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
} }
clientname = qemu_rbd_parse_clientname(conf, clientname_buf); clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
if (rados_create(&cluster, clientname) < 0) { ret = rados_create(&cluster, clientname);
error_setg(errp, "error initializing"); if (ret < 0) {
return -EIO; error_setg_errno(errp, -ret, "error initializing");
return ret;
} }
if (strstr(conf, "conf=") == NULL) { if (strstr(conf, "conf=") == NULL) {
@ -381,21 +383,27 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
return -EIO; return -EIO;
} }
if (rados_connect(cluster) < 0) { ret = rados_connect(cluster);
error_setg(errp, "error connecting"); if (ret < 0) {
error_setg_errno(errp, -ret, "error connecting");
rados_shutdown(cluster); rados_shutdown(cluster);
return -EIO; return ret;
} }
if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) { ret = rados_ioctx_create(cluster, pool, &io_ctx);
error_setg(errp, "error opening pool %s", pool); if (ret < 0) {
error_setg_errno(errp, -ret, "error opening pool %s", pool);
rados_shutdown(cluster); rados_shutdown(cluster);
return -EIO; return ret;
} }
ret = rbd_create(io_ctx, name, bytes, &obj_order); ret = rbd_create(io_ctx, name, bytes, &obj_order);
rados_ioctx_destroy(io_ctx); rados_ioctx_destroy(io_ctx);
rados_shutdown(cluster); rados_shutdown(cluster);
if (ret < 0) {
error_setg_errno(errp, -ret, "error rbd create");
return ret;
}
return ret; return ret;
} }
@ -500,7 +508,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
clientname = qemu_rbd_parse_clientname(conf, clientname_buf); clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
r = rados_create(&s->cluster, clientname); r = rados_create(&s->cluster, clientname);
if (r < 0) { if (r < 0) {
error_setg(errp, "error initializing"); error_setg_errno(errp, -r, "error initializing");
goto failed_opts; goto failed_opts;
} }
@ -546,19 +554,19 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
r = rados_connect(s->cluster); r = rados_connect(s->cluster);
if (r < 0) { if (r < 0) {
error_setg(errp, "error connecting"); error_setg_errno(errp, -r, "error connecting");
goto failed_shutdown; goto failed_shutdown;
} }
r = rados_ioctx_create(s->cluster, pool, &s->io_ctx); r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
if (r < 0) { if (r < 0) {
error_setg(errp, "error opening pool %s", pool); error_setg_errno(errp, -r, "error opening pool %s", pool);
goto failed_shutdown; goto failed_shutdown;
} }
r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
if (r < 0) { if (r < 0) {
error_setg(errp, "error reading header from %s", s->name); error_setg_errno(errp, -r, "error reading header from %s", s->name);
goto failed_open; goto failed_open;
} }

View file

@ -3426,6 +3426,7 @@ static void blockdev_mirror_common(BlockDriverState *bs,
BlockDriverState *target, BlockDriverState *target,
bool has_replaces, const char *replaces, bool has_replaces, const char *replaces,
enum MirrorSyncMode sync, enum MirrorSyncMode sync,
BlockMirrorBackingMode backing_mode,
bool has_speed, int64_t speed, bool has_speed, int64_t speed,
bool has_granularity, uint32_t granularity, bool has_granularity, uint32_t granularity,
bool has_buf_size, int64_t buf_size, bool has_buf_size, int64_t buf_size,
@ -3483,7 +3484,7 @@ static void blockdev_mirror_common(BlockDriverState *bs,
*/ */
mirror_start(bs, target, mirror_start(bs, target,
has_replaces ? replaces : NULL, has_replaces ? replaces : NULL,
speed, granularity, buf_size, sync, speed, granularity, buf_size, sync, backing_mode,
on_source_error, on_target_error, unmap, on_source_error, on_target_error, unmap,
block_job_cb, bs, errp); block_job_cb, bs, errp);
} }
@ -3506,6 +3507,7 @@ void qmp_drive_mirror(const char *device, const char *target,
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *source, *target_bs; BlockDriverState *source, *target_bs;
AioContext *aio_context; AioContext *aio_context;
BlockMirrorBackingMode backing_mode;
Error *local_err = NULL; Error *local_err = NULL;
QDict *options = NULL; QDict *options = NULL;
int flags; int flags;
@ -3579,6 +3581,12 @@ void qmp_drive_mirror(const char *device, const char *target,
} }
} }
if (mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
} else {
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
}
if ((sync == MIRROR_SYNC_MODE_FULL || !source) if ((sync == MIRROR_SYNC_MODE_FULL || !source)
&& mode != NEW_IMAGE_MODE_EXISTING) && mode != NEW_IMAGE_MODE_EXISTING)
{ {
@ -3627,7 +3635,7 @@ void qmp_drive_mirror(const char *device, const char *target,
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
blockdev_mirror_common(bs, target_bs, blockdev_mirror_common(bs, target_bs,
has_replaces, replaces, sync, has_replaces, replaces, sync, backing_mode,
has_speed, speed, has_speed, speed,
has_granularity, granularity, has_granularity, granularity,
has_buf_size, buf_size, has_buf_size, buf_size,
@ -3659,6 +3667,7 @@ void qmp_blockdev_mirror(const char *device, const char *target,
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *target_bs; BlockDriverState *target_bs;
AioContext *aio_context; AioContext *aio_context;
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
Error *local_err = NULL; Error *local_err = NULL;
blk = blk_by_name(device); blk = blk_by_name(device);
@ -3684,7 +3693,7 @@ void qmp_blockdev_mirror(const char *device, const char *target,
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
blockdev_mirror_common(bs, target_bs, blockdev_mirror_common(bs, target_bs,
has_replaces, replaces, sync, has_replaces, replaces, sync, backing_mode,
has_speed, speed, has_speed, speed,
has_granularity, granularity, has_granularity, granularity,
has_buf_size, buf_size, has_buf_size, buf_size,
@ -4157,22 +4166,18 @@ void qmp_x_blockdev_change(const char *parent, bool has_child,
BlockJobInfoList *qmp_query_block_jobs(Error **errp) BlockJobInfoList *qmp_query_block_jobs(Error **errp)
{ {
BlockJobInfoList *head = NULL, **p_next = &head; BlockJobInfoList *head = NULL, **p_next = &head;
BlockDriverState *bs; BlockJob *job;
BdrvNextIterator it;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (job = block_job_next(NULL); job; job = block_job_next(job)) {
AioContext *aio_context = bdrv_get_aio_context(bs); BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
AioContext *aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
elem->value = block_job_query(job);
if (bs->job) {
BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
elem->value = block_job_query(bs->job);
*p_next = elem;
p_next = &elem->next;
}
aio_context_release(aio_context); aio_context_release(aio_context);
*p_next = elem;
p_next = &elem->next;
} }
return head; return head;

View file

@ -361,10 +361,12 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
} }
job->busy = false; job->busy = false;
if (!block_job_is_paused(job)) {
co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
}
/* The job can be paused while sleeping, so check this again */
if (block_job_is_paused(job)) { if (block_job_is_paused(job)) {
qemu_coroutine_yield(); qemu_coroutine_yield();
} else {
co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
} }
job->busy = true; job->busy = true;
} }

View file

@ -509,6 +509,20 @@ struct BlockBackendRootState {
BlockdevDetectZeroesOptions detect_zeroes; BlockdevDetectZeroesOptions detect_zeroes;
}; };
typedef enum BlockMirrorBackingMode {
/* Reuse the existing backing chain from the source for the target.
* - sync=full: Set backing BDS to NULL.
* - sync=top: Use source's backing BDS.
* - sync=none: Use source as the backing BDS. */
MIRROR_SOURCE_BACKING_CHAIN,
/* Open the target's backing chain completely anew */
MIRROR_OPEN_BACKING_CHAIN,
/* Do not change the target's backing BDS after job completion */
MIRROR_LEAVE_BACKING_CHAIN,
} BlockMirrorBackingMode;
static inline BlockDriverState *backing_bs(BlockDriverState *bs) static inline BlockDriverState *backing_bs(BlockDriverState *bs)
{ {
return bs->backing ? bs->backing->bs : NULL; return bs->backing ? bs->backing->bs : NULL;
@ -671,6 +685,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
* @granularity: The chosen granularity for the dirty bitmap. * @granularity: The chosen granularity for the dirty bitmap.
* @buf_size: The amount of data that can be in flight at one time. * @buf_size: The amount of data that can be in flight at one time.
* @mode: Whether to collapse all images in the chain to the target. * @mode: Whether to collapse all images in the chain to the target.
* @backing_mode: How to establish the target's backing chain after completion.
* @on_source_error: The action to take upon error reading from the source. * @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target. * @on_target_error: The action to take upon error writing to the target.
* @unmap: Whether to unmap target where source sectors only contain zeroes. * @unmap: Whether to unmap target where source sectors only contain zeroes.
@ -686,7 +701,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
void mirror_start(BlockDriverState *bs, BlockDriverState *target, void mirror_start(BlockDriverState *bs, BlockDriverState *target,
const char *replaces, const char *replaces,
int64_t speed, uint32_t granularity, int64_t buf_size, int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap, bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,

View file

@ -74,6 +74,8 @@ _send_qemu_cmd $h "{ 'execute': 'block-commit',
'arguments': { 'device': 'test', 'arguments': { 'device': 'test',
'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED" 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED"
_cleanup_qemu
echo echo
echo "=== Base image info after commit and resize ===" echo "=== Base image info after commit and resize ==="
TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info

261
tests/qemu-iotests/155 Executable file
View file

@ -0,0 +1,261 @@
#!/usr/bin/env python
#
# Test whether the backing BDSs are correct after completion of a
# mirror block job; in "existing" modes (drive-mirror with
# mode=existing and blockdev-mirror) the backing chain should not be
# overridden.
#
# Copyright (C) 2016 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 qemu_img
back0_img = os.path.join(iotests.test_dir, 'back0.' + iotests.imgfmt)
back1_img = os.path.join(iotests.test_dir, 'back1.' + iotests.imgfmt)
back2_img = os.path.join(iotests.test_dir, 'back2.' + iotests.imgfmt)
source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt)
target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
# Class variables for controlling its behavior:
#
# existing: If True, explicitly create the target image and blockdev-add it
# target_backing: If existing is True: Use this filename as the backing file
# of the target image
# (None: no backing file)
# target_blockdev_backing: If existing is True: Pass this dict as "backing"
# for the blockdev-add command
# (None: do not pass "backing")
# target_real_backing: If existing is True: The real filename of the backing
# image during runtime, only makes sense if
# target_blockdev_backing is not None
# (None: same as target_backing)
class BaseClass(iotests.QMPTestCase):
target_blockdev_backing = None
target_real_backing = None
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1M')
qemu_img('create', '-f', iotests.imgfmt, '-b', back0_img, back1_img)
qemu_img('create', '-f', iotests.imgfmt, '-b', back1_img, back2_img)
qemu_img('create', '-f', iotests.imgfmt, '-b', back2_img, source_img)
self.vm = iotests.VM()
self.vm.add_drive(None, '', 'none')
self.vm.launch()
# Add the BDS via blockdev-add so it stays around after the mirror block
# job has been completed
result = self.vm.qmp('blockdev-add',
options={'node-name': 'source',
'driver': iotests.imgfmt,
'file': {'driver': 'file',
'filename': source_img}})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-insert-medium',
device='drive0', node_name='source')
self.assert_qmp(result, 'return', {})
self.assertIntactSourceBackingChain()
if self.existing:
if self.target_backing:
qemu_img('create', '-f', iotests.imgfmt,
'-b', self.target_backing, target_img, '1M')
else:
qemu_img('create', '-f', iotests.imgfmt, target_img, '1M')
if self.cmd == 'blockdev-mirror':
options = { 'node-name': 'target',
'driver': iotests.imgfmt,
'file': { 'driver': 'file',
'filename': target_img } }
if self.target_blockdev_backing:
options['backing'] = self.target_blockdev_backing
result = self.vm.qmp('blockdev-add', options=options)
self.assert_qmp(result, 'return', {})
def tearDown(self):
self.vm.shutdown()
os.remove(source_img)
os.remove(back2_img)
os.remove(back1_img)
os.remove(back0_img)
try:
os.remove(target_img)
except OSError:
pass
def findBlockNode(self, node_name, id=None):
if id:
result = self.vm.qmp('query-block')
for device in result['return']:
if device['device'] == id:
if node_name:
self.assert_qmp(device, 'inserted/node-name', node_name)
return device['inserted']
else:
result = self.vm.qmp('query-named-block-nodes')
for node in result['return']:
if node['node-name'] == node_name:
return node
self.fail('Cannot find node %s/%s' % (id, node_name))
def assertIntactSourceBackingChain(self):
node = self.findBlockNode('source')
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
source_img)
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',
back2_img)
self.assert_qmp(node, 'image' + '/backing-image' * 2 + '/filename',
back1_img)
self.assert_qmp(node, 'image' + '/backing-image' * 3 + '/filename',
back0_img)
self.assert_qmp_absent(node, 'image' + '/backing-image' * 4)
def assertCorrectBackingImage(self, node, default_image):
if self.existing:
if self.target_real_backing:
image = self.target_real_backing
else:
image = self.target_backing
else:
image = default_image
if image:
self.assert_qmp(node, 'image/backing-image/filename', image)
else:
self.assert_qmp_absent(node, 'image/backing-image')
# Class variables for controlling its behavior:
#
# cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror
class MirrorBaseClass(BaseClass):
def runMirror(self, sync):
if self.cmd == 'blockdev-mirror':
result = self.vm.qmp(self.cmd, device='drive0', sync=sync,
target='target')
else:
if self.existing:
mode = 'existing'
else:
mode = 'absolute-paths'
result = self.vm.qmp(self.cmd, device='drive0', sync=sync,
target=target_img, format=iotests.imgfmt,
mode=mode, node_name='target')
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_READY')
result = self.vm.qmp('block-job-complete', device='drive0')
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_COMPLETED')
def testFull(self):
self.runMirror('full')
node = self.findBlockNode('target', 'drive0')
self.assertCorrectBackingImage(node, None)
self.assertIntactSourceBackingChain()
def testTop(self):
self.runMirror('top')
node = self.findBlockNode('target', 'drive0')
self.assertCorrectBackingImage(node, back2_img)
self.assertIntactSourceBackingChain()
def testNone(self):
self.runMirror('none')
node = self.findBlockNode('target', 'drive0')
self.assertCorrectBackingImage(node, source_img)
self.assertIntactSourceBackingChain()
class TestDriveMirrorAbsolutePaths(MirrorBaseClass):
cmd = 'drive-mirror'
existing = False
class TestDriveMirrorExistingNoBacking(MirrorBaseClass):
cmd = 'drive-mirror'
existing = True
target_backing = None
class TestDriveMirrorExistingBacking(MirrorBaseClass):
cmd = 'drive-mirror'
existing = True
target_backing = 'null-co://'
class TestBlockdevMirrorNoBacking(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = None
class TestBlockdevMirrorBacking(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = 'null-co://'
class TestBlockdevMirrorForcedBacking(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = None
target_blockdev_backing = { 'driver': 'null-co' }
target_real_backing = 'null-co://'
class TestCommit(BaseClass):
existing = False
def testCommit(self):
result = self.vm.qmp('block-commit', device='drive0', base=back1_img)
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_READY')
result = self.vm.qmp('block-job-complete', device='drive0')
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_COMPLETED')
node = self.findBlockNode(None, 'drive0')
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
back1_img)
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',
back0_img)
self.assert_qmp_absent(node, 'image' + '/backing-image' * 2 +
'/filename')
self.assertIntactSourceBackingChain()
BaseClass = None
MirrorBaseClass = None
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'])

View file

@ -0,0 +1,5 @@
...................
----------------------------------------------------------------------
Ran 19 tests
OK

174
tests/qemu-iotests/156 Executable file
View file

@ -0,0 +1,174 @@
#!/bin/bash
#
# Tests oVirt-like storage migration:
# - Create snapshot
# - Create target image with (not yet existing) target backing chain
# (i.e. just write the name of a soon-to-be-copied-over backing file into it)
# - drive-mirror the snapshot to the target with mode=existing and sync=top
# - In the meantime, copy the original source files to the destination via
# conventional means (i.e. outside of qemu)
# - Complete the drive-mirror job
# - Delete all source images
#
# Copyright (C) 2016 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/>.
#
# creator
owner=mreitz@redhat.com
seq="$(basename $0)"
echo "QA output created by $seq"
here="$PWD"
status=1 # failure is the default!
_cleanup()
{
rm -f "$TEST_IMG{,.target}{,.backing,.overlay}"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
. ./common.qemu
_supported_fmt qcow2 qed
_supported_proto generic
_supported_os Linux
# Create source disk
TEST_IMG="$TEST_IMG.backing" _make_test_img 1M
_make_test_img -b "$TEST_IMG.backing" 1M
$QEMU_IO -c 'write -P 1 0 256k' "$TEST_IMG.backing" | _filter_qemu_io
$QEMU_IO -c 'write -P 2 64k 192k' "$TEST_IMG" | _filter_qemu_io
_launch_qemu -drive if=none,id=source,file="$TEST_IMG"
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'qmp_capabilities' }" \
'return'
# Create snapshot
TEST_IMG="$TEST_IMG.overlay" _make_test_img -b "$TEST_IMG" 1M
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'blockdev-snapshot-sync',
'arguments': { 'device': 'source',
'snapshot-file': '$TEST_IMG.overlay',
'format': '$IMGFMT',
'mode': 'existing' } }" \
'return'
# Write something to the snapshot
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"write -P 3 128k 128k\"' } }" \
'return'
# Create target image
TEST_IMG="$TEST_IMG.target.overlay" _make_test_img -b "$TEST_IMG.target" 1M
# Mirror snapshot
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'drive-mirror',
'arguments': { 'device': 'source',
'target': '$TEST_IMG.target.overlay',
'mode': 'existing',
'sync': 'top' } }" \
'return'
# Wait for convergence
_send_qemu_cmd $QEMU_HANDLE \
'' \
'BLOCK_JOB_READY'
# Write some more
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"write -P 4 192k 64k\"' } }" \
'return'
# Copy source backing chain to the target before completing the job
cp "$TEST_IMG.backing" "$TEST_IMG.target.backing"
cp "$TEST_IMG" "$TEST_IMG.target"
$QEMU_IMG rebase -u -b "$TEST_IMG.target.backing" "$TEST_IMG.target"
# Complete block job
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'block-job-complete',
'arguments': { 'device': 'source' } }" \
''
_send_qemu_cmd $QEMU_HANDLE \
'' \
'BLOCK_JOB_COMPLETED'
# Remove the source images
rm -f "$TEST_IMG{,.backing,.overlay}"
echo
# Check online disk contents
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 1 0k 64k\"' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 2 64k 64k\"' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 3 128k 64k\"' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 4 192k 64k\"' } }" \
'return'
echo
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'quit' }" \
'return'
wait=1 _cleanup_qemu
echo
# Check offline disk contents
$QEMU_IO -c 'read -P 1 0k 64k' \
-c 'read -P 2 64k 64k' \
-c 'read -P 3 128k 64k' \
-c 'read -P 4 192k 64k' \
"$TEST_IMG.target.overlay" | _filter_qemu_io
echo
# success, all done
echo '*** done'
rm -f $seq.full
status=0

View file

@ -0,0 +1,48 @@
QA output created by 156
Formatting 'TEST_DIR/t.IMGFMT.backing', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.backing
wrote 262144/262144 bytes at offset 0
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 196608/196608 bytes at offset 65536
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": {}}
Formatting 'TEST_DIR/t.IMGFMT.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT
{"return": {}}
wrote 131072/131072 bytes at offset 131072
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}}
wrote 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}}
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
read 65536/65536 bytes at offset 131072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
read 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 131072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done

View file

@ -154,3 +154,5 @@
150 rw auto quick 150 rw auto quick
152 rw auto quick 152 rw auto quick
154 rw auto backing quick 154 rw auto backing quick
155 rw auto
156 rw auto quick

View file

@ -269,6 +269,7 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count)
start >>= hb->granularity; start >>= hb->granularity;
last >>= hb->granularity; last >>= hb->granularity;
count = last - start + 1; count = last - start + 1;
assert(last < hb->size);
hb->count += count - hb_count_between(hb, start, last); hb->count += count - hb_count_between(hb, start, last);
hb_set_between(hb, HBITMAP_LEVELS - 1, start, last); hb_set_between(hb, HBITMAP_LEVELS - 1, start, last);
@ -348,6 +349,7 @@ void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count)
start >>= hb->granularity; start >>= hb->granularity;
last >>= hb->granularity; last >>= hb->granularity;
assert(last < hb->size);
hb->count -= hb_count_between(hb, start, last); hb->count -= hb_count_between(hb, start, last);
hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last); hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last);
@ -371,6 +373,7 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item)
/* Compute position and bit in the last layer. */ /* Compute position and bit in the last layer. */
uint64_t pos = item >> hb->granularity; uint64_t pos = item >> hb->granularity;
unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1)); unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1));
assert(pos < hb->size);
return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0;
} }