提交 d58d8453 编写于 作者: J John Snow 提交者: Kevin Wolf

qmp: Add support of "dirty-bitmap" sync mode for drive-backup

For "dirty-bitmap" sync mode, the block job will iterate through the
given dirty bitmap to decide if a sector needs backup (backup all the
dirty clusters and skip clean ones), just as allocation conditions of
"top" sync mode.
Signed-off-by: NFam Zheng <famz@redhat.com>
Signed-off-by: NJohn Snow <jsnow@redhat.com>
Reviewed-by: NMax Reitz <mreitz@redhat.com>
Reviewed-by: NEric Blake <eblake@redhat.com>
Reviewed-by: NStefan Hajnoczi <stefanha@redhat.com>
Message-id: 1429314609-29776-11-git-send-email-jsnow@redhat.com
Signed-off-by: NStefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 9bd2b08f
...@@ -5783,6 +5783,15 @@ static void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, ...@@ -5783,6 +5783,15 @@ static void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
} }
} }
/**
* Advance an HBitmapIter to an arbitrary offset.
*/
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
{
assert(hbi->hb);
hbitmap_iter_init(hbi, hbi->hb, offset);
}
int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
{ {
return hbitmap_count(bitmap->bitmap); return hbitmap_count(bitmap->bitmap);
......
...@@ -37,6 +37,8 @@ typedef struct CowRequest { ...@@ -37,6 +37,8 @@ typedef struct CowRequest {
typedef struct BackupBlockJob { typedef struct BackupBlockJob {
BlockJob common; BlockJob common;
BlockDriverState *target; BlockDriverState *target;
/* bitmap for sync=dirty-bitmap */
BdrvDirtyBitmap *sync_bitmap;
MirrorSyncMode sync_mode; MirrorSyncMode sync_mode;
RateLimit limit; RateLimit limit;
BlockdevOnError on_source_error; BlockdevOnError on_source_error;
...@@ -242,6 +244,91 @@ static void backup_complete(BlockJob *job, void *opaque) ...@@ -242,6 +244,91 @@ static void backup_complete(BlockJob *job, void *opaque)
g_free(data); g_free(data);
} }
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
{
if (block_job_is_cancelled(&job->common)) {
return true;
}
/* we need to yield so that bdrv_drain_all() 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, QEMU_CLOCK_REALTIME, delay_ns);
} else {
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0);
}
if (block_job_is_cancelled(&job->common)) {
return true;
}
return false;
}
static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
{
bool error_is_read;
int ret = 0;
int clusters_per_iter;
uint32_t granularity;
int64_t sector;
int64_t cluster;
int64_t end;
int64_t last_cluster = -1;
BlockDriverState *bs = job->common.bs;
HBitmapIter hbi;
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1);
bdrv_dirty_iter_init(bs, job->sync_bitmap, &hbi);
/* Find the next dirty sector(s) */
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
cluster = sector / BACKUP_SECTORS_PER_CLUSTER;
/* Fake progress updates for any clusters we skipped */
if (cluster != last_cluster + 1) {
job->common.offset += ((cluster - last_cluster - 1) *
BACKUP_CLUSTER_SIZE);
}
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
do {
if (yield_and_check(job)) {
return ret;
}
ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
if ((ret < 0) &&
backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT) {
return ret;
}
} while (ret < 0);
}
/* If the bitmap granularity is smaller than the backup granularity,
* we need to advance the iterator pointer to the next cluster. */
if (granularity < BACKUP_CLUSTER_SIZE) {
bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER);
}
last_cluster = cluster - 1;
}
/* Play some final catchup with the progress meter */
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
if (last_cluster + 1 < end) {
job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE);
}
return ret;
}
static void coroutine_fn backup_run(void *opaque) static void coroutine_fn backup_run(void *opaque)
{ {
BackupBlockJob *job = opaque; BackupBlockJob *job = opaque;
...@@ -259,8 +346,7 @@ static void coroutine_fn backup_run(void *opaque) ...@@ -259,8 +346,7 @@ static void coroutine_fn backup_run(void *opaque)
qemu_co_rwlock_init(&job->flush_rwlock); qemu_co_rwlock_init(&job->flush_rwlock);
start = 0; start = 0;
end = DIV_ROUND_UP(job->common.len / BDRV_SECTOR_SIZE, end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
BACKUP_SECTORS_PER_CLUSTER);
job->bitmap = hbitmap_alloc(end, 0); job->bitmap = hbitmap_alloc(end, 0);
...@@ -278,28 +364,13 @@ static void coroutine_fn backup_run(void *opaque) ...@@ -278,28 +364,13 @@ static void coroutine_fn backup_run(void *opaque)
qemu_coroutine_yield(); qemu_coroutine_yield();
job->common.busy = true; job->common.busy = true;
} }
} else if (job->sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) {
ret = backup_run_incremental(job);
} else { } else {
/* Both FULL and TOP SYNC_MODE's require copying.. */ /* Both FULL and TOP SYNC_MODE's require copying.. */
for (; start < end; start++) { for (; start < end; start++) {
bool error_is_read; bool error_is_read;
if (yield_and_check(job)) {
if (block_job_is_cancelled(&job->common)) {
break;
}
/* we need to yield so that bdrv_drain_all() 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, QEMU_CLOCK_REALTIME, delay_ns);
} else {
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0);
}
if (block_job_is_cancelled(&job->common)) {
break; break;
} }
...@@ -357,6 +428,18 @@ static void coroutine_fn backup_run(void *opaque) ...@@ -357,6 +428,18 @@ static void coroutine_fn backup_run(void *opaque)
qemu_co_rwlock_wrlock(&job->flush_rwlock); qemu_co_rwlock_wrlock(&job->flush_rwlock);
qemu_co_rwlock_unlock(&job->flush_rwlock); qemu_co_rwlock_unlock(&job->flush_rwlock);
if (job->sync_bitmap) {
BdrvDirtyBitmap *bm;
if (ret < 0) {
/* Merge the successor back into the parent, delete nothing. */
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
assert(bm);
} else {
/* Everything is fine, delete this bitmap and install the backup. */
bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
assert(bm);
}
}
hbitmap_free(job->bitmap); hbitmap_free(job->bitmap);
bdrv_iostatus_disable(target); bdrv_iostatus_disable(target);
...@@ -369,6 +452,7 @@ static void coroutine_fn backup_run(void *opaque) ...@@ -369,6 +452,7 @@ static void coroutine_fn backup_run(void *opaque)
void backup_start(BlockDriverState *bs, BlockDriverState *target, void backup_start(BlockDriverState *bs, BlockDriverState *target,
int64_t speed, MirrorSyncMode sync_mode, int64_t speed, MirrorSyncMode sync_mode,
BdrvDirtyBitmap *sync_bitmap,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,
...@@ -412,17 +496,36 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, ...@@ -412,17 +496,36 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
return; return;
} }
if (sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) {
if (!sync_bitmap) {
error_setg(errp, "must provide a valid bitmap name for "
"\"dirty-bitmap\" sync mode");
return;
}
/* Create a new bitmap, and freeze/disable this one. */
if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) {
return;
}
} else if (sync_bitmap) {
error_setg(errp,
"a sync_bitmap was provided to backup_run, "
"but received an incompatible sync_mode (%s)",
MirrorSyncMode_lookup[sync_mode]);
return;
}
len = bdrv_getlength(bs); len = bdrv_getlength(bs);
if (len < 0) { if (len < 0) {
error_setg_errno(errp, -len, "unable to get length for '%s'", error_setg_errno(errp, -len, "unable to get length for '%s'",
bdrv_get_device_name(bs)); bdrv_get_device_name(bs));
return; goto error;
} }
BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed, BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed,
cb, opaque, errp); cb, opaque, errp);
if (!job) { if (!job) {
return; goto error;
} }
bdrv_op_block_all(target, job->common.blocker); bdrv_op_block_all(target, job->common.blocker);
...@@ -431,7 +534,15 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, ...@@ -431,7 +534,15 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
job->on_target_error = on_target_error; job->on_target_error = on_target_error;
job->target = target; job->target = target;
job->sync_mode = sync_mode; job->sync_mode = sync_mode;
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP ?
sync_bitmap : NULL;
job->common.len = len; job->common.len = len;
job->common.co = qemu_coroutine_create(backup_run); job->common.co = qemu_coroutine_create(backup_run);
qemu_coroutine_enter(job->common.co, job); qemu_coroutine_enter(job->common.co, job);
return;
error:
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
}
} }
...@@ -718,6 +718,10 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, ...@@ -718,6 +718,10 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
bool is_none_mode; bool is_none_mode;
BlockDriverState *base; BlockDriverState *base;
if (mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) {
error_setg(errp, "Sync mode 'dirty-bitmap' not supported");
return;
}
is_none_mode = mode == MIRROR_SYNC_MODE_NONE; is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL; base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
mirror_start_job(bs, target, replaces, mirror_start_job(bs, target, replaces,
......
...@@ -1585,6 +1585,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) ...@@ -1585,6 +1585,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
backup->sync, backup->sync,
backup->has_mode, backup->mode, backup->has_mode, backup->mode,
backup->has_speed, backup->speed, backup->has_speed, backup->speed,
backup->has_bitmap, backup->bitmap,
backup->has_on_source_error, backup->on_source_error, backup->has_on_source_error, backup->on_source_error,
backup->has_on_target_error, backup->on_target_error, backup->has_on_target_error, backup->on_target_error,
&local_err); &local_err);
...@@ -2395,6 +2396,7 @@ void qmp_drive_backup(const char *device, const char *target, ...@@ -2395,6 +2396,7 @@ void qmp_drive_backup(const char *device, const char *target,
enum MirrorSyncMode sync, enum MirrorSyncMode sync,
bool has_mode, enum NewImageMode mode, bool has_mode, enum NewImageMode mode,
bool has_speed, int64_t speed, bool has_speed, int64_t speed,
bool has_bitmap, const char *bitmap,
bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_source_error, BlockdevOnError on_source_error,
bool has_on_target_error, BlockdevOnError on_target_error, bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp) Error **errp)
...@@ -2403,6 +2405,7 @@ void qmp_drive_backup(const char *device, const char *target, ...@@ -2403,6 +2405,7 @@ void qmp_drive_backup(const char *device, const char *target,
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *target_bs; BlockDriverState *target_bs;
BlockDriverState *source = NULL; BlockDriverState *source = NULL;
BdrvDirtyBitmap *bmap = NULL;
AioContext *aio_context; AioContext *aio_context;
BlockDriver *drv = NULL; BlockDriver *drv = NULL;
Error *local_err = NULL; Error *local_err = NULL;
...@@ -2502,7 +2505,16 @@ void qmp_drive_backup(const char *device, const char *target, ...@@ -2502,7 +2505,16 @@ void qmp_drive_backup(const char *device, const char *target,
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, if (has_bitmap) {
bmap = bdrv_find_dirty_bitmap(bs, bitmap);
if (!bmap) {
error_setg(errp, "Bitmap '%s' could not be found", bitmap);
goto out;
}
}
backup_start(bs, target_bs, speed, sync, bmap,
on_source_error, on_target_error,
block_job_cb, bs, &local_err); block_job_cb, bs, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
bdrv_unref(target_bs); bdrv_unref(target_bs);
...@@ -2563,8 +2575,8 @@ void qmp_blockdev_backup(const char *device, const char *target, ...@@ -2563,8 +2575,8 @@ void qmp_blockdev_backup(const char *device, const char *target,
bdrv_ref(target_bs); bdrv_ref(target_bs);
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, backup_start(bs, target_bs, speed, sync, NULL, on_source_error,
block_job_cb, bs, &local_err); on_target_error, block_job_cb, bs, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
bdrv_unref(target_bs); bdrv_unref(target_bs);
error_propagate(errp, local_err); error_propagate(errp, local_err);
......
...@@ -1061,7 +1061,8 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict) ...@@ -1061,7 +1061,8 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
qmp_drive_backup(device, filename, !!format, format, qmp_drive_backup(device, filename, !!format, format,
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
true, mode, false, 0, false, 0, false, 0, &err); true, mode, false, 0, false, NULL,
false, 0, false, 0, &err);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);
} }
......
...@@ -481,6 +481,7 @@ void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, ...@@ -481,6 +481,7 @@ void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
int64_t cur_sector, int nr_sectors); int64_t cur_sector, int nr_sectors);
void bdrv_dirty_iter_init(BlockDriverState *bs, void bdrv_dirty_iter_init(BlockDriverState *bs,
BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi); BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap);
void bdrv_enable_copy_on_read(BlockDriverState *bs); void bdrv_enable_copy_on_read(BlockDriverState *bs);
......
...@@ -602,6 +602,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, ...@@ -602,6 +602,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
* @target: Block device to write to. * @target: Block device to write to.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @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. * @sync_mode: What parts of the disk image should be copied to the destination.
* @sync_bitmap: The dirty bitmap if sync_mode is MIRROR_SYNC_MODE_DIRTY_BITMAP.
* @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.
* @cb: Completion function for the job. * @cb: Completion function for the job.
...@@ -612,6 +613,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, ...@@ -612,6 +613,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
*/ */
void backup_start(BlockDriverState *bs, BlockDriverState *target, void backup_start(BlockDriverState *bs, BlockDriverState *target,
int64_t speed, MirrorSyncMode sync_mode, int64_t speed, MirrorSyncMode sync_mode,
BdrvDirtyBitmap *sync_bitmap,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,
......
...@@ -512,10 +512,12 @@ ...@@ -512,10 +512,12 @@
# #
# @none: only copy data written from now on # @none: only copy data written from now on
# #
# @dirty-bitmap: only copy data described by the dirty bitmap. Since: 2.4
#
# Since: 1.3 # Since: 1.3
## ##
{ 'enum': 'MirrorSyncMode', { 'enum': 'MirrorSyncMode',
'data': ['top', 'full', 'none'] } 'data': ['top', 'full', 'none', 'dirty-bitmap'] }
## ##
# @BlockJobType: # @BlockJobType:
...@@ -690,14 +692,18 @@ ...@@ -690,14 +692,18 @@
# probe if @mode is 'existing', else the format of the source # probe if @mode is 'existing', else the format of the source
# #
# @sync: what parts of the disk image should be copied to the destination # @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 # (all the disk, only the sectors allocated in the topmost image, from a
# only new I/O). # dirty bitmap, or only new I/O).
# #
# @mode: #optional whether and how QEMU should create a new image, default is # @mode: #optional whether and how QEMU should create a new image, default is
# 'absolute-paths'. # 'absolute-paths'.
# #
# @speed: #optional the maximum speed, in bytes per second # @speed: #optional the maximum speed, in bytes per second
# #
# @bitmap: #optional the name of dirty bitmap if sync is "dirty-bitmap".
# Must be present if sync is "dirty-bitmap", must NOT be present
# otherwise. (Since 2.4)
#
# @on-source-error: #optional the action to take on an error on the source, # @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used # default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo). # if the block device supports io-status (see BlockInfo).
...@@ -715,7 +721,7 @@ ...@@ -715,7 +721,7 @@
{ 'type': 'DriveBackup', { 'type': 'DriveBackup',
'data': { 'device': 'str', 'target': 'str', '*format': 'str', 'data': { 'device': 'str', 'target': 'str', '*format': 'str',
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
'*speed': 'int', '*speed': 'int', '*bitmap': 'str',
'*on-source-error': 'BlockdevOnError', '*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } } '*on-target-error': 'BlockdevOnError' } }
......
...@@ -1110,7 +1110,7 @@ EQMP ...@@ -1110,7 +1110,7 @@ EQMP
{ {
.name = "drive-backup", .name = "drive-backup",
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
"on-source-error:s?,on-target-error:s?", "bitmap:s?,on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_input_drive_backup, .mhandler.cmd_new = qmp_marshal_input_drive_backup,
}, },
...@@ -1137,8 +1137,10 @@ Arguments: ...@@ -1137,8 +1137,10 @@ Arguments:
(json-string, optional) (json-string, optional)
- "sync": what parts of the disk image should be copied to the destination; - "sync": what parts of the disk image should be copied to the destination;
possibilities include "full" for all the disk, "top" for only the sectors possibilities include "full" for all the disk, "top" for only the sectors
allocated in the topmost image, or "none" to only replicate new I/O allocated in the topmost image, "dirty-bitmap" for only the dirty sectors in
(MirrorSyncMode). the bitmap, or "none" to only replicate new I/O (MirrorSyncMode).
- "bitmap": dirty bitmap name for sync==dirty-bitmap. Must be present if sync
is "dirty-bitmap", must NOT be present otherwise.
- "mode": whether and how QEMU should create a new image - "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths') (NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional) - "speed": the maximum speed, in bytes per second (json-int, optional)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册