block: extend block-commit to accept a string for the backing file
On some image chains, QEMU may not always be able to resolve the filenames properly, when updating the backing file of an image after a block commit. For instance, certain relative pathnames may fail, or drives may have been specified originally by file descriptor (e.g. /dev/fd/???), or a relative protocol pathname may have been used. In these instances, QEMU may lack the information to be able to make the correct choice, but the user or management layer most likely does have that knowledge. With this extension to the block-commit api, the user is able to change the backing file of the overlay image as part of the block-commit operation. This allows the change to be 'safe', in the sense that if the attempt to write the overlay image metadata fails, then the block-commit operation returns failure, without disrupting the guest. If the commit top is the active layer, then specifying the backing file string will be treated as an error (there is no overlay image to modify in that case). If a backing file string is not specified in the command, the backing file string to use is determined in the same manner as it was previously. Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Jeff Cody <jcody@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
5a6684d2b9
commit
54e2690090
8
block.c
8
block.c
@ -2555,12 +2555,15 @@ typedef struct BlkIntermediateStates {
|
|||||||
*
|
*
|
||||||
* base <- active
|
* base <- active
|
||||||
*
|
*
|
||||||
|
* If backing_file_str is non-NULL, it will be used when modifying top's
|
||||||
|
* overlay image metadata.
|
||||||
|
*
|
||||||
* Error conditions:
|
* Error conditions:
|
||||||
* if active == top, that is considered an error
|
* if active == top, that is considered an error
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
||||||
BlockDriverState *base)
|
BlockDriverState *base, const char *backing_file_str)
|
||||||
{
|
{
|
||||||
BlockDriverState *intermediate;
|
BlockDriverState *intermediate;
|
||||||
BlockDriverState *base_bs = NULL;
|
BlockDriverState *base_bs = NULL;
|
||||||
@ -2612,7 +2615,8 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* success - we can delete the intermediate states, and link top->base */
|
/* success - we can delete the intermediate states, and link top->base */
|
||||||
ret = bdrv_change_backing_file(new_top_bs, base_bs->filename,
|
backing_file_str = backing_file_str ? backing_file_str : base_bs->filename;
|
||||||
|
ret = bdrv_change_backing_file(new_top_bs, backing_file_str,
|
||||||
base_bs->drv ? base_bs->drv->format_name : "");
|
base_bs->drv ? base_bs->drv->format_name : "");
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -37,6 +37,7 @@ typedef struct CommitBlockJob {
|
|||||||
BlockdevOnError on_error;
|
BlockdevOnError on_error;
|
||||||
int base_flags;
|
int base_flags;
|
||||||
int orig_overlay_flags;
|
int orig_overlay_flags;
|
||||||
|
char *backing_file_str;
|
||||||
} CommitBlockJob;
|
} CommitBlockJob;
|
||||||
|
|
||||||
static int coroutine_fn commit_populate(BlockDriverState *bs,
|
static int coroutine_fn commit_populate(BlockDriverState *bs,
|
||||||
@ -141,7 +142,7 @@ wait:
|
|||||||
|
|
||||||
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
|
if (!block_job_is_cancelled(&s->common) && sector_num == end) {
|
||||||
/* success */
|
/* success */
|
||||||
ret = bdrv_drop_intermediate(active, top, base);
|
ret = bdrv_drop_intermediate(active, top, base, s->backing_file_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_free_buf:
|
exit_free_buf:
|
||||||
@ -158,7 +159,7 @@ exit_restore_reopen:
|
|||||||
if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
||||||
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
||||||
}
|
}
|
||||||
|
g_free(s->backing_file_str);
|
||||||
block_job_completed(&s->common, ret);
|
block_job_completed(&s->common, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +183,7 @@ static const BlockJobDriver commit_job_driver = {
|
|||||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||||
BlockDriverState *top, int64_t speed,
|
BlockDriverState *top, int64_t speed,
|
||||||
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
||||||
void *opaque, Error **errp)
|
void *opaque, const char *backing_file_str, Error **errp)
|
||||||
{
|
{
|
||||||
CommitBlockJob *s;
|
CommitBlockJob *s;
|
||||||
BlockReopenQueue *reopen_queue = NULL;
|
BlockReopenQueue *reopen_queue = NULL;
|
||||||
@ -244,6 +245,8 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
|||||||
s->base_flags = orig_base_flags;
|
s->base_flags = orig_base_flags;
|
||||||
s->orig_overlay_flags = orig_overlay_flags;
|
s->orig_overlay_flags = orig_overlay_flags;
|
||||||
|
|
||||||
|
s->backing_file_str = g_strdup(backing_file_str);
|
||||||
|
|
||||||
s->on_error = on_error;
|
s->on_error = on_error;
|
||||||
s->common.co = qemu_coroutine_create(commit_run);
|
s->common.co = qemu_coroutine_create(commit_run);
|
||||||
|
|
||||||
|
@ -1915,6 +1915,7 @@ void qmp_block_stream(const char *device, bool has_base,
|
|||||||
void qmp_block_commit(const char *device,
|
void qmp_block_commit(const char *device,
|
||||||
bool has_base, const char *base,
|
bool has_base, const char *base,
|
||||||
bool has_top, const char *top,
|
bool has_top, const char *top,
|
||||||
|
bool has_backing_file, const char *backing_file,
|
||||||
bool has_speed, int64_t speed,
|
bool has_speed, int64_t speed,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@ -1980,11 +1981,16 @@ void qmp_block_commit(const char *device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (top_bs == bs) {
|
if (top_bs == bs) {
|
||||||
|
if (has_backing_file) {
|
||||||
|
error_setg(errp, "'backing-file' specified,"
|
||||||
|
" but 'top' is the active layer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
|
commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
|
||||||
bs, &local_err);
|
bs, &local_err);
|
||||||
} else {
|
} else {
|
||||||
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
|
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
|
||||||
&local_err);
|
has_backing_file ? backing_file : NULL, &local_err);
|
||||||
}
|
}
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
@ -285,7 +285,8 @@ int bdrv_change_backing_file(BlockDriverState *bs,
|
|||||||
const char *backing_file, const char *backing_fmt);
|
const char *backing_file, const char *backing_fmt);
|
||||||
void bdrv_register(BlockDriver *bdrv);
|
void bdrv_register(BlockDriver *bdrv);
|
||||||
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
||||||
BlockDriverState *base);
|
BlockDriverState *base,
|
||||||
|
const char *backing_file_str);
|
||||||
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
|
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
|
||||||
BlockDriverState *bs);
|
BlockDriverState *bs);
|
||||||
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
|
BlockDriverState *bdrv_find_base(BlockDriverState *bs);
|
||||||
|
@ -463,13 +463,14 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
|||||||
* @on_error: The action to take upon error.
|
* @on_error: The action to take upon error.
|
||||||
* @cb: Completion function for the job.
|
* @cb: Completion function for the job.
|
||||||
* @opaque: Opaque pointer value passed to @cb.
|
* @opaque: Opaque pointer value passed to @cb.
|
||||||
|
* @backing_file_str: String to use as the backing file in @top's overlay
|
||||||
* @errp: Error object.
|
* @errp: Error object.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||||
BlockDriverState *top, int64_t speed,
|
BlockDriverState *top, int64_t speed,
|
||||||
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
|
||||||
void *opaque, Error **errp);
|
void *opaque, const char *backing_file_str, Error **errp);
|
||||||
/**
|
/**
|
||||||
* commit_active_start:
|
* commit_active_start:
|
||||||
* @bs: Active block device to be committed.
|
* @bs: Active block device to be committed.
|
||||||
|
@ -720,6 +720,23 @@
|
|||||||
# which contains the topmost data to be committed down. If
|
# which contains the topmost data to be committed down. If
|
||||||
# not specified, this is the active layer.
|
# not specified, this is the active layer.
|
||||||
#
|
#
|
||||||
|
# @backing-file: #optional The backing file string to write into the overlay
|
||||||
|
# image of 'top'. If 'top' is the active layer,
|
||||||
|
# specifying a backing file string is an error. This
|
||||||
|
# filename is not validated.
|
||||||
|
#
|
||||||
|
# If a pathname string is such that it cannot be
|
||||||
|
# resolved by QEMU, that means that subsequent QMP or
|
||||||
|
# HMP commands must use node-names for the image in
|
||||||
|
# question, as filename lookup methods will fail.
|
||||||
|
#
|
||||||
|
# If not specified, QEMU will automatically determine
|
||||||
|
# the backing file string to use, or error out if
|
||||||
|
# there is no obvious choice. Care should be taken
|
||||||
|
# when specifying the string, to specify a valid
|
||||||
|
# filename or protocol.
|
||||||
|
# (Since 2.1)
|
||||||
|
#
|
||||||
# If top == base, that is an error.
|
# If top == base, that is an error.
|
||||||
# If top == active, the job will not be completed by itself,
|
# If top == active, the job will not be completed by itself,
|
||||||
# user needs to complete the job with the block-job-complete
|
# user needs to complete the job with the block-job-complete
|
||||||
@ -732,7 +749,6 @@
|
|||||||
# size of the smaller top, you can safely truncate it
|
# size of the smaller top, you can safely truncate it
|
||||||
# yourself once the commit operation successfully completes.
|
# yourself once the commit operation successfully completes.
|
||||||
#
|
#
|
||||||
#
|
|
||||||
# @speed: #optional the maximum speed, in bytes per second
|
# @speed: #optional the maximum speed, in bytes per second
|
||||||
#
|
#
|
||||||
# Returns: Nothing on success
|
# Returns: Nothing on success
|
||||||
@ -747,7 +763,7 @@
|
|||||||
##
|
##
|
||||||
{ 'command': 'block-commit',
|
{ 'command': 'block-commit',
|
||||||
'data': { 'device': 'str', '*base': 'str', '*top': 'str',
|
'data': { 'device': 'str', '*base': 'str', '*top': 'str',
|
||||||
'*speed': 'int' } }
|
'*backing-file': 'str', '*speed': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @drive-backup
|
# @drive-backup
|
||||||
|
@ -985,7 +985,7 @@ EQMP
|
|||||||
|
|
||||||
{
|
{
|
||||||
.name = "block-commit",
|
.name = "block-commit",
|
||||||
.args_type = "device:B,base:s?,top:s?,speed:o?",
|
.args_type = "device:B,base:s?,top:s?,backing-file:s?,speed:o?",
|
||||||
.mhandler.cmd_new = qmp_marshal_input_block_commit,
|
.mhandler.cmd_new = qmp_marshal_input_block_commit,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1006,6 +1006,23 @@ Arguments:
|
|||||||
which contains the topmost data to be committed down. If
|
which contains the topmost data to be committed down. If
|
||||||
not specified, this is the active layer. (json-string, optional)
|
not specified, this is the active layer. (json-string, optional)
|
||||||
|
|
||||||
|
- backing-file: The backing file string to write into the overlay
|
||||||
|
image of 'top'. If 'top' is the active layer,
|
||||||
|
specifying a backing file string is an error. This
|
||||||
|
filename is not validated.
|
||||||
|
|
||||||
|
If a pathname string is such that it cannot be
|
||||||
|
resolved by QEMU, that means that subsequent QMP or
|
||||||
|
HMP commands must use node-names for the image in
|
||||||
|
question, as filename lookup methods will fail.
|
||||||
|
|
||||||
|
If not specified, QEMU will automatically determine
|
||||||
|
the backing file string to use, or error out if
|
||||||
|
there is no obvious choice. Care should be taken
|
||||||
|
when specifying the string, to specify a valid
|
||||||
|
filename or protocol.
|
||||||
|
(json-string, optional) (Since 2.1)
|
||||||
|
|
||||||
If top == base, that is an error.
|
If top == base, that is an error.
|
||||||
If top == active, the job will not be completed by itself,
|
If top == active, the job will not be completed by itself,
|
||||||
user needs to complete the job with the block-job-complete
|
user needs to complete the job with the block-job-complete
|
||||||
|
Loading…
Reference in New Issue
Block a user