This is simple: instead of clearing the bitmap, just leave the bitmap data intact even in case of success.
Signed-off-by: John Snow <js...@redhat.com> --- block.c | 9 ++++++++- block/backup.c | 17 ++++++++++------- block/mirror.c | 9 +++++++-- include/block/block.h | 1 + qapi/block-core.json | 6 ++++-- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/block.c b/block.c index 5551f79..3e780f9 100644 --- a/block.c +++ b/block.c @@ -3166,7 +3166,9 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) * Requires that the bitmap is not frozen and has no successor. */ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap, Error **errp) + BdrvDirtyBitmap *bitmap, + MirrorSyncMode sync_mode, + Error **errp) { uint64_t granularity; BdrvDirtyBitmap *child; @@ -3191,6 +3193,11 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, /* Install the successor and freeze the parent */ bitmap->successor = child; bitmap->successor_refcount = 1; + + if (sync_mode == MIRROR_SYNC_MODE_DIFFERENTIAL) { + bitmap->act = SUCCESSOR_ACTION_RECLAIM; + } + return 0; } diff --git a/block/backup.c b/block/backup.c index a8f7c43..dd808c2 100644 --- a/block/backup.c +++ b/block/backup.c @@ -390,7 +390,8 @@ static void coroutine_fn backup_run(void *opaque) qemu_coroutine_yield(); job->common.busy = true; } - } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { + } else if ((job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) || + (job->sync_mode == MIRROR_SYNC_MODE_DIFFERENTIAL)) { ret = backup_run_incremental(job); } else { /* Both FULL and TOP SYNC_MODE's require copying.. */ @@ -510,15 +511,18 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, return; } - if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { + if ((sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) || + (sync_mode == MIRROR_SYNC_MODE_DIFFERENTIAL)) { if (!sync_bitmap) { - error_setg(errp, "must provide a valid bitmap name for " - "\"incremental\" sync mode"); + error_setg(errp, + "must provide a valid bitmap name for \"%s\" sync mode", + MirrorSyncMode_lookup[sync_mode]); return; } /* Create a new bitmap, and freeze/disable this one. */ - if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) { + if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, + sync_mode, errp) < 0) { return; } } else if (sync_bitmap) { @@ -548,8 +552,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, job->on_target_error = on_target_error; job->target = target; job->sync_mode = sync_mode; - job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ? - sync_bitmap : NULL; + job->sync_bitmap = sync_bitmap; job->common.len = len; job->common.co = qemu_coroutine_create(backup_run); qemu_coroutine_enter(job->common.co, job); diff --git a/block/mirror.c b/block/mirror.c index adf391c..1cde86b 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -709,9 +709,14 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, bool is_none_mode; BlockDriverState *base; - if (mode == MIRROR_SYNC_MODE_INCREMENTAL) { - error_setg(errp, "Sync mode 'incremental' not supported"); + switch (mode) { + case MIRROR_SYNC_MODE_INCREMENTAL: + case MIRROR_SYNC_MODE_DIFFERENTIAL: + error_setg(errp, "Sync mode \"%s\" not supported", + MirrorSyncMode_lookup[mode]); return; + default: + break; } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; diff --git a/include/block/block.h b/include/block/block.h index e88a332..8169a60 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -462,6 +462,7 @@ BdrvDirtyBitmap *bdrv_copy_dirty_bitmap(BlockDriverState *bs, Error **errp); int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + MirrorSyncMode sync_mode, Error **errp); BdrvDirtyBitmap *bdrv_frozen_bitmap_decref(BlockDriverState *bs, BdrvDirtyBitmap *parent, diff --git a/qapi/block-core.json b/qapi/block-core.json index 92c9e53..421fd25 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -534,12 +534,14 @@ # # @none: only copy data written from now on # -# @incremental: only copy data described by the dirty bitmap. Since: 2.4 +# @incremental: Copy data described by the bitmap, and clear it. Since: 2.4 +# +# @differential: Copy data described by the bitmap, don't clear it. Since: 2.4 # # Since: 1.3 ## { 'enum': 'MirrorSyncMode', - 'data': ['top', 'full', 'none', 'incremental'] } + 'data': ['top', 'full', 'none', 'incremental', 'differential'] } ## # @BlockJobType: -- 2.1.0