X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=blockjob.c;h=69126af97fe82e1cb4bd72a5a424fdddeb4287ce;hb=c5e737e5fbb8e48f9371764cc156ed15f4047a00;hp=422851fde5da5eb929841e6500957909a4f35e67;hpb=23d402d42beb975b97303beb3d9d4f60f4a1a883;p=mirror_qemu.git diff --git a/blockjob.c b/blockjob.c index 422851fde5..69126af97f 100644 --- a/blockjob.c +++ b/blockjob.c @@ -25,9 +25,8 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "trace.h" #include "block/block.h" -#include "block/blockjob.h" +#include "block/blockjob_int.h" #include "block/block_int.h" #include "sysemu/block-backend.h" #include "qapi/qmp/qerror.h" @@ -38,6 +37,9 @@ #include "qemu/timer.h" #include "qapi-event.h" +static void block_job_event_cancelled(BlockJob *job); +static void block_job_event_completed(BlockJob *job, const char *msg); + /* Transactional group of block jobs */ struct BlockJobTxn { @@ -53,6 +55,19 @@ struct BlockJobTxn { static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); +static char *child_job_get_parent_desc(BdrvChild *c) +{ + BlockJob *job = c->opaque; + return g_strdup_printf("%s job '%s'", + BlockJobType_lookup[job->driver->job_type], + job->id); +} + +static const BdrvChildRole child_job = { + .get_parent_desc = child_job_get_parent_desc, + .stay_at_node = true, +}; + BlockJob *block_job_next(BlockJob *job) { if (!job) { @@ -66,7 +81,7 @@ BlockJob *block_job_get(const char *id) BlockJob *job; QLIST_FOREACH(job, &block_jobs, job_list) { - if (!strcmp(id, job->id)) { + if (job->id && !strcmp(id, job->id)) { return job; } } @@ -113,27 +128,51 @@ static void block_job_detach_aio_context(void *opaque) block_job_unref(job); } -void block_job_add_bdrv(BlockJob *job, BlockDriverState *bs) +void block_job_remove_all_bdrv(BlockJob *job) +{ + GSList *l; + for (l = job->nodes; l; l = l->next) { + BdrvChild *c = l->data; + bdrv_op_unblock_all(c->bs, job->blocker); + bdrv_root_unref_child(c); + } + g_slist_free(job->nodes); + job->nodes = NULL; +} + +int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + uint64_t perm, uint64_t shared_perm, Error **errp) { - job->nodes = g_slist_prepend(job->nodes, bs); + BdrvChild *c; + + c = bdrv_root_attach_child(bs, name, &child_job, perm, shared_perm, + job, errp); + 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; } void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockDriverState *bs, int64_t speed, + BlockDriverState *bs, uint64_t perm, + uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) { BlockBackend *blk; BlockJob *job; + int ret; - assert(cb); if (bs->job) { error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); return NULL; } - if (job_id == NULL) { + if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); if (!*job_id) { error_setg(errp, "An explicit job ID is required for this node"); @@ -141,23 +180,34 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } } - if (!id_wellformed(job_id)) { - error_setg(errp, "Invalid job ID '%s'", job_id); - return NULL; + if (job_id) { + if (flags & BLOCK_JOB_INTERNAL) { + error_setg(errp, "Cannot specify job ID for internal block job"); + return NULL; + } + + if (!id_wellformed(job_id)) { + error_setg(errp, "Invalid job ID '%s'", job_id); + return NULL; + } + + if (block_job_get(job_id)) { + error_setg(errp, "Job ID '%s' already in use", job_id); + return NULL; + } } - if (block_job_get(job_id)) { - error_setg(errp, "Job ID '%s' already in use", job_id); + blk = blk_new(perm, shared_perm); + ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { + blk_unref(blk); return NULL; } - blk = blk_new(); - blk_insert_bs(blk, bs); - job = g_malloc0(driver->instance_size); error_setg(&job->blocker, "block device is in use by block job: %s", BlockJobType_lookup[driver->job_type]); - block_job_add_bdrv(job, bs); + block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); job->driver = driver; @@ -165,7 +215,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->blk = blk; job->cb = cb; job->opaque = opaque; - job->busy = true; + job->busy = false; + job->paused = true; + job->pause_count = 1; job->refcnt = 1; bs->job = job; @@ -188,6 +240,28 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return job; } +bool block_job_is_internal(BlockJob *job) +{ + return (job->id == NULL); +} + +static bool block_job_started(BlockJob *job) +{ + return job->co; +} + +void block_job_start(BlockJob *job) +{ + assert(job && !block_job_started(job) && job->paused && + !job->busy && job->driver->start); + job->co = qemu_coroutine_create(job->driver->start, job); + if (--job->pause_count == 0) { + job->paused = false; + job->busy = true; + qemu_coroutine_enter(job->co); + } +} + void block_job_ref(BlockJob *job) { ++job->refcnt; @@ -196,15 +270,9 @@ void block_job_ref(BlockJob *job) void block_job_unref(BlockJob *job) { if (--job->refcnt == 0) { - GSList *l; BlockDriverState *bs = blk_bs(job->blk); bs->job = NULL; - for (l = job->nodes; l; l = l->next) { - bs = l->data; - bdrv_op_unblock_all(bs, job->blocker); - bdrv_unref(bs); - } - g_slist_free(job->nodes); + block_job_remove_all_bdrv(job); blk_remove_aio_context_notifier(job->blk, block_job_attached_aio_context, block_job_detach_aio_context, job); @@ -227,8 +295,29 @@ static void block_job_completed_single(BlockJob *job) job->driver->abort(job); } } - job->cb(job->opaque, job->ret); + if (job->driver->clean) { + job->driver->clean(job); + } + + if (job->cb) { + job->cb(job->opaque, job->ret); + } + + /* Emit events only if we actually started */ + if (block_job_started(job)) { + if (block_job_is_cancelled(job)) { + block_job_event_cancelled(job); + } else { + const char *msg = NULL; + if (job->ret < 0) { + msg = strerror(-job->ret); + } + block_job_event_completed(job, msg); + } + } + if (job->txn) { + QLIST_REMOVE(job, txn_list); block_job_txn_unref(job->txn); } block_job_unref(job); @@ -330,7 +419,10 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) void block_job_complete(BlockJob *job, Error **errp) { - if (job->pause_count || job->cancelled || !job->driver->complete) { + /* Should not be reachable via external interface for internal jobs */ + assert(job->id); + if (job->pause_count || job->cancelled || + !block_job_started(job) || !job->driver->complete) { error_setg(errp, "The active block job '%s' cannot be completed", job->id); return; @@ -344,13 +436,26 @@ void block_job_pause(BlockJob *job) job->pause_count++; } +void block_job_user_pause(BlockJob *job) +{ + job->user_paused = true; + block_job_pause(job); +} + static bool block_job_should_pause(BlockJob *job) { return job->pause_count > 0; } +bool block_job_user_paused(BlockJob *job) +{ + return job ? job->user_paused : 0; +} + void coroutine_fn block_job_pause_point(BlockJob *job) { + assert(job && block_job_started(job)); + if (!block_job_should_pause(job)) { return; } @@ -385,6 +490,14 @@ void block_job_resume(BlockJob *job) block_job_enter(job); } +void block_job_user_resume(BlockJob *job) +{ + if (job && job->user_paused && job->pause_count > 0) { + job->user_paused = false; + block_job_resume(job); + } +} + void block_job_enter(BlockJob *job) { if (job->co && !job->busy) { @@ -394,9 +507,13 @@ void block_job_enter(BlockJob *job) void block_job_cancel(BlockJob *job) { - job->cancelled = true; - block_job_iostatus_reset(job); - block_job_enter(job); + if (block_job_started(job)) { + job->cancelled = true; + block_job_iostatus_reset(job); + block_job_enter(job); + } else { + block_job_completed(job, -ECANCELED); + } } bool block_job_is_cancelled(BlockJob *job) @@ -510,9 +627,15 @@ void block_job_yield(BlockJob *job) block_job_pause_point(job); } -BlockJobInfo *block_job_query(BlockJob *job) +BlockJobInfo *block_job_query(BlockJob *job, Error **errp) { - BlockJobInfo *info = g_new0(BlockJobInfo, 1); + BlockJobInfo *info; + + if (block_job_is_internal(job)) { + error_setg(errp, "Cannot query QEMU internal jobs"); + return NULL; + } + info = g_new0(BlockJobInfo, 1); info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]); info->device = g_strdup(job->id); info->len = job->len; @@ -533,8 +656,12 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } -void block_job_event_cancelled(BlockJob *job) +static void block_job_event_cancelled(BlockJob *job) { + if (block_job_is_internal(job)) { + return; + } + qapi_event_send_block_job_cancelled(job->driver->job_type, job->id, job->len, @@ -543,8 +670,12 @@ void block_job_event_cancelled(BlockJob *job) &error_abort); } -void block_job_event_completed(BlockJob *job, const char *msg) +static void block_job_event_completed(BlockJob *job, const char *msg) { + if (block_job_is_internal(job)) { + return; + } + qapi_event_send_block_job_completed(job->driver->job_type, job->id, job->len, @@ -559,6 +690,10 @@ void block_job_event_ready(BlockJob *job) { job->ready = true; + if (block_job_is_internal(job)) { + return; + } + qapi_event_send_block_job_ready(job->driver->job_type, job->id, job->len, @@ -589,14 +724,15 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, default: abort(); } - qapi_event_send_block_job_error(job->id, - is_read ? IO_OPERATION_TYPE_READ : - IO_OPERATION_TYPE_WRITE, - action, &error_abort); + if (!block_job_is_internal(job)) { + qapi_event_send_block_job_error(job->id, + is_read ? IO_OPERATION_TYPE_READ : + IO_OPERATION_TYPE_WRITE, + action, &error_abort); + } if (action == BLOCK_ERROR_ACTION_STOP) { /* make the pause user visible, which will be resumed from QMP. */ - job->user_paused = true; - block_job_pause(job); + block_job_user_pause(job); block_job_iostatus_set_err(job, error); } return action;