@@ -253,8 +253,17 @@ struct JobDriver {
/**
* If the callback is not NULL, it will be invoked in job_cancel_async
+ *
+ * This function must return true if the job will be cancelled
+ * immediately without any further I/O (mandatory if @force is
+ * true), and false otherwise. This lets the generic job layer
+ * know whether a job has been truly (force-)cancelled, or whether
+ * it is just in a special completion mode (like mirror after
+ * READY).
+ * (If the callback is NULL, the job is assumed to terminate
+ * without I/O.)
*/
- void (*cancel)(Job *job, bool force);
+ bool (*cancel)(Job *job, bool force);
/** Called when the job is freed */
@@ -327,11 +327,12 @@ static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
}
}
-static void backup_cancel(Job *job, bool force)
+static bool backup_cancel(Job *job, bool force)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
bdrv_cancel_in_flight(s->target_bs);
+ return true;
}
static const BlockJobDriver backup_job_driver = {
@@ -1087,9 +1087,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job),
delay_ns);
job_sleep_ns(&s->common.job, delay_ns);
- if (job_is_cancelled(&s->common.job) &&
- (!job_is_ready(&s->common.job) || s->common.job.force_cancel))
- {
+ if (job_is_cancelled(&s->common.job) && s->common.job.force_cancel) {
break;
}
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
@@ -1102,7 +1100,7 @@ immediate_exit:
* the target is a copy of the source.
*/
assert(ret < 0 ||
- ((s->common.job.force_cancel || !job_is_ready(&s->common.job)) &&
+ (s->common.job.force_cancel &&
job_is_cancelled(&s->common.job)));
assert(need_drain);
mirror_wait_for_all_io(s);
@@ -1188,14 +1186,27 @@ static bool mirror_drained_poll(BlockJob *job)
return !!s->in_flight;
}
-static void mirror_cancel(Job *job, bool force)
+static bool mirror_cancel(Job *job, bool force)
{
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
BlockDriverState *target = blk_bs(s->target);
- if (force || !job_is_ready(job)) {
+ /*
+ * Before the job is READY, we treat any cancellation like a
+ * force-cancellation.
+ */
+ force = force || !job_is_ready(job);
+
+ if (force) {
bdrv_cancel_in_flight(target);
}
+ return force;
+}
+
+static bool commit_active_cancel(Job *job, bool force)
+{
+ /* Same as above in mirror_cancel() */
+ return force || !job_is_ready(job);
}
static const BlockJobDriver mirror_job_driver = {
@@ -1225,6 +1236,7 @@ static const BlockJobDriver commit_active_job_driver = {
.abort = mirror_abort,
.pause = mirror_pause,
.complete = mirror_complete,
+ .cancel = commit_active_cancel,
},
.drained_poll = mirror_drained_poll,
};
@@ -719,8 +719,12 @@ static int job_finalize_single(Job *job)
static void job_cancel_async(Job *job, bool force)
{
if (job->driver->cancel) {
- job->driver->cancel(job, force);
+ force = job->driver->cancel(job, force);
+ } else {
+ /* No .cancel() means the job will behave as if force-cancelled */
+ force = true;
}
+
if (job->user_paused) {
/* Do not call job_enter here, the caller will handle it. */
if (job->driver->user_resume) {