X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=blockjob.c;h=207e8c7fd9167755da7e9221c2fb81c8c91a2e0e;hb=415fa2fe91e2a49fe8d56d6aacc8f8db82c74775;hp=9ca942ba01673c69a1879fd4a898e3662eeb4104;hpb=293c76cb48332d2bbd70bd975dc60ee3650b3170;p=mirror_qemu.git diff --git a/blockjob.c b/blockjob.c index 9ca942ba01..207e8c7fd9 100644 --- a/blockjob.c +++ b/blockjob.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "block/block.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -34,6 +33,7 @@ #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" #include "qemu/coroutine.h" +#include "qemu/main-loop.h" #include "qemu/timer.h" /* @@ -84,26 +84,12 @@ BlockJob *block_job_get(const char *id) void block_job_free(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); - BlockDriverState *bs = blk_bs(bjob->blk); - bs->job = NULL; block_job_remove_all_bdrv(bjob); blk_unref(bjob->blk); error_free(bjob->blocker); } -void block_job_drain(Job *job) -{ - BlockJob *bjob = container_of(job, BlockJob, job); - const JobDriver *drv = job->driver; - BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); - - blk_drain(bjob->blk); - if (bjdrv->drain) { - bjdrv->drain(bjob); - } -} - static char *child_job_get_parent_desc(BdrvChild *c) { BlockJob *job = c->opaque; @@ -138,7 +124,7 @@ static bool child_job_drained_poll(BdrvChild *c) } } -static void child_job_drained_end(BdrvChild *c) +static void child_job_drained_end(BdrvChild *c, int *drained_end_counter) { BlockJob *job = c->opaque; job_resume(&job->job); @@ -177,7 +163,7 @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx, job->job.aio_context = ctx; } -static const BdrvChildRole child_job = { +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, @@ -189,29 +175,63 @@ static const BdrvChildRole child_job = { void block_job_remove_all_bdrv(BlockJob *job) { - GSList *l; - for (l = job->nodes; l; l = l->next) { + /* + * bdrv_root_unref_child() may reach child_job_[can_]set_aio_ctx(), + * which will also traverse job->nodes, so consume the list one by + * one to make sure that such a concurrent access does not attempt + * to process an already freed BdrvChild. + */ + while (job->nodes) { + GSList *l = job->nodes; BdrvChild *c = l->data; + + job->nodes = l->next; + bdrv_op_unblock_all(c->bs, job->blocker); bdrv_root_unref_child(c); + + g_slist_free_1(l); } - g_slist_free(job->nodes); - job->nodes = NULL; +} + +bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) +{ + GSList *el; + + for (el = job->nodes; el; el = el->next) { + BdrvChild *c = el->data; + if (c->bs == bs) { + return true; + } + } + + return false; } int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) { BdrvChild *c; + bool need_context_ops; + + bdrv_ref(bs); + + need_context_ops = bdrv_get_aio_context(bs) != job->job.aio_context; - c = bdrv_root_attach_child(bs, name, &child_job, perm, shared_perm, - job, errp); + 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, 0, + job->job.aio_context, 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 -EPERM; } job->nodes = g_slist_prepend(job->nodes, c); - bdrv_ref(bs); bdrv_op_block_all(bs, job->blocker); return 0; @@ -238,27 +258,36 @@ static bool job_timer_pending(Job *job) 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; + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { + return false; } if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed", + "a non-negative value"); + 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) @@ -281,18 +310,22 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) 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 = job->job.progress.current; + info->len = job->job.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; } @@ -314,8 +347,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) qapi_event_send_block_job_cancelled(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed); } @@ -329,13 +362,13 @@ static void block_job_event_completed(Notifier *n, void *opaque) } if (job->job.ret < 0) { - msg = strerror(-job->job.ret); + msg = error_get_pretty(job->job.err); } qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed, !!msg, msg); @@ -363,8 +396,8 @@ static void block_job_event_ready(Notifier *n, void *opaque) qapi_event_send_block_job_ready(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed); } @@ -381,21 +414,13 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, { BlockBackend *blk; BlockJob *job; - int ret; - - if (bs->job) { - error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); - return NULL; - } if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); } - blk = blk_new(perm, shared_perm); - ret = blk_insert_bs(blk, bs, errp); - if (ret < 0) { - blk_unref(blk); + blk = blk_new_with_bs(bs, perm, shared_perm, errp); + if (!blk) { return NULL; } @@ -409,7 +434,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, 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->blk = blk; @@ -430,20 +454,18 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, 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); - bs->job = job; 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); /* Only set speed when necessary to avoid NotSupported error */ if (speed != 0) { - Error *local_err = NULL; - - block_job_set_speed(job, speed, &local_err); - if (local_err) { + if (!block_job_set_speed(job, speed, errp)) { job_early_fail(&job->job); - error_propagate(errp, local_err); return NULL; } }