BlockJob *block_job_next(BlockJob *bjob)
{
Job *job = bjob ? &bjob->job : NULL;
+ GLOBAL_STATE_CODE();
do {
job = job_next(job);
BlockJob *block_job_get(const char *id)
{
Job *job = job_get(id);
+ GLOBAL_STATE_CODE();
if (job && is_block_job(job)) {
return container_of(job, BlockJob, job);
void block_job_free(Job *job)
{
BlockJob *bjob = container_of(job, BlockJob, job);
+ GLOBAL_STATE_CODE();
block_job_remove_all_bdrv(bjob);
- blk_unref(bjob->blk);
+ ratelimit_destroy(&bjob->limit);
error_free(bjob->blocker);
}
job->job.aio_context = ctx;
}
-static const BdrvChildRole child_job = {
+static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
+{
+ BlockJob *job = c->opaque;
+
+ return job->job.aio_context;
+}
+
+static const BdrvChildClass child_job = {
.get_parent_desc = child_job_get_parent_desc,
.drained_begin = child_job_drained_begin,
.drained_poll = child_job_drained_poll,
.can_set_aio_ctx = child_job_can_set_aio_ctx,
.set_aio_ctx = child_job_set_aio_ctx,
.stay_at_node = true,
+ .get_parent_aio_context = child_job_get_parent_aio_context,
};
void block_job_remove_all_bdrv(BlockJob *job)
{
+ GLOBAL_STATE_CODE();
/*
* bdrv_root_unref_child() may reach child_job_[can_]set_aio_ctx(),
* which will also traverse job->nodes, so consume the list one by
bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs)
{
GSList *el;
+ GLOBAL_STATE_CODE();
for (el = job->nodes; el; el = el->next) {
BdrvChild *c = el->data;
uint64_t perm, uint64_t shared_perm, Error **errp)
{
BdrvChild *c;
+ bool need_context_ops;
+ GLOBAL_STATE_CODE();
bdrv_ref(bs);
- if (job->job.aio_context != qemu_get_aio_context()) {
+
+ need_context_ops = bdrv_get_aio_context(bs) != job->job.aio_context;
+
+ if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
aio_context_release(job->job.aio_context);
}
- c = bdrv_root_attach_child(bs, name, &child_job, job->job.aio_context,
- perm, shared_perm, job, errp);
- if (job->job.aio_context != qemu_get_aio_context()) {
+ c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job,
+ errp);
+ if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
aio_context_acquire(job->job.aio_context);
}
if (c == NULL) {
return timer_pending(&job->sleep_timer);
}
-void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
+bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
+ const BlockJobDriver *drv = block_job_driver(job);
int64_t old_speed = job->speed;
- if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
- return;
+ GLOBAL_STATE_CODE();
+
+ if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) {
+ return false;
}
if (speed < 0) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed",
"a non-negative value");
- return;
+ return false;
}
ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME);
job->speed = speed;
+
+ if (drv->set_speed) {
+ drv->set_speed(job, speed);
+ }
+
if (speed && speed <= old_speed) {
- return;
+ return true;
}
/* kick only if a timer is pending */
job_enter_cond(&job->job, job_timer_pending);
+
+ return true;
}
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
{
- if (!job->speed) {
- return 0;
- }
-
+ IO_CODE();
return ratelimit_calculate_delay(&job->limit, n);
}
BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
{
BlockJobInfo *info;
+ uint64_t progress_current, progress_total;
+
+ GLOBAL_STATE_CODE();
if (block_job_is_internal(job)) {
error_setg(errp, "Cannot query QEMU internal jobs");
return NULL;
}
+
+ progress_get_snapshot(&job->job.progress, &progress_current,
+ &progress_total);
+
info = g_new0(BlockJobInfo, 1);
info->type = g_strdup(job_type_str(&job->job));
info->device = g_strdup(job->job.id);
- info->busy = atomic_read(&job->job.busy);
+ info->busy = qatomic_read(&job->job.busy);
info->paused = job->job.pause_count > 0;
- info->offset = job->job.progress_current;
- info->len = job->job.progress_total;
+ info->offset = progress_current;
+ info->len = progress_total;
info->speed = job->speed;
info->io_status = job->iostatus;
info->ready = job_is_ready(&job->job),
info->status = job->job.status;
info->auto_finalize = job->job.auto_finalize;
info->auto_dismiss = job->job.auto_dismiss;
- info->has_error = job->job.ret != 0;
- info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
+ if (job->job.ret) {
+ info->has_error = true;
+ info->error = job->job.err ?
+ g_strdup(error_get_pretty(job->job.err)) :
+ g_strdup(strerror(-job->job.ret));
+ }
return info;
}
static void block_job_event_cancelled(Notifier *n, void *opaque)
{
BlockJob *job = opaque;
+ uint64_t progress_current, progress_total;
if (block_job_is_internal(job)) {
return;
}
+ progress_get_snapshot(&job->job.progress, &progress_current,
+ &progress_total);
+
qapi_event_send_block_job_cancelled(job_type(&job->job),
job->job.id,
- job->job.progress_total,
- job->job.progress_current,
+ progress_total,
+ progress_current,
job->speed);
}
{
BlockJob *job = opaque;
const char *msg = NULL;
+ uint64_t progress_current, progress_total;
if (block_job_is_internal(job)) {
return;
}
if (job->job.ret < 0) {
- msg = strerror(-job->job.ret);
+ msg = error_get_pretty(job->job.err);
}
+ progress_get_snapshot(&job->job.progress, &progress_current,
+ &progress_total);
+
qapi_event_send_block_job_completed(job_type(&job->job),
job->job.id,
- job->job.progress_total,
- job->job.progress_current,
+ progress_total,
+ progress_current,
job->speed,
!!msg,
msg);
static void block_job_event_ready(Notifier *n, void *opaque)
{
BlockJob *job = opaque;
+ uint64_t progress_current, progress_total;
if (block_job_is_internal(job)) {
return;
}
+ progress_get_snapshot(&job->job.progress, &progress_current,
+ &progress_total);
+
qapi_event_send_block_job_ready(job_type(&job->job),
job->job.id,
- job->job.progress_total,
- job->job.progress_current,
+ progress_total,
+ progress_current,
job->speed);
}
uint64_t shared_perm, int64_t speed, int flags,
BlockCompletionFunc *cb, void *opaque, Error **errp)
{
- BlockBackend *blk;
BlockJob *job;
int ret;
+ GLOBAL_STATE_CODE();
if (job_id == NULL && !(flags & JOB_INTERNAL)) {
job_id = bdrv_get_device_name(bs);
}
- blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm);
- ret = blk_insert_bs(blk, bs, errp);
- if (ret < 0) {
- blk_unref(blk);
- return NULL;
- }
-
- job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk),
+ job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs),
flags, cb, opaque, errp);
if (job == NULL) {
- blk_unref(blk);
return NULL;
}
assert(job->job.driver->free == &block_job_free);
assert(job->job.driver->user_resume == &block_job_user_resume);
- job->blk = blk;
+ ratelimit_init(&job->limit);
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
job->finalize_completed_notifier.notify = block_job_event_completed;
error_setg(&job->blocker, "block device is in use by block job: %s",
job_type_str(&job->job));
- block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
- bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
-
- /* Disable request queuing in the BlockBackend to avoid deadlocks on drain:
- * The job reports that it's busy until it reaches a pause point. */
- blk_set_disable_request_queuing(blk, true);
- blk_set_allow_aio_context_change(blk, true);
+ ret = block_job_add_bdrv(job, "main node", bs, perm, shared_perm, errp);
+ if (ret < 0) {
+ goto fail;
+ }
- /* Only set speed when necessary to avoid NotSupported error */
- if (speed != 0) {
- Error *local_err = NULL;
+ bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
- block_job_set_speed(job, speed, &local_err);
- if (local_err) {
- job_early_fail(&job->job);
- error_propagate(errp, local_err);
- return NULL;
- }
+ if (!block_job_set_speed(job, speed, errp)) {
+ goto fail;
}
return job;
+
+fail:
+ job_early_fail(&job->job);
+ return NULL;
}
void block_job_iostatus_reset(BlockJob *job)
{
+ GLOBAL_STATE_CODE();
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
return;
}
void block_job_user_resume(Job *job)
{
BlockJob *bjob = container_of(job, BlockJob, job);
+ GLOBAL_STATE_CODE();
block_job_iostatus_reset(bjob);
}
int is_read, int error)
{
BlockErrorAction action;
+ IO_CODE();
switch (on_err) {
case BLOCKDEV_ON_ERROR_ENOSPC:
}
return action;
}
+
+AioContext *block_job_get_aio_context(BlockJob *job)
+{
+ GLOBAL_STATE_CODE();
+ return job->job.aio_context;
+}