block: add block-job-complete
While streaming can be dropped as soon as it progressed through the whole image, mirroring needs to be completed manually for two reasons: 1) so that management knows exactly when the VM switches to the target; 2) because for other use cases such as replication, we may leave the operation running for the whole life of the virtual machine. Add a new block job command that manually completes background operations. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
65f4632243
commit
aeae883baf
13
blockdev.c
13
blockdev.c
@ -1266,6 +1266,19 @@ void qmp_block_job_resume(const char *device, Error **errp)
|
|||||||
block_job_resume(job);
|
block_job_resume(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qmp_block_job_complete(const char *device, Error **errp)
|
||||||
|
{
|
||||||
|
BlockJob *job = find_block_job(device);
|
||||||
|
|
||||||
|
if (!job) {
|
||||||
|
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_qmp_block_job_complete(job);
|
||||||
|
block_job_complete(job, errp);
|
||||||
|
}
|
||||||
|
|
||||||
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
|
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BlockJobInfoList **prev = opaque;
|
BlockJobInfoList **prev = opaque;
|
||||||
|
10
blockjob.c
10
blockjob.c
@ -99,6 +99,16 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||||||
job->speed = speed;
|
job->speed = speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void block_job_complete(BlockJob *job, Error **errp)
|
||||||
|
{
|
||||||
|
if (job->paused || job->cancelled || !job->job_type->complete) {
|
||||||
|
error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
job->job_type->complete(job, errp);
|
||||||
|
}
|
||||||
|
|
||||||
void block_job_pause(BlockJob *job)
|
void block_job_pause(BlockJob *job)
|
||||||
{
|
{
|
||||||
job->paused = true;
|
job->paused = true;
|
||||||
|
15
blockjob.h
15
blockjob.h
@ -41,6 +41,12 @@ typedef struct BlockJobType {
|
|||||||
|
|
||||||
/** Optional callback for job types that support setting a speed limit */
|
/** Optional callback for job types that support setting a speed limit */
|
||||||
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
|
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional callback for job types whose completion must be triggered
|
||||||
|
* manually.
|
||||||
|
*/
|
||||||
|
void (*complete)(BlockJob *job, Error **errp);
|
||||||
} BlockJobType;
|
} BlockJobType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,6 +169,15 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
|
|||||||
*/
|
*/
|
||||||
void block_job_cancel(BlockJob *job);
|
void block_job_cancel(BlockJob *job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* block_job_complete:
|
||||||
|
* @job: The job to be completed.
|
||||||
|
* @errp: Error object.
|
||||||
|
*
|
||||||
|
* Asynchronously complete the specified job.
|
||||||
|
*/
|
||||||
|
void block_job_complete(BlockJob *job, Error **errp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* block_job_is_cancelled:
|
* block_job_is_cancelled:
|
||||||
* @job: The job being queried.
|
* @job: The job being queried.
|
||||||
|
@ -109,7 +109,22 @@ ETEXI
|
|||||||
STEXI
|
STEXI
|
||||||
@item block_job_cancel
|
@item block_job_cancel
|
||||||
@findex block_job_cancel
|
@findex block_job_cancel
|
||||||
Stop an active block streaming operation.
|
Stop an active background block operation (streaming, mirroring).
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "block_job_complete",
|
||||||
|
.args_type = "device:B",
|
||||||
|
.params = "device",
|
||||||
|
.help = "stop an active background block operation",
|
||||||
|
.mhandler.cmd = hmp_block_job_complete,
|
||||||
|
},
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item block_job_complete
|
||||||
|
@findex block_job_complete
|
||||||
|
Manually trigger completion of an active background block operation.
|
||||||
|
For mirroring, this will switch the device to the destination path.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
10
hmp.c
10
hmp.c
@ -990,6 +990,16 @@ void hmp_block_job_resume(Monitor *mon, const QDict *qdict)
|
|||||||
hmp_handle_error(mon, &error);
|
hmp_handle_error(mon, &error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hmp_block_job_complete(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
Error *error = NULL;
|
||||||
|
const char *device = qdict_get_str(qdict, "device");
|
||||||
|
|
||||||
|
qmp_block_job_complete(device, &error);
|
||||||
|
|
||||||
|
hmp_handle_error(mon, &error);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct MigrationStatus
|
typedef struct MigrationStatus
|
||||||
{
|
{
|
||||||
QEMUTimer *timer;
|
QEMUTimer *timer;
|
||||||
|
1
hmp.h
1
hmp.h
@ -66,6 +66,7 @@ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
|
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
|
void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_block_job_resume(Monitor *mon, const QDict *qdict);
|
void hmp_block_job_resume(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_block_job_complete(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_migrate(Monitor *mon, const QDict *qdict);
|
void hmp_migrate(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_device_del(Monitor *mon, const QDict *qdict);
|
void hmp_device_del(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
|
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
|
||||||
|
@ -2032,6 +2032,31 @@
|
|||||||
##
|
##
|
||||||
{ 'command': 'block-job-resume', 'data': { 'device': 'str' } }
|
{ 'command': 'block-job-resume', 'data': { 'device': 'str' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @block-job-complete:
|
||||||
|
#
|
||||||
|
# Manually trigger completion of an active background block operation. This
|
||||||
|
# is supported for drive mirroring, where it also switches the device to
|
||||||
|
# write to the target path only.
|
||||||
|
#
|
||||||
|
# This command completes an active background block operation synchronously.
|
||||||
|
# The ordering of this command's return with the BLOCK_JOB_COMPLETED event
|
||||||
|
# is not defined. Note that if an I/O error occurs during the processing of
|
||||||
|
# this command: 1) the command itself will fail; 2) the error will be processed
|
||||||
|
# according to the rerror/werror arguments that were specified when starting
|
||||||
|
# the operation.
|
||||||
|
#
|
||||||
|
# A cancelled or paused job cannot be completed.
|
||||||
|
#
|
||||||
|
# @device: the device name
|
||||||
|
#
|
||||||
|
# Returns: Nothing on success
|
||||||
|
# If no background operation is active on this device, DeviceNotActive
|
||||||
|
#
|
||||||
|
# Since: 1.3
|
||||||
|
##
|
||||||
|
{ 'command': 'block-job-complete', 'data': { 'device': 'str' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @ObjectTypeInfo:
|
# @ObjectTypeInfo:
|
||||||
#
|
#
|
||||||
|
3
qerror.h
3
qerror.h
@ -54,6 +54,9 @@ void assert_no_error(Error *err);
|
|||||||
#define QERR_BLOCK_JOB_PAUSED \
|
#define QERR_BLOCK_JOB_PAUSED \
|
||||||
ERROR_CLASS_GENERIC_ERROR, "The block job for device '%s' is currently paused"
|
ERROR_CLASS_GENERIC_ERROR, "The block job for device '%s' is currently paused"
|
||||||
|
|
||||||
|
#define QERR_BLOCK_JOB_NOT_READY \
|
||||||
|
ERROR_CLASS_GENERIC_ERROR, "The active block job for device '%s' cannot be completed"
|
||||||
|
|
||||||
#define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \
|
#define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \
|
||||||
ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'"
|
ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'"
|
||||||
|
|
||||||
|
@ -842,6 +842,11 @@ EQMP
|
|||||||
.args_type = "device:B",
|
.args_type = "device:B",
|
||||||
.mhandler.cmd_new = qmp_marshal_input_block_job_resume,
|
.mhandler.cmd_new = qmp_marshal_input_block_job_resume,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "block-job-complete",
|
||||||
|
.args_type = "device:B",
|
||||||
|
.mhandler.cmd_new = qmp_marshal_input_block_job_complete,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "transaction",
|
.name = "transaction",
|
||||||
.args_type = "actions:q",
|
.args_type = "actions:q",
|
||||||
|
@ -81,6 +81,7 @@ commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) "
|
|||||||
qmp_block_job_cancel(void *job) "job %p"
|
qmp_block_job_cancel(void *job) "job %p"
|
||||||
qmp_block_job_pause(void *job) "job %p"
|
qmp_block_job_pause(void *job) "job %p"
|
||||||
qmp_block_job_resume(void *job) "job %p"
|
qmp_block_job_resume(void *job) "job %p"
|
||||||
|
qmp_block_job_complete(void *job) "job %p"
|
||||||
block_job_cb(void *bs, void *job, int ret) "bs %p job %p ret %d"
|
block_job_cb(void *bs, void *job, int ret) "bs %p job %p ret %d"
|
||||||
qmp_block_stream(void *bs, void *job) "bs %p job %p"
|
qmp_block_stream(void *bs, void *job) "bs %p job %p"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user