.instance_size = sizeof(BackupBlockJob),
.job_type = JOB_TYPE_BACKUP,
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = backup_run,
},
.commit = backup_commit,
.instance_size = sizeof(CommitBlockJob),
.job_type = JOB_TYPE_COMMIT,
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = commit_run,
},
};
.instance_size = sizeof(MirrorBlockJob),
.job_type = JOB_TYPE_MIRROR,
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = mirror_run,
.pause = mirror_pause,
},
.instance_size = sizeof(MirrorBlockJob),
.job_type = JOB_TYPE_COMMIT,
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = mirror_run,
.pause = mirror_pause,
},
.job_type = JOB_TYPE_STREAM,
.free = block_job_free,
.start = stream_run,
+ .user_resume = block_job_user_resume,
},
};
force = false;
}
- if (block_job_user_paused(job) && !force) {
+ if (job_user_paused(&job->job) && !force) {
error_setg(errp, "The block job for device '%s' is currently paused",
device);
goto out;
}
trace_qmp_block_job_pause(job);
- block_job_user_pause(job, errp);
+ job_user_pause(&job->job, errp);
aio_context_release(aio_context);
}
}
trace_qmp_block_job_resume(job);
- block_job_user_resume(job, errp);
+ job_user_resume(&job->job, errp);
aio_context_release(aio_context);
}
}
}
-/* Assumes the job_mutex is held */
-static bool job_timer_not_pending(Job *job)
-{
- return !timer_pending(&job->sleep_timer);
-}
-
-static void block_job_pause(BlockJob *job)
-{
- job->job.pause_count++;
-}
-
-static void block_job_resume(BlockJob *job)
-{
- assert(job->job.pause_count > 0);
- job->job.pause_count--;
- if (job->job.pause_count) {
- return;
- }
-
- /* kick only if no timer is pending */
- job_enter_cond(&job->job, job_timer_not_pending);
-}
-
static void block_job_attached_aio_context(AioContext *new_context,
void *opaque);
static void block_job_detach_aio_context(void *opaque);
job->driver->attached_aio_context(job, new_context);
}
- block_job_resume(job);
+ job_resume(&job->job);
}
static void block_job_drain(BlockJob *job)
/* In case the job terminates during aio_poll()... */
job_ref(&job->job);
- block_job_pause(job);
+ job_pause(&job->job);
while (!job->job.paused && !job->completed) {
block_job_drain(job);
static void child_job_drained_begin(BdrvChild *c)
{
BlockJob *job = c->opaque;
- block_job_pause(job);
+ job_pause(&job->job);
}
static void child_job_drained_end(BdrvChild *c)
{
BlockJob *job = c->opaque;
- block_job_resume(job);
+ job_resume(&job->job);
}
static const BdrvChildRole child_job = {
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
block_job_iostatus_reset(job);
}
- if (job->user_paused) {
+ if (job->job.user_paused) {
/* Do not call block_job_enter here, the caller will handle it. */
- job->user_paused = false;
+ job->job.user_paused = false;
job->job.pause_count--;
}
job->job.cancelled = true;
*jobptr = NULL;
}
-void block_job_user_pause(BlockJob *job, Error **errp)
-{
- if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) {
- return;
- }
- if (job->user_paused) {
- error_setg(errp, "Job is already paused");
- return;
- }
- job->user_paused = true;
- block_job_pause(job);
-}
-
-bool block_job_user_paused(BlockJob *job)
-{
- return job->user_paused;
-}
-
-void block_job_user_resume(BlockJob *job, Error **errp)
-{
- assert(job);
- if (!job->user_paused || job->job.pause_count <= 0) {
- error_setg(errp, "Can't resume a job that was not paused");
- return;
- }
- if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) {
- return;
- }
- block_job_iostatus_reset(job);
- job->user_paused = false;
- block_job_resume(job);
-}
-
void block_job_cancel(BlockJob *job, bool force)
{
if (job->job.status == JOB_STATUS_CONCLUDED) {
assert(is_block_job(&job->job));
assert(job->job.driver->free == &block_job_free);
+ assert(job->job.driver->user_resume == &block_job_user_resume);
job->driver = driver;
job->blk = blk;
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
return;
}
- assert(job->user_paused && job->job.pause_count > 0);
+ assert(job->job.user_paused && job->job.pause_count > 0);
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
}
+void block_job_user_resume(Job *job)
+{
+ BlockJob *bjob = container_of(job, BlockJob, job);
+ block_job_iostatus_reset(bjob);
+}
+
void block_job_event_ready(BlockJob *job)
{
job_state_transition(&job->job, JOB_STATUS_READY);
action, &error_abort);
}
if (action == BLOCK_ERROR_ACTION_STOP) {
- block_job_pause(job);
+ job_pause(&job->job);
/* make the pause user visible, which will be resumed from QMP. */
- job->user_paused = true;
+ job->job.user_paused = true;
block_job_iostatus_set_err(job, error);
}
return action;
*/
bool force;
- /**
- * 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 true when the job is ready to be completed.
*/
*/
BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
-/**
- * block_job_user_pause:
- * @job: The job to be paused.
- *
- * Asynchronously pause the specified job.
- * Do not allow a resume until a matching call to block_job_user_resume.
- */
-void block_job_user_pause(BlockJob *job, Error **errp);
-
-/**
- * block_job_paused:
- * @job: The job to query.
- *
- * Returns true if the job is user-paused.
- */
-bool block_job_user_paused(BlockJob *job);
-
-/**
- * block_job_user_resume:
- * @job: The job to be resumed.
- *
- * Resume the specified job.
- * Must be paired with a preceding block_job_user_pause.
- */
-void block_job_user_resume(BlockJob *job, Error **errp);
-
/**
* block_job_user_cancel:
* @job: The job to be cancelled.
*/
void block_job_free(Job *job);
+/**
+ * block_job_user_resume:
+ * Callback to be used for JobDriver.user_resume in all block jobs. Resets the
+ * iostatus when the user resumes @job.
+ */
+void block_job_user_resume(Job *job);
+
/**
* block_job_yield:
* @job: The job that calls the function.
*/
bool paused;
+ /**
+ * 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 true if the job should cancel itself. The flag must
* always be tested just before toggling the busy flag from false
*/
void coroutine_fn (*resume)(Job *job);
+ /**
+ * Called when the job is resumed by the user (i.e. user_paused becomes
+ * false). .user_resume is called before .resume.
+ */
+ void (*user_resume)(Job *job);
+
/** Called when the job is freed */
void (*free)(Job *job);
};
/** Returns whether the job is scheduled for cancellation. */
bool job_is_cancelled(Job *job);
+/**
+ * Request @job to pause at the next pause point. Must be paired with
+ * job_resume(). If the job is supposed to be resumed by user action, call
+ * job_user_pause() instead.
+ */
+void job_pause(Job *job);
+
+/** Resumes a @job paused with job_pause. */
+void job_resume(Job *job);
+
+/**
+ * Asynchronously pause the specified @job.
+ * Do not allow a resume until a matching call to job_user_resume.
+ */
+void job_user_pause(Job *job, Error **errp);
+
+/** Returns true if the job is user-paused. */
+bool job_user_paused(Job *job);
+
+/**
+ * Resume the specified @job.
+ * Must be paired with a preceding job_user_pause.
+ */
+void job_user_resume(Job *job, Error **errp);
+
/**
* Get the next element from the list of block jobs after @job, or the
* first one if @job is %NULL.
aio_co_enter(job->aio_context, job->co);
}
+/* Assumes the block_job_mutex is held */
+static bool job_timer_not_pending(Job *job)
+{
+ return !timer_pending(&job->sleep_timer);
+}
+
+void job_pause(Job *job)
+{
+ job->pause_count++;
+}
+
+void job_resume(Job *job)
+{
+ assert(job->pause_count > 0);
+ job->pause_count--;
+ if (job->pause_count) {
+ return;
+ }
+
+ /* kick only if no timer is pending */
+ job_enter_cond(job, job_timer_not_pending);
+}
+
+void job_user_pause(Job *job, Error **errp)
+{
+ if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) {
+ return;
+ }
+ if (job->user_paused) {
+ error_setg(errp, "Job is already paused");
+ return;
+ }
+ job->user_paused = true;
+ job_pause(job);
+}
+
+bool job_user_paused(Job *job)
+{
+ return job->user_paused;
+}
+
+void job_user_resume(Job *job, Error **errp)
+{
+ assert(job);
+ if (!job->user_paused || job->pause_count <= 0) {
+ error_setg(errp, "Can't resume a job that was not paused");
+ return;
+ }
+ if (job_apply_verb(job, JOB_VERB_RESUME, errp)) {
+ return;
+ }
+ if (job->driver->user_resume) {
+ job->driver->user_resume(job);
+ }
+ job->user_paused = false;
+ job_resume(job);
+}
+
+
typedef struct {
Job *job;
JobDeferToMainLoopFn *fn;
.job_driver = {
.instance_size = sizeof(TestBlockJob),
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = test_job_start,
},
.complete = test_job_complete,
.job_driver = {
.instance_size = sizeof(TestBlockJob),
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = test_block_job_run,
},
};
.job_driver = {
.instance_size = sizeof(BlockJob),
.free = block_job_free,
+ .user_resume = block_job_user_resume,
},
};
.job_driver = {
.instance_size = sizeof(CancelJob),
.free = block_job_free,
+ .user_resume = block_job_user_resume,
.start = cancel_job_start,
},
.complete = cancel_job_complete,
job_start(&job->job);
assert(job->job.status == JOB_STATUS_RUNNING);
- block_job_user_pause(job, &error_abort);
+ job_user_pause(&job->job, &error_abort);
block_job_enter(job);
assert(job->job.status == JOB_STATUS_PAUSED);
block_job_enter(job);
assert(job->job.status == JOB_STATUS_READY);
- block_job_user_pause(job, &error_abort);
+ job_user_pause(&job->job, &error_abort);
block_job_enter(job);
assert(job->job.status == JOB_STATUS_STANDBY);