From: Paolo Bonzini <pbonzini@redhat.com>
To: Ian Main <imain@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH V1 1/2] Implement sync modes for drive-backup.
Date: Mon, 01 Jul 2013 14:16:12 +0200 [thread overview]
Message-ID: <51D1730C.6030902@redhat.com> (raw)
In-Reply-To: <1372386525-23155-2-git-send-email-imain@redhat.com>
Il 28/06/2013 04:28, Ian Main ha scritto:
> This patch adds sync-modes to the drive-backup interface and
> implements the FULL, NONE and TOP modes of synchronization.
>
> FULL performs as before copying the entire contents of the drive
> while preserving the point-in-time using CoW.
> NONE only copies new writes to the target drive.
> TOP copies changes to the topmost drive image and preserves the
> point-in-time using CoW.
>
> Signed-off-by: Ian Main <imain@redhat.com>
> ---
> block/backup.c | 90 +++++++++++++++++++++++++++++++----------------
> blockdev.c | 25 ++++++++-----
> include/block/block_int.h | 4 ++-
> qapi-schema.json | 4 +++
> qmp-commands.hx | 1 +
> 5 files changed, 85 insertions(+), 39 deletions(-)
>
> diff --git a/block/backup.c b/block/backup.c
> index 16105d4..9bad85f 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -37,6 +37,7 @@ typedef struct CowRequest {
> typedef struct BackupBlockJob {
> BlockJob common;
> BlockDriverState *target;
> + MirrorSyncMode sync_mode;
> RateLimit limit;
> BlockdevOnError on_source_error;
> BlockdevOnError on_target_error;
> @@ -247,40 +248,68 @@ static void coroutine_fn backup_run(void *opaque)
>
> bdrv_add_before_write_notifier(bs, &before_write);
>
> - for (; start < end; start++) {
> - bool error_is_read;
> -
> - if (block_job_is_cancelled(&job->common)) {
> - break;
> + if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
> + while (!block_job_is_cancelled(&job->common)) {
> + /* Sleep for 100ms (SLICE_TIME). Our only goal here is to
> + * catch when the job is cancelled. Otherwise we just let
> + * our before_write notify callback service CoW requests. */
> + block_job_sleep_ns(&job->common, rt_clock, SLICE_TIME);
I think there is no need to poll here. You can just do
qemu_coroutine_yield().
> }
> + } else {
> + /* Both FULL and TOP SYNC_MODE's require copying.. */
> + for (; start < end; start++) {
> + bool error_is_read;
>
> - /* we need to yield so that qemu_aio_flush() returns.
> - * (without, VM does not reboot)
> - */
> - if (job->common.speed) {
> - uint64_t delay_ns = ratelimit_calculate_delay(
> - &job->limit, job->sectors_read);
> - job->sectors_read = 0;
> - block_job_sleep_ns(&job->common, rt_clock, delay_ns);
> - } else {
> - block_job_sleep_ns(&job->common, rt_clock, 0);
> - }
> + if (block_job_is_cancelled(&job->common)) {
> + break;
> + }
>
> - if (block_job_is_cancelled(&job->common)) {
> - break;
> - }
> + /* we need to yield so that qemu_aio_flush() returns.
> + * (without, VM does not reboot)
> + */
> + if (job->common.speed) {
> + uint64_t delay_ns = ratelimit_calculate_delay(
> + &job->limit, job->sectors_read);
> + job->sectors_read = 0;
> + block_job_sleep_ns(&job->common, rt_clock, delay_ns);
> + } else {
> + block_job_sleep_ns(&job->common, rt_clock, 0);
> + }
>
> - ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
> - BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
> - if (ret < 0) {
> - /* Depending on error action, fail now or retry cluster */
> - BlockErrorAction action =
> - backup_error_action(job, error_is_read, -ret);
> - if (action == BDRV_ACTION_REPORT) {
> + if (block_job_is_cancelled(&job->common)) {
> break;
> - } else {
> - start--;
> - continue;
> + }
> +
> + if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
> + int n, alloced;
> +
> + /* Check to see if these blocks are already in the backing file. */
> +
> + alloced =
> + bdrv_co_is_allocated_above(bs, bs->backing_hd,
> + start * BACKUP_SECTORS_PER_CLUSTER,
> + BACKUP_SECTORS_PER_CLUSTER, &n);
You can just use bdrv_co_is_allocated, instead of
bdrv_co_is_allocated-above.
> + /* The above call returns true if the given sector is in the
> + * topmost image. If that is the case then we must copy it as
> + * it has been modified from the original backing file.
> + * Otherwise we skip it. */
> + if (alloced == 0) {
> + continue;
> + }
> + }
> + /* FULL sync mode we copy the whole drive. */
> + ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
> + BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
> + if (ret < 0) {
> + /* Depending on error action, fail now or retry cluster */
> + BlockErrorAction action =
> + backup_error_action(job, error_is_read, -ret);
> + if (action == BDRV_ACTION_REPORT) {
> + break;
> + } else {
> + start--;
> + continue;
> + }
> }
> }
> }
> @@ -300,7 +329,7 @@ static void coroutine_fn backup_run(void *opaque)
> }
>
> void backup_start(BlockDriverState *bs, BlockDriverState *target,
> - int64_t speed,
> + int64_t speed, MirrorSyncMode sync_mode,
> BlockdevOnError on_source_error,
> BlockdevOnError on_target_error,
> BlockDriverCompletionFunc *cb, void *opaque,
> @@ -335,6 +364,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
> job->on_source_error = on_source_error;
> job->on_target_error = on_target_error;
> job->target = target;
> + job->sync_mode = sync_mode;
> job->common.len = len;
> job->common.co = qemu_coroutine_create(backup_run);
> qemu_coroutine_enter(job->common.co, job);
> diff --git a/blockdev.c b/blockdev.c
> index c5abd65..000dea6 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -1430,17 +1430,13 @@ void qmp_drive_backup(const char *device, const char *target,
> Error **errp)
> {
> BlockDriverState *bs;
> - BlockDriverState *target_bs;
> + BlockDriverState *target_bs, *source;
> BlockDriver *drv = NULL;
> Error *local_err = NULL;
> int flags;
> int64_t size;
> int ret;
>
> - if (sync != MIRROR_SYNC_MODE_FULL) {
> - error_setg(errp, "only sync mode 'full' is currently supported");
> - return;
> - }
> if (!has_speed) {
> speed = 0;
> }
> @@ -1483,6 +1479,13 @@ void qmp_drive_backup(const char *device, const char *target,
>
> flags = bs->open_flags | BDRV_O_RDWR;
>
> + /* See if we have a backing HD we can use to create our new image
> + * on top of. */
> + source = bs->backing_hd;
Should the source be "bs" for MIRROR_SYNC_MODE_NONE? Also in this case
you may want to default the format to "qcow2" instead of bs's format.
Paolo
> + if (!source && sync == MIRROR_SYNC_MODE_TOP) {
> + sync = MIRROR_SYNC_MODE_FULL;
> + }
> +
> size = bdrv_getlength(bs);
> if (size < 0) {
> error_setg_errno(errp, -size, "bdrv_getlength failed");
> @@ -1491,8 +1494,14 @@ void qmp_drive_backup(const char *device, const char *target,
>
> if (mode != NEW_IMAGE_MODE_EXISTING) {
> assert(format && drv);
> - bdrv_img_create(target, format,
> - NULL, NULL, NULL, size, flags, &local_err, false);
> + if (sync == MIRROR_SYNC_MODE_TOP) {
> + bdrv_img_create(target, format, source->filename,
> + source->drv->format_name, NULL,
> + size, flags, &local_err, false);
> + } else {
> + bdrv_img_create(target, format, NULL, NULL, NULL,
> + size, flags, &local_err, false);
> + }
> }
>
> if (error_is_set(&local_err)) {
> @@ -1508,7 +1517,7 @@ void qmp_drive_backup(const char *device, const char *target,
> return;
> }
>
> - backup_start(bs, target_bs, speed, on_source_error, on_target_error,
> + backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
> block_job_cb, bs, &local_err);
> if (local_err != NULL) {
> bdrv_delete(target_bs);
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index c6ac871..e45f2a0 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -404,6 +404,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
> * @bs: Block device to operate on.
> * @target: Block device to write to.
> * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
> + * @sync_mode: What parts of the disk image should be copied to the destination.
> * @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.
> * @cb: Completion function for the job.
> @@ -413,7 +414,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
> * until the job is cancelled or manually completed.
> */
> void backup_start(BlockDriverState *bs, BlockDriverState *target,
> - int64_t speed, BlockdevOnError on_source_error,
> + int64_t speed, MirrorSyncMode sync_mode,
> + BlockdevOnError on_source_error,
> BlockdevOnError on_target_error,
> BlockDriverCompletionFunc *cb, void *opaque,
> Error **errp);
> diff --git a/qapi-schema.json b/qapi-schema.json
> index cdd2d6b..b3f6c2a 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1807,6 +1807,10 @@
> # @format: #optional the format of the new destination, default is to
> # probe if @mode is 'existing', else the format of the source
> #
> +# @sync: what parts of the disk image should be copied to the destination
> +# (all the disk, only the sectors allocated in the topmost image, or
> +# only new I/O).
> +#
> # @mode: #optional whether and how QEMU should create a new image, default is
> # 'absolute-paths'.
> #
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index e075df4..f3f6b3d 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -957,6 +957,7 @@ Arguments:
>
> Example:
> -> { "execute": "drive-backup", "arguments": { "device": "drive0",
> + "sync": "full",
> "target": "backup.img" } }
> <- { "return": {} }
> EQMP
>
next prev parent reply other threads:[~2013-07-01 12:16 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-06-28 2:28 [Qemu-devel] [PATCH V1 0/2] Implement sync modes for drive-backup Ian Main
2013-06-28 2:28 ` [Qemu-devel] [PATCH V1 1/2] " Ian Main
2013-07-01 12:16 ` Paolo Bonzini [this message]
2013-07-03 18:14 ` Ian Main
2013-07-04 7:45 ` Paolo Bonzini
2013-07-08 9:21 ` Fam Zheng
2013-07-15 10:50 ` Paolo Bonzini
2013-07-15 17:49 ` Ian Main
2013-07-15 20:47 ` Paolo Bonzini
2013-07-16 18:17 ` Ian Main
2013-06-28 2:28 ` [Qemu-devel] [PATCH V1 2/2] Add tests for sync modes 'TOP' and 'NONE' Ian Main
2013-07-04 13:20 ` Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=51D1730C.6030902@redhat.com \
--to=pbonzini@redhat.com \
--cc=imain@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).