diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2012-09-28 17:22:50 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2012-09-28 19:14:32 +0200 |
commit | 8acc72a4d20910d522516dab31272fe66da8da28 (patch) | |
tree | 4d521649f4d3a32eb74cf021ea12cfc1fc763e8f | |
parent | 8d65883fff22e00d70f5880a26b7a1248c59a2d8 (diff) |
block: add support for job pause/resume
Job pausing reuses the existing support for cancellable sleeps. A pause
happens at the next sleeping point and lasts until the coroutine is
re-entered explicitly. Cancellation was already doing a forced resume,
so implement it explicitly in terms of resume.
Paused jobs cannot be canceled without first resuming them. This ensures
that I/O errors are never missed by management.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-rw-r--r-- | blockdev.c | 4 | ||||
-rw-r--r-- | blockjob.c | 35 | ||||
-rw-r--r-- | blockjob.h | 31 | ||||
-rw-r--r-- | qapi-schema.json | 5 | ||||
-rw-r--r-- | qerror.h | 3 |
5 files changed, 72 insertions, 6 deletions
diff --git a/blockdev.c b/blockdev.c index 9a98ce9c89..612dd71f2d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1221,6 +1221,10 @@ void qmp_block_job_cancel(const char *device, Error **errp) error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device); return; } + if (job->paused) { + error_set(errp, QERR_BLOCK_JOB_PAUSED, device); + return; + } trace_qmp_block_job_cancel(job); block_job_cancel(job); diff --git a/blockjob.c b/blockjob.c index 64c9d2dda1..8219f73979 100644 --- a/blockjob.c +++ b/blockjob.c @@ -99,14 +99,30 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) job->speed = speed; } -void block_job_cancel(BlockJob *job) +void block_job_pause(BlockJob *job) { - job->cancelled = true; + job->paused = true; +} + +bool block_job_is_paused(BlockJob *job) +{ + return job->paused; +} + +void block_job_resume(BlockJob *job) +{ + job->paused = false; if (job->co && !job->busy) { qemu_coroutine_enter(job->co, NULL); } } +void block_job_cancel(BlockJob *job) +{ + job->cancelled = true; + block_job_resume(job); +} + bool block_job_is_cancelled(BlockJob *job) { return job->cancelled; @@ -154,12 +170,20 @@ int block_job_cancel_sync(BlockJob *job) void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns) { + assert(job->busy); + /* Check cancellation *before* setting busy = false, too! */ - if (!block_job_is_cancelled(job)) { - job->busy = false; + if (block_job_is_cancelled(job)) { + return; + } + + job->busy = false; + if (block_job_is_paused(job)) { + qemu_coroutine_yield(); + } else { co_sleep_ns(clock, ns); - job->busy = true; } + job->busy = true; } BlockJobInfo *block_job_query(BlockJob *job) @@ -169,6 +193,7 @@ BlockJobInfo *block_job_query(BlockJob *job) info->device = g_strdup(bdrv_get_device_name(job->bs)); info->len = job->len; info->busy = job->busy; + info->paused = job->paused; info->offset = job->offset; info->speed = job->speed; return info; diff --git a/blockjob.h b/blockjob.h index f3d8d58ce3..ece5afa75b 100644 --- a/blockjob.h +++ b/blockjob.h @@ -70,6 +70,12 @@ struct BlockJob { bool cancelled; /** + * Set to true if the job is either paused, or will pause itself + * as soon as possible (if busy == true). + */ + bool paused; + + /** * Set to false by the job while it is in a quiescent state, where * no I/O is pending and the job has yielded on any condition * that is not detected by #qemu_aio_wait, such as a timer. @@ -171,6 +177,31 @@ bool block_job_is_cancelled(BlockJob *job); BlockJobInfo *block_job_query(BlockJob *job); /** + * block_job_pause: + * @job: The job to be paused. + * + * Asynchronously pause the specified job. + */ +void block_job_pause(BlockJob *job); + +/** + * block_job_resume: + * @job: The job to be resumed. + * + * Resume the specified job. + */ +void block_job_resume(BlockJob *job); + +/** + * block_job_is_paused: + * @job: The job being queried. + * + * Returns whether the job is currently paused, or will pause + * as soon as it reaches a sleeping point. + */ +bool block_job_is_paused(BlockJob *job); + +/** * block_job_cancel_sync: * @job: The job to be canceled. * diff --git a/qapi-schema.json b/qapi-schema.json index 6fc6edaa2d..86a6c7fe93 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1101,6 +1101,9 @@ # @busy: false if the job is known to be in a quiescent state, with # no pending I/O. Since 1.3. # +# @paused: whether the job is paused or, if @busy is true, will +# pause itself as soon as possible. Since 1.3. +# # @offset: the current progress value # # @speed: the rate limit, bytes per second @@ -1109,7 +1112,7 @@ ## { 'type': 'BlockJobInfo', 'data': {'type': 'str', 'device': 'str', 'len': 'int', - 'offset': 'int', 'busy': 'bool', 'speed': 'int'} } + 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int'} } ## # @query-block-jobs: @@ -51,6 +51,9 @@ void assert_no_error(Error *err); #define QERR_BLOCK_JOB_NOT_ACTIVE \ ERROR_CLASS_DEVICE_NOT_ACTIVE, "No active block job on device '%s'" +#define QERR_BLOCK_JOB_PAUSED \ + ERROR_CLASS_GENERIC_ERROR, "The block job for device '%s' is currently paused" + #define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \ ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'" |