blockjob: Allow nested pause
This patch changes block_job_pause to increase the pause counter and block_job_resume to decrease it. The counter will allow calling block_job_pause/block_job_resume unconditionally on a job when we need to suspend the IO temporarily. From now on, each block_job_resume must be paired with a block_job_pause to keep the counter balanced. The user pause from QMP or HMP will only trigger block_job_pause once until it's resumed, this is achieved by adding a user_paused flag in BlockJob. One occurrence of block_job_resume in mirror_complete is replaced with block_job_enter which does what is necessary. In block_job_cancel, the cancel flag is good enough to instruct coroutines to quit loop, so use block_job_enter to replace the unpaired block_job_resume. Upon block job IO error, user is notified about the entering to the pause state, so this pause belongs to user pause, set the flag accordingly and expect a matching QMP resume. [Extended doc comments as suggested by Paolo Bonzini <pbonzini@redhat.com>. --Stefan] Signed-off-by: Fam Zheng <famz@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Alberto Garcia <berto@igalia.com> Message-id: 1428069921-2957-2-git-send-email-famz@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
199667a8c8
commit
751ebd76e6
@ -634,7 +634,7 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->should_complete = true;
|
s->should_complete = true;
|
||||||
block_job_resume(job);
|
block_job_enter(&s->common);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const BlockJobDriver mirror_job_driver = {
|
static const BlockJobDriver mirror_job_driver = {
|
||||||
|
@ -2699,7 +2699,7 @@ void qmp_block_job_cancel(const char *device,
|
|||||||
force = false;
|
force = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job->paused && !force) {
|
if (job->user_paused && !force) {
|
||||||
error_setg(errp, "The block job for device '%s' is currently paused",
|
error_setg(errp, "The block job for device '%s' is currently paused",
|
||||||
device);
|
device);
|
||||||
goto out;
|
goto out;
|
||||||
@ -2716,10 +2716,11 @@ void qmp_block_job_pause(const char *device, Error **errp)
|
|||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
BlockJob *job = find_block_job(device, &aio_context, errp);
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
||||||
|
|
||||||
if (!job) {
|
if (!job || job->user_paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
job->user_paused = true;
|
||||||
trace_qmp_block_job_pause(job);
|
trace_qmp_block_job_pause(job);
|
||||||
block_job_pause(job);
|
block_job_pause(job);
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
@ -2730,10 +2731,11 @@ void qmp_block_job_resume(const char *device, Error **errp)
|
|||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
BlockJob *job = find_block_job(device, &aio_context, errp);
|
BlockJob *job = find_block_job(device, &aio_context, errp);
|
||||||
|
|
||||||
if (!job) {
|
if (!job || !job->user_paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
job->user_paused = false;
|
||||||
trace_qmp_block_job_resume(job);
|
trace_qmp_block_job_resume(job);
|
||||||
block_job_resume(job);
|
block_job_resume(job);
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
|
23
blockjob.c
23
blockjob.c
@ -107,7 +107,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||||||
|
|
||||||
void block_job_complete(BlockJob *job, Error **errp)
|
void block_job_complete(BlockJob *job, Error **errp)
|
||||||
{
|
{
|
||||||
if (job->paused || job->cancelled || !job->driver->complete) {
|
if (job->pause_count || job->cancelled || !job->driver->complete) {
|
||||||
error_set(errp, QERR_BLOCK_JOB_NOT_READY,
|
error_set(errp, QERR_BLOCK_JOB_NOT_READY,
|
||||||
bdrv_get_device_name(job->bs));
|
bdrv_get_device_name(job->bs));
|
||||||
return;
|
return;
|
||||||
@ -118,17 +118,26 @@ void block_job_complete(BlockJob *job, Error **errp)
|
|||||||
|
|
||||||
void block_job_pause(BlockJob *job)
|
void block_job_pause(BlockJob *job)
|
||||||
{
|
{
|
||||||
job->paused = true;
|
job->pause_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool block_job_is_paused(BlockJob *job)
|
bool block_job_is_paused(BlockJob *job)
|
||||||
{
|
{
|
||||||
return job->paused;
|
return job->pause_count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_job_resume(BlockJob *job)
|
void block_job_resume(BlockJob *job)
|
||||||
{
|
{
|
||||||
job->paused = false;
|
assert(job->pause_count > 0);
|
||||||
|
job->pause_count--;
|
||||||
|
if (job->pause_count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
block_job_enter(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
void block_job_enter(BlockJob *job)
|
||||||
|
{
|
||||||
block_job_iostatus_reset(job);
|
block_job_iostatus_reset(job);
|
||||||
if (job->co && !job->busy) {
|
if (job->co && !job->busy) {
|
||||||
qemu_coroutine_enter(job->co, NULL);
|
qemu_coroutine_enter(job->co, NULL);
|
||||||
@ -138,7 +147,7 @@ void block_job_resume(BlockJob *job)
|
|||||||
void block_job_cancel(BlockJob *job)
|
void block_job_cancel(BlockJob *job)
|
||||||
{
|
{
|
||||||
job->cancelled = true;
|
job->cancelled = true;
|
||||||
block_job_resume(job);
|
block_job_enter(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool block_job_is_cancelled(BlockJob *job)
|
bool block_job_is_cancelled(BlockJob *job)
|
||||||
@ -258,7 +267,7 @@ BlockJobInfo *block_job_query(BlockJob *job)
|
|||||||
info->device = g_strdup(bdrv_get_device_name(job->bs));
|
info->device = g_strdup(bdrv_get_device_name(job->bs));
|
||||||
info->len = job->len;
|
info->len = job->len;
|
||||||
info->busy = job->busy;
|
info->busy = job->busy;
|
||||||
info->paused = job->paused;
|
info->paused = job->pause_count > 0;
|
||||||
info->offset = job->offset;
|
info->offset = job->offset;
|
||||||
info->speed = job->speed;
|
info->speed = job->speed;
|
||||||
info->io_status = job->iostatus;
|
info->io_status = job->iostatus;
|
||||||
@ -335,6 +344,8 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
|
|||||||
IO_OPERATION_TYPE_WRITE,
|
IO_OPERATION_TYPE_WRITE,
|
||||||
action, &error_abort);
|
action, &error_abort);
|
||||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||||
|
/* make the pause user visible, which will be resumed from QMP. */
|
||||||
|
job->user_paused = true;
|
||||||
block_job_pause(job);
|
block_job_pause(job);
|
||||||
block_job_iostatus_set_err(job, error);
|
block_job_iostatus_set_err(job, error);
|
||||||
if (bs != job->bs) {
|
if (bs != job->bs) {
|
||||||
|
@ -79,10 +79,16 @@ struct BlockJob {
|
|||||||
bool cancelled;
|
bool cancelled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true if the job is either paused, or will pause itself
|
* Counter for pause request. If non-zero, the block job is either paused,
|
||||||
* as soon as possible (if busy == true).
|
* or if busy == true will pause itself as soon as possible.
|
||||||
*/
|
*/
|
||||||
bool paused;
|
int pause_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true if the job is paused by user. Can be unpaused with the
|
||||||
|
* block-job-resume QMP command.
|
||||||
|
*/
|
||||||
|
bool user_paused;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to false by the job while it is in a quiescent state, where
|
* Set to false by the job while it is in a quiescent state, where
|
||||||
@ -225,10 +231,18 @@ void block_job_pause(BlockJob *job);
|
|||||||
* block_job_resume:
|
* block_job_resume:
|
||||||
* @job: The job to be resumed.
|
* @job: The job to be resumed.
|
||||||
*
|
*
|
||||||
* Resume the specified job.
|
* Resume the specified job. Must be paired with a preceding block_job_pause.
|
||||||
*/
|
*/
|
||||||
void block_job_resume(BlockJob *job);
|
void block_job_resume(BlockJob *job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* block_job_enter:
|
||||||
|
* @job: The job to enter.
|
||||||
|
*
|
||||||
|
* Continue the specified job by entering the coroutine.
|
||||||
|
*/
|
||||||
|
void block_job_enter(BlockJob *job);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* block_job_event_cancelled:
|
* block_job_event_cancelled:
|
||||||
* @job: The job whose information is requested.
|
* @job: The job whose information is requested.
|
||||||
|
Loading…
Reference in New Issue
Block a user