X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/a589569f2f40a0454b52398035cfe7fbe44ab1e9..fb6345f452ba7cefb395389abb17d0af0e42c54b:/blockjob.c diff --git a/blockjob.c b/blockjob.c index bc63d42dc8..62bb906634 100644 --- a/blockjob.c +++ b/blockjob.c @@ -26,23 +26,24 @@ #include "config-host.h" #include "qemu-common.h" #include "trace.h" -#include "monitor/monitor.h" #include "block/block.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" #include "block/coroutine.h" #include "qmp-commands.h" #include "qemu/timer.h" +#include "qapi-event.h" void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, - int64_t speed, BlockDriverCompletionFunc *cb, + int64_t speed, BlockCompletionFunc *cb, void *opaque, Error **errp) { BlockJob *job; if (bs->job) { - error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); + error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); return NULL; } bdrv_ref(bs); @@ -50,6 +51,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, error_setg(&job->blocker, "block device is in use by block job: %s", BlockJobType_lookup[driver->job_type]); bdrv_op_block_all(bs, job->blocker); + bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); job->driver = driver; job->bs = bs; @@ -64,10 +66,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, block_job_set_speed(job, speed, &local_err); if (local_err) { - bs->job = NULL; - bdrv_op_unblock_all(bs, job->blocker); - error_free(job->blocker); - g_free(job); + block_job_release(bs); error_propagate(errp, local_err); return NULL; } @@ -75,24 +74,31 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs, return job; } -void block_job_completed(BlockJob *job, int ret) +void block_job_release(BlockDriverState *bs) { - BlockDriverState *bs = job->bs; + BlockJob *job = bs->job; - assert(bs->job == job); - job->cb(job->opaque, ret); bs->job = NULL; bdrv_op_unblock_all(bs, job->blocker); error_free(job->blocker); g_free(job); } +void block_job_completed(BlockJob *job, int ret) +{ + BlockDriverState *bs = job->bs; + + assert(bs->job == job); + job->cb(job->opaque, ret); + block_job_release(bs); +} + void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { Error *local_err = NULL; if (!job->driver->set_speed) { - error_set(errp, QERR_UNSUPPORTED); + error_setg(errp, QERR_UNSUPPORTED); return; } job->driver->set_speed(job, speed, &local_err); @@ -106,8 +112,9 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) void block_job_complete(BlockJob *job, Error **errp) { - if (job->paused || job->cancelled || !job->driver->complete) { - error_set(errp, QERR_BLOCK_JOB_NOT_READY, job->bs->device_name); + if (job->pause_count || job->cancelled || !job->driver->complete) { + error_setg(errp, QERR_BLOCK_JOB_NOT_READY, + bdrv_get_device_name(job->bs)); return; } @@ -116,17 +123,26 @@ void block_job_complete(BlockJob *job, Error **errp) void block_job_pause(BlockJob *job) { - job->paused = true; + job->pause_count++; } bool block_job_is_paused(BlockJob *job) { - return job->paused; + return job->pause_count > 0; } void block_job_resume(BlockJob *job) { - job->paused = false; + assert(job->pause_count > 0); + job->pause_count--; + if (job->pause_count) { + return; + } + block_job_enter(job); +} + +void block_job_enter(BlockJob *job) +{ block_job_iostatus_reset(job); if (job->co && !job->busy) { qemu_coroutine_enter(job->co, NULL); @@ -136,7 +152,7 @@ void block_job_resume(BlockJob *job) void block_job_cancel(BlockJob *job) { job->cancelled = true; - block_job_resume(job); + block_job_enter(job); } bool block_job_is_cancelled(BlockJob *job) @@ -152,27 +168,30 @@ void block_job_iostatus_reset(BlockJob *job) } } -struct BlockCancelData { +struct BlockFinishData { BlockJob *job; - BlockDriverCompletionFunc *cb; + BlockCompletionFunc *cb; void *opaque; bool cancelled; int ret; }; -static void block_job_cancel_cb(void *opaque, int ret) +static void block_job_finish_cb(void *opaque, int ret) { - struct BlockCancelData *data = opaque; + struct BlockFinishData *data = opaque; data->cancelled = block_job_is_cancelled(data->job); data->ret = ret; data->cb(data->opaque, ret); } -int block_job_cancel_sync(BlockJob *job) +static int block_job_finish_sync(BlockJob *job, + void (*finish)(BlockJob *, Error **errp), + Error **errp) { - struct BlockCancelData data; + struct BlockFinishData data; BlockDriverState *bs = job->bs; + Error *local_err = NULL; assert(bs->job == job); @@ -183,15 +202,37 @@ int block_job_cancel_sync(BlockJob *job) data.cb = job->cb; data.opaque = job->opaque; data.ret = -EINPROGRESS; - job->cb = block_job_cancel_cb; + job->cb = block_job_finish_cb; job->opaque = &data; - block_job_cancel(job); + finish(job, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EBUSY; + } while (data.ret == -EINPROGRESS) { - qemu_aio_wait(); + aio_poll(bdrv_get_aio_context(bs), true); } return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret; } +/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be + * used with block_job_finish_sync() without the need for (rather nasty) + * function pointer casts there. */ +static void block_job_cancel_err(BlockJob *job, Error **errp) +{ + block_job_cancel(job); +} + +int block_job_cancel_sync(BlockJob *job) +{ + return block_job_finish_sync(job, &block_job_cancel_err, NULL); +} + +int block_job_complete_sync(BlockJob *job, Error **errp) +{ + return block_job_finish_sync(job, &block_job_complete, errp); +} + void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns) { assert(job->busy); @@ -205,8 +246,22 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns) if (block_job_is_paused(job)) { qemu_coroutine_yield(); } else { - co_sleep_ns(type, ns); + co_aio_sleep_ns(bdrv_get_aio_context(job->bs), type, ns); + } + job->busy = true; +} + +void block_job_yield(BlockJob *job) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (block_job_is_cancelled(job)) { + return; } + + job->busy = false; + qemu_coroutine_yield(); job->busy = true; } @@ -217,10 +272,11 @@ BlockJobInfo *block_job_query(BlockJob *job) info->device = g_strdup(bdrv_get_device_name(job->bs)); info->len = job->len; info->busy = job->busy; - info->paused = job->paused; + info->paused = job->pause_count > 0; info->offset = job->offset; info->speed = job->speed; info->io_status = job->iostatus; + info->ready = job->ready; return info; } @@ -232,26 +288,37 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } +void block_job_event_cancelled(BlockJob *job) +{ + qapi_event_send_block_job_cancelled(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, + &error_abort); +} -QObject *qobject_from_block_job(BlockJob *job) +void block_job_event_completed(BlockJob *job, const char *msg) { - return qobject_from_jsonf("{ 'type': %s," - "'device': %s," - "'len': %" PRId64 "," - "'offset': %" PRId64 "," - "'speed': %" PRId64 " }", - BlockJobType_lookup[job->driver->job_type], - bdrv_get_device_name(job->bs), - job->len, - job->offset, - job->speed); + qapi_event_send_block_job_completed(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, + !!msg, + msg, + &error_abort); } -void block_job_ready(BlockJob *job) +void block_job_event_ready(BlockJob *job) { - QObject *data = qobject_from_block_job(job); - monitor_protocol_event(QEVENT_BLOCK_JOB_READY, data); - qobject_decref(data); + job->ready = true; + + qapi_event_send_block_job_ready(job->driver->job_type, + bdrv_get_device_name(job->bs), + job->len, + job->offset, + job->speed, &error_abort); } BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, @@ -277,8 +344,13 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, default: abort(); } - bdrv_emit_qmp_error_event(job->bs, QEVENT_BLOCK_JOB_ERROR, action, is_read); + qapi_event_send_block_job_error(bdrv_get_device_name(job->bs), + 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_iostatus_set_err(job, error); if (bs != job->bs) { @@ -287,3 +359,48 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, } return action; } + +typedef struct { + BlockJob *job; + QEMUBH *bh; + AioContext *aio_context; + BlockJobDeferToMainLoopFn *fn; + void *opaque; +} BlockJobDeferToMainLoopData; + +static void block_job_defer_to_main_loop_bh(void *opaque) +{ + BlockJobDeferToMainLoopData *data = opaque; + AioContext *aio_context; + + qemu_bh_delete(data->bh); + + /* Prevent race with block_job_defer_to_main_loop() */ + aio_context_acquire(data->aio_context); + + /* Fetch BDS AioContext again, in case it has changed */ + aio_context = bdrv_get_aio_context(data->job->bs); + aio_context_acquire(aio_context); + + data->fn(data->job, data->opaque); + + aio_context_release(aio_context); + + aio_context_release(data->aio_context); + + g_free(data); +} + +void block_job_defer_to_main_loop(BlockJob *job, + BlockJobDeferToMainLoopFn *fn, + void *opaque) +{ + BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); + data->job = job; + data->bh = qemu_bh_new(block_job_defer_to_main_loop_bh, data); + data->aio_context = bdrv_get_aio_context(job->bs); + data->fn = fn; + data->opaque = opaque; + + qemu_bh_schedule(data->bh); +}