X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/e6a76719987e5fcd63da552f7cf32d837b0a5cea..fe1755cbb21c34a40234e2c380d049b9b031c4e2:/block.c diff --git a/block.c b/block.c index b38940bf85..e95f613aa4 100644 --- a/block.c +++ b/block.c @@ -26,8 +26,10 @@ #include "trace.h" #include "monitor.h" #include "block_int.h" +#include "blockjob.h" #include "module.h" #include "qjson.h" +#include "sysemu.h" #include "qemu-coroutine.h" #include "qmp-commands.h" #include "qemu-timer.h" @@ -433,7 +435,11 @@ int get_tmp_filename(char *filename, int size) return -EOVERFLOW; } fd = mkstemp(filename); - if (fd < 0 || close(fd)) { + if (fd < 0) { + return -errno; + } + if (close(fd) != 0) { + unlink(filename); return -errno; } return 0; @@ -664,7 +670,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename, open_flags |= BDRV_O_RDWR; } - bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR); + bs->read_only = !(open_flags & BDRV_O_RDWR); /* Open the image, either directly or using a protocol */ if (drv->bdrv_file_open) { @@ -804,6 +810,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, goto unlink_and_fail; } + if (flags & BDRV_O_RDWR) { + flags |= BDRV_O_ALLOW_RDWR; + } + /* Open the image */ ret = bdrv_open_common(bs, filename, flags, drv); if (ret < 0) { @@ -833,12 +843,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, bdrv_close(bs); return ret; } - if (bs->is_temporary) { - bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR); - } else { - /* base image inherits from "parent" */ - bs->backing_hd->keep_read_only = bs->keep_read_only; - } } if (!bdrv_key_required(bs)) { @@ -859,6 +863,238 @@ unlink_and_fail: return ret; } +typedef struct BlockReopenQueueEntry { + bool prepared; + BDRVReopenState state; + QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry; +} BlockReopenQueueEntry; + +/* + * Adds a BlockDriverState to a simple queue for an atomic, transactional + * reopen of multiple devices. + * + * bs_queue can either be an existing BlockReopenQueue that has had QSIMPLE_INIT + * already performed, or alternatively may be NULL a new BlockReopenQueue will + * be created and initialized. This newly created BlockReopenQueue should be + * passed back in for subsequent calls that are intended to be of the same + * atomic 'set'. + * + * bs is the BlockDriverState to add to the reopen queue. + * + * flags contains the open flags for the associated bs + * + * returns a pointer to bs_queue, which is either the newly allocated + * bs_queue, or the existing bs_queue being used. + * + */ +BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, + BlockDriverState *bs, int flags) +{ + assert(bs != NULL); + + BlockReopenQueueEntry *bs_entry; + if (bs_queue == NULL) { + bs_queue = g_new0(BlockReopenQueue, 1); + QSIMPLEQ_INIT(bs_queue); + } + + if (bs->file) { + bdrv_reopen_queue(bs_queue, bs->file, flags); + } + + bs_entry = g_new0(BlockReopenQueueEntry, 1); + QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); + + bs_entry->state.bs = bs; + bs_entry->state.flags = flags; + + return bs_queue; +} + +/* + * Reopen multiple BlockDriverStates atomically & transactionally. + * + * The queue passed in (bs_queue) must have been built up previous + * via bdrv_reopen_queue(). + * + * Reopens all BDS specified in the queue, with the appropriate + * flags. All devices are prepared for reopen, and failure of any + * device will cause all device changes to be abandonded, and intermediate + * data cleaned up. + * + * If all devices prepare successfully, then the changes are committed + * to all devices. + * + */ +int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) +{ + int ret = -1; + BlockReopenQueueEntry *bs_entry, *next; + Error *local_err = NULL; + + assert(bs_queue != NULL); + + bdrv_drain_all(); + + QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { + error_propagate(errp, local_err); + goto cleanup; + } + bs_entry->prepared = true; + } + + /* If we reach this point, we have success and just need to apply the + * changes + */ + QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + bdrv_reopen_commit(&bs_entry->state); + } + + ret = 0; + +cleanup: + QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + if (ret && bs_entry->prepared) { + bdrv_reopen_abort(&bs_entry->state); + } + g_free(bs_entry); + } + g_free(bs_queue); + return ret; +} + + +/* Reopen a single BlockDriverState with the specified flags. */ +int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) +{ + int ret = -1; + Error *local_err = NULL; + BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags); + + ret = bdrv_reopen_multiple(queue, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + } + return ret; +} + + +/* + * Prepares a BlockDriverState for reopen. All changes are staged in the + * 'opaque' field of the BDRVReopenState, which is used and allocated by + * the block driver layer .bdrv_reopen_prepare() + * + * bs is the BlockDriverState to reopen + * flags are the new open flags + * queue is the reopen queue + * + * Returns 0 on success, non-zero on error. On error errp will be set + * as well. + * + * On failure, bdrv_reopen_abort() will be called to clean up any data. + * It is the responsibility of the caller to then call the abort() or + * commit() for any other BDS that have been left in a prepare() state + * + */ +int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, + Error **errp) +{ + int ret = -1; + Error *local_err = NULL; + BlockDriver *drv; + + assert(reopen_state != NULL); + assert(reopen_state->bs->drv != NULL); + drv = reopen_state->bs->drv; + + /* if we are to stay read-only, do not allow permission change + * to r/w */ + if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) && + reopen_state->flags & BDRV_O_RDWR) { + error_set(errp, QERR_DEVICE_IS_READ_ONLY, + reopen_state->bs->device_name); + goto error; + } + + + ret = bdrv_flush(reopen_state->bs); + if (ret) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error (%s) flushing drive", + strerror(-ret)); + goto error; + } + + if (drv->bdrv_reopen_prepare) { + ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err); + if (ret) { + if (local_err != NULL) { + error_propagate(errp, local_err); + } else { + error_set(errp, QERR_OPEN_FILE_FAILED, + reopen_state->bs->filename); + } + goto error; + } + } else { + /* It is currently mandatory to have a bdrv_reopen_prepare() + * handler for each supported drv. */ + error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + drv->format_name, reopen_state->bs->device_name, + "reopening of file"); + ret = -1; + goto error; + } + + ret = 0; + +error: + return ret; +} + +/* + * Takes the staged changes for the reopen from bdrv_reopen_prepare(), and + * makes them final by swapping the staging BlockDriverState contents into + * the active BlockDriverState contents. + */ +void bdrv_reopen_commit(BDRVReopenState *reopen_state) +{ + BlockDriver *drv; + + assert(reopen_state != NULL); + drv = reopen_state->bs->drv; + assert(drv != NULL); + + /* If there are any driver level actions to take */ + if (drv->bdrv_reopen_commit) { + drv->bdrv_reopen_commit(reopen_state); + } + + /* set BDS specific flags now */ + reopen_state->bs->open_flags = reopen_state->flags; + reopen_state->bs->enable_write_cache = !!(reopen_state->flags & + BDRV_O_CACHE_WB); + reopen_state->bs->read_only = !(reopen_state->flags & BDRV_O_RDWR); +} + +/* + * Abort the reopen, and delete and free the staged changes in + * reopen_state + */ +void bdrv_reopen_abort(BDRVReopenState *reopen_state) +{ + BlockDriver *drv; + + assert(reopen_state != NULL); + drv = reopen_state->bs->drv; + assert(drv != NULL); + + if (drv->bdrv_reopen_abort) { + drv->bdrv_reopen_abort(reopen_state); + } +} + + void bdrv_close(BlockDriverState *bs) { bdrv_flush(bs); @@ -897,10 +1133,10 @@ void bdrv_close(BlockDriverState *bs) bdrv_delete(bs->file); bs->file = NULL; } - - bdrv_dev_change_media_cb(bs, false); } + bdrv_dev_change_media_cb(bs, false); + /*throttling disk I/O limits*/ if (bs->io_limits_enabled) { bdrv_io_limits_disable(bs); @@ -1152,7 +1388,8 @@ void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops, } void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, - BlockQMPEventAction action, int is_read) + enum MonitorEvent ev, + BlockErrorAction action, bool is_read) { QObject *data; const char *action_str; @@ -1175,7 +1412,7 @@ void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, bdrv->device_name, action_str, is_read ? "read" : "write"); - monitor_protocol_event(QEVENT_BLOCK_IO_ERROR, data); + monitor_protocol_event(ev, data); qobject_decref(data); } @@ -1265,13 +1502,11 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) int bdrv_commit(BlockDriverState *bs) { BlockDriver *drv = bs->drv; - BlockDriver *backing_drv; int64_t sector, total_sectors; int n, ro, open_flags; - int ret = 0, rw_ret = 0; + int ret = 0; uint8_t *buf; - char filename[1024]; - BlockDriverState *bs_rw, *bs_ro; + char filename[PATH_MAX]; if (!drv) return -ENOMEDIUM; @@ -1280,42 +1515,19 @@ int bdrv_commit(BlockDriverState *bs) return -ENOTSUP; } - if (bs->backing_hd->keep_read_only) { - return -EACCES; - } - if (bdrv_in_use(bs) || bdrv_in_use(bs->backing_hd)) { return -EBUSY; } - backing_drv = bs->backing_hd->drv; ro = bs->backing_hd->read_only; - strncpy(filename, bs->backing_hd->filename, sizeof(filename)); + /* Use pstrcpy (not strncpy): filename must be NUL-terminated. */ + pstrcpy(filename, sizeof(filename), bs->backing_hd->filename); open_flags = bs->backing_hd->open_flags; if (ro) { - /* re-open as RW */ - bdrv_delete(bs->backing_hd); - bs->backing_hd = NULL; - bs_rw = bdrv_new(""); - rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR, - backing_drv); - if (rw_ret < 0) { - bdrv_delete(bs_rw); - /* try to re-open read-only */ - bs_ro = bdrv_new(""); - ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, - backing_drv); - if (ret < 0) { - bdrv_delete(bs_ro); - /* drive not functional anymore */ - bs->drv = NULL; - return ret; - } - bs->backing_hd = bs_ro; - return rw_ret; + if (bdrv_reopen(bs->backing_hd, open_flags | BDRV_O_RDWR, NULL)) { + return -EACCES; } - bs->backing_hd = bs_rw; } total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; @@ -1352,20 +1564,8 @@ ro_cleanup: g_free(buf); if (ro) { - /* re-open as RO */ - bdrv_delete(bs->backing_hd); - bs->backing_hd = NULL; - bs_ro = bdrv_new(""); - ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, - backing_drv); - if (ret < 0) { - bdrv_delete(bs_ro); - /* drive not functional anymore */ - bs->drv = NULL; - return ret; - } - bs->backing_hd = bs_ro; - bs->backing_hd->keep_read_only = 0; + /* ignoring error return here */ + bdrv_reopen(bs->backing_hd, open_flags & ~BDRV_O_RDWR, NULL); } return ret; @@ -1528,6 +1728,149 @@ int bdrv_change_backing_file(BlockDriverState *bs, return ret; } +/* + * Finds the image layer in the chain that has 'bs' as its backing file. + * + * active is the current topmost image. + * + * Returns NULL if bs is not found in active's image chain, + * or if active == bs. + */ +BlockDriverState *bdrv_find_overlay(BlockDriverState *active, + BlockDriverState *bs) +{ + BlockDriverState *overlay = NULL; + BlockDriverState *intermediate; + + assert(active != NULL); + assert(bs != NULL); + + /* if bs is the same as active, then by definition it has no overlay + */ + if (active == bs) { + return NULL; + } + + intermediate = active; + while (intermediate->backing_hd) { + if (intermediate->backing_hd == bs) { + overlay = intermediate; + break; + } + intermediate = intermediate->backing_hd; + } + + return overlay; +} + +typedef struct BlkIntermediateStates { + BlockDriverState *bs; + QSIMPLEQ_ENTRY(BlkIntermediateStates) entry; +} BlkIntermediateStates; + + +/* + * Drops images above 'base' up to and including 'top', and sets the image + * above 'top' to have base as its backing file. + * + * Requires that the overlay to 'top' is opened r/w, so that the backing file + * information in 'bs' can be properly updated. + * + * E.g., this will convert the following chain: + * bottom <- base <- intermediate <- top <- active + * + * to + * + * bottom <- base <- active + * + * It is allowed for bottom==base, in which case it converts: + * + * base <- intermediate <- top <- active + * + * to + * + * base <- active + * + * Error conditions: + * if active == top, that is considered an error + * + */ +int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, + BlockDriverState *base) +{ + BlockDriverState *intermediate; + BlockDriverState *base_bs = NULL; + BlockDriverState *new_top_bs = NULL; + BlkIntermediateStates *intermediate_state, *next; + int ret = -EIO; + + QSIMPLEQ_HEAD(states_to_delete, BlkIntermediateStates) states_to_delete; + QSIMPLEQ_INIT(&states_to_delete); + + if (!top->drv || !base->drv) { + goto exit; + } + + new_top_bs = bdrv_find_overlay(active, top); + + if (new_top_bs == NULL) { + /* we could not find the image above 'top', this is an error */ + goto exit; + } + + /* special case of new_top_bs->backing_hd already pointing to base - nothing + * to do, no intermediate images */ + if (new_top_bs->backing_hd == base) { + ret = 0; + goto exit; + } + + intermediate = top; + + /* now we will go down through the list, and add each BDS we find + * into our deletion queue, until we hit the 'base' + */ + while (intermediate) { + intermediate_state = g_malloc0(sizeof(BlkIntermediateStates)); + intermediate_state->bs = intermediate; + QSIMPLEQ_INSERT_TAIL(&states_to_delete, intermediate_state, entry); + + if (intermediate->backing_hd == base) { + base_bs = intermediate->backing_hd; + break; + } + intermediate = intermediate->backing_hd; + } + if (base_bs == NULL) { + /* something went wrong, we did not end at the base. safely + * unravel everything, and exit with error */ + goto exit; + } + + /* success - we can delete the intermediate states, and link top->base */ + ret = bdrv_change_backing_file(new_top_bs, base_bs->filename, + base_bs->drv ? base_bs->drv->format_name : ""); + if (ret) { + goto exit; + } + new_top_bs->backing_hd = base_bs; + + + QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { + /* so that bdrv_close() does not recursively close the chain */ + intermediate_state->bs->backing_hd = NULL; + bdrv_delete(intermediate_state->bs); + } + ret = 0; + +exit: + QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) { + g_free(intermediate_state); + } + return ret; +} + + static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, size_t size) { @@ -2134,18 +2477,51 @@ void bdrv_set_io_limits(BlockDriverState *bs, bs->io_limits_enabled = bdrv_io_limits_enabled(bs); } -void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, - BlockErrorAction on_write_error) +void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, + BlockdevOnError on_write_error) { bs->on_read_error = on_read_error; bs->on_write_error = on_write_error; } -BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read) +BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read) { return is_read ? bs->on_read_error : bs->on_write_error; } +BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error) +{ + BlockdevOnError on_err = is_read ? bs->on_read_error : bs->on_write_error; + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + return (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_STOP: + return BDRV_ACTION_STOP; + case BLOCKDEV_ON_ERROR_REPORT: + return BDRV_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_IGNORE: + return BDRV_ACTION_IGNORE; + default: + abort(); + } +} + +/* This is done by device models because, while the block layer knows + * about the error, it does not know whether an operation comes from + * the device or the block layer (from a job, for example). + */ +void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, + bool is_read, int error) +{ + assert(error >= 0); + bdrv_emit_qmp_error_event(bs, QEVENT_BLOCK_IO_ERROR, action, is_read); + if (action == BDRV_ACTION_STOP) { + vm_stop(RUN_STATE_IO_ERROR); + bdrv_iostatus_set_err(bs, error); + } +} + int bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; @@ -2164,6 +2540,13 @@ int bdrv_enable_write_cache(BlockDriverState *bs) void bdrv_set_enable_write_cache(BlockDriverState *bs, bool wce) { bs->enable_write_cache = wce; + + /* so a reopen() will preserve wce */ + if (wce) { + bs->open_flags |= BDRV_O_CACHE_WB; + } else { + bs->open_flags &= ~BDRV_O_CACHE_WB; + } } int bdrv_is_encrypted(BlockDriverState *bs) @@ -2445,11 +2828,15 @@ BlockInfoList *qmp_query_block(Error **errp) info->value->inserted->ro = bs->read_only; info->value->inserted->drv = g_strdup(bs->drv->format_name); info->value->inserted->encrypted = bs->encrypted; + info->value->inserted->encryption_key_missing = bdrv_key_required(bs); if (bs->backing_file[0]) { info->value->inserted->has_backing_file = true; info->value->inserted->backing_file = g_strdup(bs->backing_file); } + info->value->inserted->backing_file_depth = + bdrv_get_backing_file_depth(bs); + if (bs->io_limits_enabled) { info->value->inserted->bps = bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]; @@ -2754,6 +3141,35 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, return NULL; } +int bdrv_get_backing_file_depth(BlockDriverState *bs) +{ + if (!bs->drv) { + return 0; + } + + if (!bs->backing_hd) { + return 0; + } + + return 1 + bdrv_get_backing_file_depth(bs->backing_hd); +} + +BlockDriverState *bdrv_find_base(BlockDriverState *bs) +{ + BlockDriverState *curr_bs = NULL; + + if (!bs) { + return NULL; + } + + curr_bs = bs; + + while (curr_bs->backing_hd) { + curr_bs = curr_bs->backing_hd; + } + return curr_bs; +} + #define NB_SUFFIXES 4 char *get_human_readable_size(char *buf, int buf_size, int64_t size) @@ -3517,7 +3933,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) /* But don't actually force it to the disk with cache=unsafe */ if (bs->open_flags & BDRV_O_NO_FLUSH) { - return 0; + goto flush_parent; } if (bs->drv->bdrv_co_flush_to_disk) { @@ -3556,6 +3972,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) /* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH * in the case of cache=unsafe, so there are no useless flushes. */ +flush_parent: return bdrv_co_flush(bs->file); } @@ -3828,9 +4245,9 @@ void bdrv_iostatus_enable(BlockDriverState *bs) bool bdrv_iostatus_is_enabled(const BlockDriverState *bs) { return (bs->iostatus_enabled && - (bs->on_write_error == BLOCK_ERR_STOP_ENOSPC || - bs->on_write_error == BLOCK_ERR_STOP_ANY || - bs->on_read_error == BLOCK_ERR_STOP_ANY)); + (bs->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || + bs->on_write_error == BLOCKDEV_ON_ERROR_STOP || + bs->on_read_error == BLOCKDEV_ON_ERROR_STOP)); } void bdrv_iostatus_disable(BlockDriverState *bs) @@ -3845,14 +4262,10 @@ void bdrv_iostatus_reset(BlockDriverState *bs) } } -/* XXX: Today this is set by device models because it makes the implementation - quite simple. However, the block layer knows about the error, so it's - possible to implement this without device models being involved */ void bdrv_iostatus_set_err(BlockDriverState *bs, int error) { - if (bdrv_iostatus_is_enabled(bs) && - bs->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { - assert(error >= 0); + assert(bdrv_iostatus_is_enabled(bs)); + if (bs->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { bs->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : BLOCK_DEVICE_IO_STATUS_FAILED; } @@ -4026,130 +4439,3 @@ out: return ret; } - -void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs, - int64_t speed, BlockDriverCompletionFunc *cb, - void *opaque, Error **errp) -{ - BlockJob *job; - - if (bs->job || bdrv_in_use(bs)) { - error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs)); - return NULL; - } - bdrv_set_in_use(bs, 1); - - job = g_malloc0(job_type->instance_size); - job->job_type = job_type; - job->bs = bs; - job->cb = cb; - job->opaque = opaque; - job->busy = true; - bs->job = job; - - /* 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 (error_is_set(&local_err)) { - bs->job = NULL; - g_free(job); - bdrv_set_in_use(bs, 0); - error_propagate(errp, local_err); - return NULL; - } - } - return job; -} - -void block_job_complete(BlockJob *job, int ret) -{ - BlockDriverState *bs = job->bs; - - assert(bs->job == job); - job->cb(job->opaque, ret); - bs->job = NULL; - g_free(job); - bdrv_set_in_use(bs, 0); -} - -void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - Error *local_err = NULL; - - if (!job->job_type->set_speed) { - error_set(errp, QERR_NOT_SUPPORTED); - return; - } - job->job_type->set_speed(job, speed, &local_err); - if (error_is_set(&local_err)) { - error_propagate(errp, local_err); - return; - } - - job->speed = speed; -} - -void block_job_cancel(BlockJob *job) -{ - job->cancelled = true; - if (job->co && !job->busy) { - qemu_coroutine_enter(job->co, NULL); - } -} - -bool block_job_is_cancelled(BlockJob *job) -{ - return job->cancelled; -} - -struct BlockCancelData { - BlockJob *job; - BlockDriverCompletionFunc *cb; - void *opaque; - bool cancelled; - int ret; -}; - -static void block_job_cancel_cb(void *opaque, int ret) -{ - struct BlockCancelData *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) -{ - struct BlockCancelData data; - BlockDriverState *bs = job->bs; - - assert(bs->job == job); - - /* Set up our own callback to store the result and chain to - * the original callback. - */ - data.job = job; - data.cb = job->cb; - data.opaque = job->opaque; - data.ret = -EINPROGRESS; - job->cb = block_job_cancel_cb; - job->opaque = &data; - block_job_cancel(job); - while (data.ret == -EINPROGRESS) { - qemu_aio_wait(); - } - return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret; -} - -void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns) -{ - /* Check cancellation *before* setting busy = false, too! */ - if (!block_job_is_cancelled(job)) { - job->busy = false; - co_sleep_ns(clock, ns); - job->busy = true; - } -}