提交 09158f00 编写于 作者: B Benoît Canet 提交者: Kevin Wolf

block: Add replaces argument to drive-mirror

drive-mirror will bdrv_swap the new BDS named node-name with the one
pointed by replaces when the mirroring is finished.
Signed-off-by: NBenoit Canet <benoit@irqsave.net>
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
上级 823c6863
...@@ -5766,3 +5766,28 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) ...@@ -5766,3 +5766,28 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
return false; return false;
} }
BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
{
BlockDriverState *to_replace_bs = bdrv_find_node(node_name);
if (!to_replace_bs) {
error_setg(errp, "Node name '%s' not found", node_name);
return NULL;
}
if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) {
return NULL;
}
/* We don't want arbitrary node of the BDS chain to be replaced only the top
* most non filter in order to prevent data corruption.
* Another benefit is that this tests exclude backing files which are
* blocked by the backing blockers.
*/
if (!bdrv_is_first_non_filter(to_replace_bs)) {
error_setg(errp, "Only top most non filter can be replaced");
return NULL;
}
return to_replace_bs;
}
...@@ -32,6 +32,12 @@ typedef struct MirrorBlockJob { ...@@ -32,6 +32,12 @@ typedef struct MirrorBlockJob {
RateLimit limit; RateLimit limit;
BlockDriverState *target; BlockDriverState *target;
BlockDriverState *base; BlockDriverState *base;
/* The name of the graph node to replace */
char *replaces;
/* The BDS to replace */
BlockDriverState *to_replace;
/* Used to block operations on the drive-mirror-replace target */
Error *replace_blocker;
bool is_none_mode; bool is_none_mode;
BlockdevOnError on_source_error, on_target_error; BlockdevOnError on_source_error, on_target_error;
bool synced; bool synced;
...@@ -500,10 +506,14 @@ immediate_exit: ...@@ -500,10 +506,14 @@ immediate_exit:
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
bdrv_iostatus_disable(s->target); bdrv_iostatus_disable(s->target);
if (s->should_complete && ret == 0) { if (s->should_complete && ret == 0) {
if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) { BlockDriverState *to_replace = s->common.bs;
bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL); if (s->to_replace) {
to_replace = s->to_replace;
} }
bdrv_swap(s->target, s->common.bs); if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
}
bdrv_swap(s->target, to_replace);
if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) { if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
/* drop the bs loop chain formed by the swap: break the loop then /* drop the bs loop chain formed by the swap: break the loop then
* trigger the unref from the top one */ * trigger the unref from the top one */
...@@ -512,6 +522,12 @@ immediate_exit: ...@@ -512,6 +522,12 @@ immediate_exit:
bdrv_unref(p); bdrv_unref(p);
} }
} }
if (s->to_replace) {
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
error_free(s->replace_blocker);
bdrv_unref(s->to_replace);
}
g_free(s->replaces);
bdrv_unref(s->target); bdrv_unref(s->target);
block_job_completed(&s->common, ret); block_job_completed(&s->common, ret);
} }
...@@ -550,6 +566,20 @@ static void mirror_complete(BlockJob *job, Error **errp) ...@@ -550,6 +566,20 @@ static void mirror_complete(BlockJob *job, Error **errp)
return; return;
} }
/* check the target bs is not blocked and block all operations on it */
if (s->replaces) {
s->to_replace = check_to_replace_node(s->replaces, &local_err);
if (!s->to_replace) {
error_propagate(errp, local_err);
return;
}
error_setg(&s->replace_blocker,
"block device is in use by block-job-complete");
bdrv_op_block_all(s->to_replace, s->replace_blocker);
bdrv_ref(s->to_replace);
}
s->should_complete = true; s->should_complete = true;
block_job_resume(job); block_job_resume(job);
} }
...@@ -572,14 +602,15 @@ static const BlockJobDriver commit_active_job_driver = { ...@@ -572,14 +602,15 @@ static const BlockJobDriver commit_active_job_driver = {
}; };
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
int64_t speed, int64_t granularity, const char *replaces,
int64_t buf_size, int64_t speed, int64_t granularity,
BlockdevOnError on_source_error, int64_t buf_size,
BlockdevOnError on_target_error, BlockdevOnError on_source_error,
BlockDriverCompletionFunc *cb, BlockdevOnError on_target_error,
void *opaque, Error **errp, BlockDriverCompletionFunc *cb,
const BlockJobDriver *driver, void *opaque, Error **errp,
bool is_none_mode, BlockDriverState *base) const BlockJobDriver *driver,
bool is_none_mode, BlockDriverState *base)
{ {
MirrorBlockJob *s; MirrorBlockJob *s;
...@@ -610,6 +641,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, ...@@ -610,6 +641,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
return; return;
} }
s->replaces = g_strdup(replaces);
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->target = target; s->target = target;
...@@ -631,6 +663,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, ...@@ -631,6 +663,7 @@ 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,
int64_t speed, int64_t granularity, int64_t buf_size, int64_t speed, int64_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
...@@ -642,7 +675,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, ...@@ -642,7 +675,8 @@ 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 ? bs->backing_hd : NULL; base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
mirror_start_job(bs, target, speed, granularity, buf_size, mirror_start_job(bs, target, replaces,
speed, granularity, buf_size,
on_source_error, on_target_error, cb, opaque, errp, on_source_error, on_target_error, cb, opaque, errp,
&mirror_job_driver, is_none_mode, base); &mirror_job_driver, is_none_mode, base);
} }
...@@ -690,7 +724,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, ...@@ -690,7 +724,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
} }
bdrv_ref(base); bdrv_ref(base);
mirror_start_job(bs, base, speed, 0, 0, mirror_start_job(bs, base, NULL, speed, 0, 0,
on_error, on_error, cb, opaque, &local_err, on_error, on_error, cb, opaque, &local_err,
&commit_active_job_driver, false, base); &commit_active_job_driver, false, base);
if (local_err) { if (local_err) {
......
...@@ -2100,6 +2100,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) ...@@ -2100,6 +2100,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
void qmp_drive_mirror(const char *device, const char *target, void qmp_drive_mirror(const char *device, const char *target,
bool has_format, const char *format, bool has_format, const char *format,
bool has_node_name, const char *node_name, bool has_node_name, const char *node_name,
bool has_replaces, const char *replaces,
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,
...@@ -2187,6 +2188,29 @@ void qmp_drive_mirror(const char *device, const char *target, ...@@ -2187,6 +2188,29 @@ void qmp_drive_mirror(const char *device, const char *target,
return; return;
} }
if (has_replaces) {
BlockDriverState *to_replace_bs;
if (!has_node_name) {
error_setg(errp, "a node-name must be provided when replacing a"
" named node of the graph");
return;
}
to_replace_bs = check_to_replace_node(replaces, &local_err);
if (!to_replace_bs) {
error_propagate(errp, local_err);
return;
}
if (size != bdrv_getlength(to_replace_bs)) {
error_setg(errp, "cannot replace image with a mirror image of "
"different size");
return;
}
}
if ((sync == MIRROR_SYNC_MODE_FULL || !source) if ((sync == MIRROR_SYNC_MODE_FULL || !source)
&& mode != NEW_IMAGE_MODE_EXISTING) && mode != NEW_IMAGE_MODE_EXISTING)
{ {
...@@ -2231,7 +2255,12 @@ void qmp_drive_mirror(const char *device, const char *target, ...@@ -2231,7 +2255,12 @@ void qmp_drive_mirror(const char *device, const char *target,
return; return;
} }
mirror_start(bs, target_bs, speed, granularity, buf_size, sync, /* pass the node name to replace to mirror start since it's loose coupling
* and will allow to check whether the node still exist at mirror completion
*/
mirror_start(bs, target_bs,
has_replaces ? replaces : NULL,
speed, granularity, buf_size, sync,
on_source_error, on_target_error, 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) {
......
...@@ -933,7 +933,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) ...@@ -933,7 +933,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
} }
qmp_drive_mirror(device, filename, !!format, format, qmp_drive_mirror(device, filename, !!format, format,
false, NULL, false, NULL, false, NULL,
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, true, mode, false, 0, false, 0, false, 0,
false, 0, false, 0, &err); false, 0, false, 0, &err);
......
...@@ -175,6 +175,7 @@ typedef enum BlockOpType { ...@@ -175,6 +175,7 @@ typedef enum BlockOpType {
BLOCK_OP_TYPE_MIRROR, BLOCK_OP_TYPE_MIRROR,
BLOCK_OP_TYPE_RESIZE, BLOCK_OP_TYPE_RESIZE,
BLOCK_OP_TYPE_STREAM, BLOCK_OP_TYPE_STREAM,
BLOCK_OP_TYPE_REPLACE,
BLOCK_OP_TYPE_MAX, BLOCK_OP_TYPE_MAX,
} BlockOpType; } BlockOpType;
...@@ -314,6 +315,9 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, ...@@ -314,6 +315,9 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
BlockDriverState *candidate); BlockDriverState *candidate);
bool bdrv_is_first_non_filter(BlockDriverState *candidate); bool bdrv_is_first_non_filter(BlockDriverState *candidate);
/* check if a named node can be replaced when doing drive-mirror */
BlockDriverState *check_to_replace_node(const char *node_name, Error **errp);
/* async block I/O */ /* async block I/O */
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
int sector_num); int sector_num);
......
...@@ -489,6 +489,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, ...@@ -489,6 +489,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
* mirror_start: * mirror_start:
* @bs: Block device to operate on. * @bs: Block device to operate on.
* @target: Block device to write to. * @target: Block device to write to.
* @replaces: Block graph node name to replace once the mirror is done. Can
* only be used when full mirroring is selected.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @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.
...@@ -505,6 +507,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, ...@@ -505,6 +507,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
* @bs will be switched to read from @target. * @bs will be switched to read from @target.
*/ */
void mirror_start(BlockDriverState *bs, BlockDriverState *target, void mirror_start(BlockDriverState *bs, BlockDriverState *target,
const char *replaces,
int64_t speed, int64_t granularity, int64_t buf_size, int64_t speed, int64_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
......
...@@ -768,6 +768,10 @@ ...@@ -768,6 +768,10 @@
# @node-name: #optional the new block driver state node name in the graph # @node-name: #optional the new block driver state node name in the graph
# (Since 2.1) # (Since 2.1)
# #
# @replaces: #optional with sync=full graph node name to be replaced by the new
# image when a whole image copy is done. This can be used to repair
# broken Quorum files. (Since 2.1)
#
# @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'.
# #
...@@ -800,7 +804,7 @@ ...@@ -800,7 +804,7 @@
## ##
{ 'command': 'drive-mirror', { 'command': 'drive-mirror',
'data': { 'device': 'str', 'target': 'str', '*format': 'str', 'data': { 'device': 'str', 'target': 'str', '*format': 'str',
'*node-name': 'str', '*node-name': 'str', '*replaces': 'str',
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
'*speed': 'int', '*granularity': 'uint32', '*speed': 'int', '*granularity': 'uint32',
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
......
...@@ -1293,7 +1293,7 @@ EQMP ...@@ -1293,7 +1293,7 @@ EQMP
{ {
.name = "drive-mirror", .name = "drive-mirror",
.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?,"
"node-name:s?," "node-name:s?,replaces:s?,"
"on-source-error:s?,on-target-error:s?," "on-source-error:s?,on-target-error:s?,"
"granularity:i?,buf-size:i?", "granularity:i?,buf-size:i?",
.mhandler.cmd_new = qmp_marshal_input_drive_mirror, .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
...@@ -1317,6 +1317,8 @@ Arguments: ...@@ -1317,6 +1317,8 @@ Arguments:
- "format": format of new image (json-string, optional) - "format": format of new image (json-string, optional)
- "node-name": the name of the new block driver state in the node graph - "node-name": the name of the new block driver state in the node graph
(json-string, optional) (json-string, optional)
- "replaces": the block driver node name to replace when finished
(json-string, optional)
- "mode": how an image file should be created into the target - "mode": how an image file should be created into the target
file/device (NewImageMode, optional, default 'absolute-paths') file/device (NewImageMode, optional, default 'absolute-paths')
- "speed": maximum speed of the streaming job, in bytes per second - "speed": maximum speed of the streaming job, in bytes per second
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册