.job_type = JOB_TYPE_BACKUP,
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = backup_run,
.commit = backup_commit,
.abort = backup_abort,
.job_type = JOB_TYPE_COMMIT,
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = commit_run,
},
};
.job_type = JOB_TYPE_MIRROR,
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = mirror_run,
.pause = mirror_pause,
},
.job_type = JOB_TYPE_COMMIT,
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = mirror_run,
.pause = mirror_pause,
},
.free = block_job_free,
.start = stream_run,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
},
};
job_resume(&job->job);
}
-static void block_job_drain(BlockJob *job)
+void block_job_drain(Job *job)
{
- /* If job is !job->job.busy this kicks it into the next pause point. */
- block_job_enter(job);
+ BlockJob *bjob = container_of(job, BlockJob, job);
- blk_drain(job->blk);
- if (job->driver->drain) {
- job->driver->drain(job);
+ blk_drain(bjob->blk);
+ if (bjob->driver->drain) {
+ bjob->driver->drain(bjob);
}
}
job_pause(&job->job);
while (!job->job.paused && !job_is_completed(&job->job)) {
- block_job_drain(job);
+ job_drain(&job->job);
}
job->job.aio_context = NULL;
job_unref(&job->job);
return -EBUSY;
}
- /* block_job_drain calls block_job_enter, and it should be enough to
- * induce progress until the job completes or moves to the main thread.
+ /* job_drain calls job_enter, and it should be enough to induce progress
+ * until the job completes or moves to the main thread.
*/
while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
- block_job_drain(job);
+ job_drain(&job->job);
}
while (!job_is_completed(&job->job)) {
aio_poll(qemu_get_aio_context(), true);
assert(is_block_job(&job->job));
assert(job->job.driver->free == &block_job_free);
assert(job->job.driver->user_resume == &block_job_user_resume);
+ assert(job->job.driver->drain == &block_job_drain);
job->driver = driver;
job->blk = blk;
* If the callback is not NULL, it will be invoked when the job has to be
* synchronously cancelled or completed; it should drain BlockDriverStates
* as required to ensure progress.
+ *
+ * Block jobs must use the default implementation for job_driver.drain,
+ * which will in turn call this callback after doing generic block job
+ * stuff.
*/
void (*drain)(BlockJob *job);
};
*/
void block_job_user_resume(Job *job);
+/**
+ * block_job_drain:
+ * Callback to be used for JobDriver.drain in all block jobs. Drains the main
+ * block node associated with the block jobs and calls BlockJobDriver.drain for
+ * job-specific actions.
+ */
+void block_job_drain(Job *job);
+
/**
* block_job_yield:
* @job: The job that calls the function.
*/
void (*user_resume)(Job *job);
+ /*
+ * If the callback is not NULL, it will be invoked when the job has to be
+ * synchronously cancelled or completed; it should drain any activities
+ * as required to ensure progress.
+ */
+ void (*drain)(Job *job);
+
/**
* If the callback is not NULL, it will be invoked when all the jobs
* belonging to the same transaction complete; or upon this job's
*/
void job_user_resume(Job *job, Error **errp);
+/*
+ * Drain any activities as required to ensure progress. This can be called in a
+ * loop to synchronously complete a job.
+ */
+void job_drain(Job *job);
+
/**
* Get the next element from the list of block jobs after @job, or the
* first one if @job is %NULL.
job_pause_point(job);
}
+void job_drain(Job *job)
+{
+ /* If job is !busy this kicks it into the next pause point. */
+ job_enter(job);
+
+ if (job->driver->drain) {
+ job->driver->drain(job);
+ }
+}
+
+
/**
* All jobs must allow a pause point before entering their job proper. This
* ensures that jobs can be paused prior to being started, then resumed later.
.instance_size = sizeof(TestBlockJob),
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = test_job_start,
},
.complete = test_job_complete,
.instance_size = sizeof(TestBlockJob),
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = test_block_job_run,
},
};
.instance_size = sizeof(BlockJob),
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
},
};
.instance_size = sizeof(CancelJob),
.free = block_job_free,
.user_resume = block_job_user_resume,
+ .drain = block_job_drain,
.start = cancel_job_start,
},
.complete = cancel_job_complete,