From cf29a570a7aa7abab66bf256fdf9540873590811 Mon Sep 17 00:00:00 2001 From: Benoît Canet Date: Wed, 11 Jun 2014 15:24:10 +0200 Subject: quorum: Add the rewrite-corrupted parameter to quorum On read operations when this parameter is set and some replicas are corrupted while quorum can be reached quorum will proceed to rewrite the correct version of the data to fix the corrupted replicas. This will shine with SSD where the FTL will remap the same block at another place on rewrite. Signed-off-by: Benoit Canet Reviewed-by: Max Reitz Signed-off-by: Kevin Wolf --- qapi/block-core.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'qapi') diff --git a/qapi/block-core.json b/qapi/block-core.json index af6b436540..de31f9fd54 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1329,12 +1329,15 @@ # # @vote-threshold: the vote limit under which a read will fail # +# @rewrite-corrupted: #optional rewrite corrupted data when quorum is reached +# (Since 2.1) +# # Since: 2.0 ## { 'type': 'BlockdevOptionsQuorum', 'data': { '*blkverify': 'bool', 'children': [ 'BlockdevRef' ], - 'vote-threshold': 'int' } } + 'vote-threshold': 'int', '*rewrite-corrupted': 'bool' } } ## # @BlockdevOptions -- cgit v1.2.3-55-g7522 From 4c828dc61a0d729ae9bfa6fdee55558314135737 Mon Sep 17 00:00:00 2001 From: Benoît Canet Date: Mon, 16 Jun 2014 12:00:55 +0200 Subject: block: Add node-name argument to drive-mirror This new argument can be used to specify the node-name of the new mirrored BDS. Signed-off-by: Benoit Canet Reviewed-by: Max Reitz Signed-off-by: Kevin Wolf --- blockdev.c | 11 +++++++++-- hmp.c | 1 + qapi/block-core.json | 4 ++++ qmp-commands.hx | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) (limited to 'qapi') diff --git a/blockdev.c b/blockdev.c index e8bfa3c660..943301226d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2099,6 +2099,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, + bool has_node_name, const char *node_name, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, @@ -2112,6 +2113,7 @@ void qmp_drive_mirror(const char *device, const char *target, BlockDriverState *source, *target_bs; BlockDriver *drv = NULL; Error *local_err = NULL; + QDict *options = NULL; int flags; int64_t size; int ret; @@ -2213,12 +2215,17 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + if (has_node_name) { + options = qdict_new(); + qdict_put(options, "node-name", qstring_from_str(node_name)); + } + /* Mirroring takes care of copy-on-write using the source's backing * file. */ target_bs = NULL; - ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING, - drv, &local_err); + ret = bdrv_open(&target_bs, target, NULL, options, + flags | BDRV_O_NO_BACKING, drv, &local_err); if (ret < 0) { error_propagate(errp, local_err); return; diff --git a/hmp.c b/hmp.c index dc3d2795d2..73acc3283c 100644 --- a/hmp.c +++ b/hmp.c @@ -933,6 +933,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) } qmp_drive_mirror(device, filename, !!format, format, + false, NULL, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, true, mode, false, 0, false, 0, false, 0, false, 0, false, 0, &err); diff --git a/qapi/block-core.json b/qapi/block-core.json index de31f9fd54..a46cdbe6aa 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -765,6 +765,9 @@ # @format: #optional the format of the new destination, default is to # probe if @mode is 'existing', else the format of the source # +# @node-name: #optional the new block driver state node name in the graph +# (Since 2.1) +# # @mode: #optional whether and how QEMU should create a new image, default is # 'absolute-paths'. # @@ -797,6 +800,7 @@ ## { 'command': 'drive-mirror', 'data': { 'device': 'str', 'target': 'str', '*format': 'str', + '*node-name': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', diff --git a/qmp-commands.hx b/qmp-commands.hx index e4a1c80434..5254938878 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1293,6 +1293,7 @@ EQMP { .name = "drive-mirror", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," + "node-name:s?," "on-source-error:s?,on-target-error:s?," "granularity:i?,buf-size:i?", .mhandler.cmd_new = qmp_marshal_input_drive_mirror, @@ -1314,6 +1315,8 @@ Arguments: - "device": device name to operate on (json-string) - "target": name of new image file (json-string) - "format": format of new image (json-string, optional) +- "node-name": the name of the new block driver state in the node graph + (json-string, optional) - "mode": how an image file should be created into the target file/device (NewImageMode, optional, default 'absolute-paths') - "speed": maximum speed of the streaming job, in bytes per second -- cgit v1.2.3-55-g7522 From 518848a214ff67bbaea087552a709afde4f88fa5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 27 Jun 2014 19:24:13 +0200 Subject: blockjob: Fix recent BLOCK_JOB_READY regression Commit bcada37 dropped the (up to now undocumented) members type, len, offset, speed, breaking tests/qemu-iotests/040 and 041. Restore and document them. This fixes 040, and partially fixes 041. Signed-off-by: Markus Armbruster Tested-By: Benoit Canet Signed-off-by: Kevin Wolf --- blockjob.c | 6 +++++- qapi/block-core.json | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'qapi') diff --git a/blockjob.c b/blockjob.c index a6db01e953..a32c1c8dd7 100644 --- a/blockjob.c +++ b/blockjob.c @@ -270,7 +270,11 @@ void block_job_event_completed(BlockJob *job, const char *msg) void block_job_event_ready(BlockJob *job) { - qapi_event_send_block_job_ready(bdrv_get_device_name(job->bs), &error_abort); + qapi_event_send_block_job_ready(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, &error_abort); } BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, diff --git a/qapi/block-core.json b/qapi/block-core.json index a46cdbe6aa..6f41f84c44 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1558,12 +1558,25 @@ # # Emitted when a block job is ready to complete # +# @type: job type +# # @device: device name # +# @len: maximum progress value +# +# @offset: current progress value. On success this is equal to len. +# On failure this is less than len +# +# @speed: rate limit, bytes per second +# # Note: The "ready to complete" status is always reset by a @BLOCK_JOB_ERROR # event # # Since: 1.3 ## { 'event': 'BLOCK_JOB_READY', - 'data': { 'device': 'str' } } + 'data': { 'type' : 'BlockJobType', + 'device': 'str', + 'len' : 'int', + 'offset': 'int', + 'speed' : 'int' } } -- cgit v1.2.3-55-g7522 From 823c686356e6758bacb46d3a316b841536d6d707 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 27 Jun 2014 19:24:14 +0200 Subject: blockjob: Fix recent BLOCK_JOB_ERROR regression Commit 5a2d2cb screwed up the the value of members device and action, breaking tests/qemu-iotests/041. Signed-off-by: Markus Armbruster Tested-By: Benoit Canet Reviewed-by: Kevin Wolf Reviewed-by: Luiz Capitulino Signed-off-by: Kevin Wolf --- blockjob.c | 2 +- qapi/block-core.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'qapi') diff --git a/blockjob.c b/blockjob.c index a32c1c8dd7..67a64ea318 100644 --- a/blockjob.c +++ b/blockjob.c @@ -300,7 +300,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, default: abort(); } - qapi_event_send_block_job_error(bdrv_get_device_name(bs), + qapi_event_send_block_job_error(bdrv_get_device_name(job->bs), is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE, action, &error_abort); diff --git a/qapi/block-core.json b/qapi/block-core.json index 6f41f84c44..ff7224f647 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1551,7 +1551,7 @@ { 'event': 'BLOCK_JOB_ERROR', 'data': { 'device' : 'str', 'operation': 'IoOperationType', - 'action' : 'BlockdevOnError' } } + 'action' : 'BlockErrorAction' } } ## # @BLOCK_JOB_READY -- cgit v1.2.3-55-g7522 From 09158f00e0fdb506dcbf36f67c615b7f6c604c5a Mon Sep 17 00:00:00 2001 From: Benoît Canet Date: Fri, 27 Jun 2014 18:25:25 +0200 Subject: 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: Benoit Canet Signed-off-by: Kevin Wolf --- block.c | 25 ++++++++++++++++++++ block/mirror.c | 60 +++++++++++++++++++++++++++++++++++++---------- blockdev.c | 31 +++++++++++++++++++++++- hmp.c | 2 +- include/block/block.h | 4 ++++ include/block/block_int.h | 3 +++ qapi/block-core.json | 6 ++++- qmp-commands.hx | 4 +++- 8 files changed, 118 insertions(+), 17 deletions(-) (limited to 'qapi') diff --git a/block.c b/block.c index 106238d0b7..9ecadf002f 100644 --- a/block.c +++ b/block.c @@ -5766,3 +5766,28 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate) 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; +} diff --git a/block/mirror.c b/block/mirror.c index 7c9f898089..6c3ee7041c 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -32,6 +32,12 @@ typedef struct MirrorBlockJob { RateLimit limit; BlockDriverState *target; 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; BlockdevOnError on_source_error, on_target_error; bool synced; @@ -500,10 +506,14 @@ immediate_exit: bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); bdrv_iostatus_disable(s->target); if (s->should_complete && ret == 0) { - if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) { - bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL); + BlockDriverState *to_replace = s->common.bs; + 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) { /* drop the bs loop chain formed by the swap: break the loop then * trigger the unref from the top one */ @@ -512,6 +522,12 @@ immediate_exit: 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); block_job_completed(&s->common, ret); } @@ -550,6 +566,20 @@ static void mirror_complete(BlockJob *job, Error **errp) 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; block_job_resume(job); } @@ -572,14 +602,15 @@ static const BlockJobDriver commit_active_job_driver = { }; static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, int64_t granularity, - int64_t buf_size, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, - BlockDriverCompletionFunc *cb, - void *opaque, Error **errp, - const BlockJobDriver *driver, - bool is_none_mode, BlockDriverState *base) + const char *replaces, + int64_t speed, int64_t granularity, + int64_t buf_size, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, + BlockDriverCompletionFunc *cb, + void *opaque, Error **errp, + const BlockJobDriver *driver, + bool is_none_mode, BlockDriverState *base) { MirrorBlockJob *s; @@ -610,6 +641,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, return; } + s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; s->target = target; @@ -631,6 +663,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, } void mirror_start(BlockDriverState *bs, BlockDriverState *target, + const char *replaces, int64_t speed, int64_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, @@ -642,7 +675,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, is_none_mode = mode == MIRROR_SYNC_MODE_NONE; 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, &mirror_job_driver, is_none_mode, base); } @@ -690,7 +724,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *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, &commit_active_job_driver, false, base); if (local_err) { diff --git a/blockdev.c b/blockdev.c index 943301226d..69b7c2a8c5 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2100,6 +2100,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, bool has_node_name, const char *node_name, + bool has_replaces, const char *replaces, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, @@ -2187,6 +2188,29 @@ void qmp_drive_mirror(const char *device, const char *target, 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) && mode != NEW_IMAGE_MODE_EXISTING) { @@ -2231,7 +2255,12 @@ void qmp_drive_mirror(const char *device, const char *target, 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, block_job_cb, bs, &local_err); if (local_err != NULL) { diff --git a/hmp.c b/hmp.c index 73acc3283c..6429e6b447 100644 --- a/hmp.c +++ b/hmp.c @@ -933,7 +933,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) } qmp_drive_mirror(device, filename, !!format, format, - false, NULL, + false, NULL, false, NULL, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, true, mode, false, 0, false, 0, false, 0, false, 0, false, 0, &err); diff --git a/include/block/block.h b/include/block/block.h index d0baf4fb83..b53833d1d6 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -175,6 +175,7 @@ typedef enum BlockOpType { BLOCK_OP_TYPE_MIRROR, BLOCK_OP_TYPE_RESIZE, BLOCK_OP_TYPE_STREAM, + BLOCK_OP_TYPE_REPLACE, BLOCK_OP_TYPE_MAX, } BlockOpType; @@ -314,6 +315,9 @@ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, 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 */ typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, int sector_num); diff --git a/include/block/block_int.h b/include/block/block_int.h index 135c5dc0e9..53e77cf11e 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -489,6 +489,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base, * mirror_start: * @bs: Block device to operate on. * @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. * @granularity: The chosen granularity for the dirty bitmap. * @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, * @bs will be switched to read from @target. */ void mirror_start(BlockDriverState *bs, BlockDriverState *target, + const char *replaces, int64_t speed, int64_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, diff --git a/qapi/block-core.json b/qapi/block-core.json index ff7224f647..406ce03951 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -768,6 +768,10 @@ # @node-name: #optional the new block driver state node name in the graph # (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 # 'absolute-paths'. # @@ -800,7 +804,7 @@ ## { 'command': 'drive-mirror', 'data': { 'device': 'str', 'target': 'str', '*format': 'str', - '*node-name': 'str', + '*node-name': 'str', '*replaces': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', diff --git a/qmp-commands.hx b/qmp-commands.hx index 5254938878..d342b8a067 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1293,7 +1293,7 @@ EQMP { .name = "drive-mirror", .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?," "granularity:i?,buf-size:i?", .mhandler.cmd_new = qmp_marshal_input_drive_mirror, @@ -1317,6 +1317,8 @@ Arguments: - "format": format of new image (json-string, optional) - "node-name": the name of the new block driver state in the node graph (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 file/device (NewImageMode, optional, default 'absolute-paths') - "speed": maximum speed of the streaming job, in bytes per second -- cgit v1.2.3-55-g7522