From d44f928a54497188c25357840a3224925d1b527b Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 19 Oct 2015 17:53:08 +0200 Subject: block: Set BDRV_O_INCOMING in bdrv_fill_options() This flag should not be set for the root BDS only, but for any BDS that is being created while incoming migration is pending, so setting it is moved from blockdev_init() to bdrv_fill_options(). Signed-off-by: Max Reitz Reviewed-by: Eric Blake Reviewed-by: Kevin Wolf Reviewed-by: Alberto Garcia Signed-off-by: Kevin Wolf --- blockdev.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index 8141b6b3da..27398b1041 100644 --- a/blockdev.c +++ b/blockdev.c @@ -537,10 +537,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, bdrv_flags |= BDRV_O_COPY_ON_READ; } - if (runstate_check(RUN_STATE_INMIGRATE)) { - bdrv_flags |= BDRV_O_INCOMING; - } - bdrv_flags |= ro ? 0 : BDRV_O_RDWR; blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags, -- cgit 1.4.1 From be4b67bc7d99da26b7878f7f45370f50a3bd4af5 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 19 Oct 2015 17:53:09 +0200 Subject: blockdev: Allow creation of BDS trees without BB If the "id" field is missing from the options given to blockdev-add, just omit the BlockBackend and create the BlockDriverState tree alone. However, if "id" is missing, "node-name" must be specified; otherwise, the BDS tree would no longer be accessible. Many BDS options which are not parsed by bdrv_open() (like caching) cannot be specified for these BB-less BDS trees yet. A future patch will remove this limitation. Signed-off-by: Max Reitz Reviewed-by: Eric Blake Reviewed-by: Kevin Wolf Reviewed-by: Alberto Garcia Signed-off-by: Kevin Wolf --- blockdev.c | 44 +++++++++++++++++++++++++++++++------------- qapi/block-core.json | 13 +++++++++---- tests/qemu-iotests/087 | 2 +- tests/qemu-iotests/087.out | 4 ++-- 4 files changed, 43 insertions(+), 20 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index 27398b1041..0785557550 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3026,17 +3026,12 @@ out: void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { QmpOutputVisitor *ov = qmp_output_visitor_new(); - BlockBackend *blk; + BlockDriverState *bs; + BlockBackend *blk = NULL; QObject *obj; QDict *qdict; Error *local_err = NULL; - /* Require an ID in the top level */ - if (!options->has_id) { - error_setg(errp, "Block device needs an ID"); - goto fail; - } - /* TODO Sort it out in raw-posix and drive_new(): Reject aio=native with * cache.direct=false instead of silently switching to aio=threads, except * when called from drive_new(). @@ -3064,14 +3059,37 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) qdict_flatten(qdict); - blk = blockdev_init(NULL, qdict, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto fail; + if (options->has_id) { + blk = blockdev_init(NULL, qdict, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + bs = blk_bs(blk); + } else { + int ret; + + if (!qdict_get_try_str(qdict, "node-name")) { + error_setg(errp, "'id' and/or 'node-name' need to be specified for " + "the root node"); + goto fail; + } + + bs = NULL; + ret = bdrv_open(&bs, NULL, NULL, qdict, BDRV_O_RDWR | BDRV_O_CACHE_WB, + errp); + if (ret < 0) { + goto fail; + } } - if (bdrv_key_required(blk_bs(blk))) { - blk_unref(blk); + if (bs && bdrv_key_required(bs)) { + if (blk) { + blk_unref(blk); + } else { + bdrv_unref(bs); + } error_setg(errp, "blockdev-add doesn't support encrypted devices"); goto fail; } diff --git a/qapi/block-core.json b/qapi/block-core.json index c04256185b..425fdab706 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1393,9 +1393,12 @@ # # @driver: block driver name # @id: #optional id by which the new block device can be referred to. -# This is a required option on the top level of blockdev-add, and -# currently not allowed on any other level. -# @node-name: #optional the name of a block driver state node (Since 2.0) +# This option is only allowed on the top level of blockdev-add. +# A BlockBackend will be created by blockdev-add if and only if +# this option is given. +# @node-name: #optional the name of a block driver state node (Since 2.0). +# This option is required on the top level of blockdev-add if +# the @id option is not given there. # @discard: #optional discard-related options (default: ignore) # @cache: #optional cache-related options # @aio: #optional AIO backend (default: threads) @@ -1859,7 +1862,9 @@ ## # @blockdev-add: # -# Creates a new block device. +# Creates a new block device. If the @id option is given at the top level, a +# BlockBackend will be created; otherwise, @node-name is mandatory at the top +# level and no BlockBackend will be created. # # This command is still a work in progress. It doesn't support all # block drivers, it lacks a matching blockdev-del, and more. Stay diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index 8694749947..af44299e07 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -54,7 +54,7 @@ size=128M _make_test_img $size echo -echo === Missing ID === +echo === Missing ID and node-name === echo run_qemu < Date: Mon, 19 Oct 2015 17:53:22 +0200 Subject: block: Move I/O status and error actions into BB These options are only relevant for the user of a whole BDS tree (like a guest device or a block job) and should thus be moved into the BlockBackend. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block.c | 125 ----------------------------------------- block/backup.c | 17 ++++-- block/block-backend.c | 116 ++++++++++++++++++++++++++++++++++++-- block/commit.c | 3 +- block/mirror.c | 17 ++++-- block/qapi.c | 4 +- block/stream.c | 3 +- blockdev.c | 6 +- blockjob.c | 5 +- include/block/block.h | 11 ---- include/block/block_int.h | 6 -- include/sysemu/block-backend.h | 7 +++ qmp.c | 6 +- 13 files changed, 158 insertions(+), 168 deletions(-) (limited to 'blockdev.c') diff --git a/block.c b/block.c index dbce7bc181..e28a32a201 100644 --- a/block.c +++ b/block.c @@ -257,7 +257,6 @@ BlockDriverState *bdrv_new(void) for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { QLIST_INIT(&bs->op_blockers[i]); } - bdrv_iostatus_disable(bs); notifier_list_init(&bs->close_notifiers); notifier_with_return_list_init(&bs->before_write_notifiers); qemu_co_queue_init(&bs->throttled_reqs[0]); @@ -2005,14 +2004,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->enable_write_cache = bs_src->enable_write_cache; - /* r/w error */ - bs_dest->on_read_error = bs_src->on_read_error; - bs_dest->on_write_error = bs_src->on_write_error; - - /* i/o status */ - bs_dest->iostatus_enabled = bs_src->iostatus_enabled; - bs_dest->iostatus = bs_src->iostatus; - /* dirty bitmap */ bs_dest->dirty_bitmaps = bs_src->dirty_bitmaps; } @@ -2499,82 +2490,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; } -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; -} - -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) ? - BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; - case BLOCKDEV_ON_ERROR_STOP: - return BLOCK_ERROR_ACTION_STOP; - case BLOCKDEV_ON_ERROR_REPORT: - return BLOCK_ERROR_ACTION_REPORT; - case BLOCKDEV_ON_ERROR_IGNORE: - return BLOCK_ERROR_ACTION_IGNORE; - default: - abort(); - } -} - -static void send_qmp_error_event(BlockDriverState *bs, - BlockErrorAction action, - bool is_read, int error) -{ - IoOperationType optype; - - optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; - qapi_event_send_block_io_error(bdrv_get_device_name(bs), optype, action, - bdrv_iostatus_is_enabled(bs), - error == ENOSPC, strerror(error), - &error_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); - - if (action == BLOCK_ERROR_ACTION_STOP) { - /* First set the iostatus, so that "info block" returns an iostatus - * that matches the events raised so far (an additional error iostatus - * is fine, but not a lost one). - */ - bdrv_iostatus_set_err(bs, error); - - /* Then raise the request to stop the VM and the event. - * qemu_system_vmstop_request_prepare has two effects. First, - * it ensures that the STOP event always comes after the - * BLOCK_IO_ERROR event. Second, it ensures that even if management - * can observe the STOP event and do a "cont" before the STOP - * event is issued, the VM will not stop. In this case, vm_start() - * also ensures that the STOP/RESUME pair of events is emitted. - */ - qemu_system_vmstop_request_prepare(); - send_qmp_error_event(bs, action, is_read, error); - qemu_system_vmstop_request(RUN_STATE_IO_ERROR); - } else { - send_qmp_error_event(bs, action, is_read, error); - } -} - int bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; @@ -3602,46 +3517,6 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs) return true; } -void bdrv_iostatus_enable(BlockDriverState *bs) -{ - bs->iostatus_enabled = true; - bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK; -} - -/* The I/O status is only enabled if the drive explicitly - * enables it _and_ the VM is configured to stop on errors */ -bool bdrv_iostatus_is_enabled(const BlockDriverState *bs) -{ - return (bs->iostatus_enabled && - (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) -{ - bs->iostatus_enabled = false; -} - -void bdrv_iostatus_reset(BlockDriverState *bs) -{ - if (bdrv_iostatus_is_enabled(bs)) { - bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK; - if (bs->job) { - block_job_iostatus_reset(bs->job); - } - } -} - -void bdrv_iostatus_set_err(BlockDriverState *bs, int error) -{ - 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; - } -} - void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, char *options, uint64_t img_size, int flags, diff --git a/block/backup.c b/block/backup.c index 5696431711..ec01db8eff 100644 --- a/block/backup.c +++ b/block/backup.c @@ -21,6 +21,7 @@ #include "block/blockjob.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "sysemu/block-backend.h" #define BACKUP_CLUSTER_BITS 16 #define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS) @@ -215,7 +216,9 @@ static void backup_iostatus_reset(BlockJob *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common); - bdrv_iostatus_reset(s->target); + if (s->target->blk) { + blk_iostatus_reset(s->target->blk); + } } static const BlockJobDriver backup_job_driver = { @@ -360,8 +363,10 @@ static void coroutine_fn backup_run(void *opaque) job->bitmap = hbitmap_alloc(end, 0); bdrv_set_enable_write_cache(target, true); - bdrv_set_on_error(target, on_target_error, on_target_error); - bdrv_iostatus_enable(target); + if (target->blk) { + blk_set_on_error(target->blk, on_target_error, on_target_error); + blk_iostatus_enable(target->blk); + } bdrv_add_before_write_notifier(bs, &before_write); @@ -451,7 +456,9 @@ static void coroutine_fn backup_run(void *opaque) } hbitmap_free(job->bitmap); - bdrv_iostatus_disable(target); + if (target->blk) { + blk_iostatus_disable(target->blk); + } bdrv_op_unblock_all(target, job->common.blocker); data = g_malloc(sizeof(*data)); @@ -480,7 +487,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); return; } diff --git a/block/block-backend.c b/block/block-backend.c index a52037b418..2708ad146c 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -12,7 +12,9 @@ #include "sysemu/block-backend.h" #include "block/block_int.h" +#include "block/blockjob.h" #include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" #include "qapi-event.h" /* Number of coroutines to reserve per attached device model */ @@ -37,6 +39,10 @@ struct BlockBackend { /* I/O stats (display with "info blockstats"). */ BlockAcctStats stats; + + BlockdevOnError on_read_error, on_write_error; + bool iostatus_enabled; + BlockDeviceIoStatus iostatus; }; typedef struct BlockBackendAIOCB { @@ -330,7 +336,7 @@ int blk_attach_dev(BlockBackend *blk, void *dev) } blk_ref(blk); blk->dev = dev; - bdrv_iostatus_reset(blk->bs); + blk_iostatus_reset(blk); return 0; } @@ -462,7 +468,47 @@ void blk_dev_resize_cb(BlockBackend *blk) void blk_iostatus_enable(BlockBackend *blk) { - bdrv_iostatus_enable(blk->bs); + blk->iostatus_enabled = true; + blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; +} + +/* The I/O status is only enabled if the drive explicitly + * enables it _and_ the VM is configured to stop on errors */ +bool blk_iostatus_is_enabled(const BlockBackend *blk) +{ + return (blk->iostatus_enabled && + (blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || + blk->on_write_error == BLOCKDEV_ON_ERROR_STOP || + blk->on_read_error == BLOCKDEV_ON_ERROR_STOP)); +} + +BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) +{ + return blk->iostatus; +} + +void blk_iostatus_disable(BlockBackend *blk) +{ + blk->iostatus_enabled = false; +} + +void blk_iostatus_reset(BlockBackend *blk) +{ + if (blk_iostatus_is_enabled(blk)) { + blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + if (blk->bs && blk->bs->job) { + block_job_iostatus_reset(blk->bs->job); + } + } +} + +void blk_iostatus_set_err(BlockBackend *blk, int error) +{ + assert(blk_iostatus_is_enabled(blk)); + if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : + BLOCK_DEVICE_IO_STATUS_FAILED; + } } static int blk_check_byte_request(BlockBackend *blk, int64_t offset, @@ -738,21 +784,81 @@ void blk_drain_all(void) bdrv_drain_all(); } +void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, + BlockdevOnError on_write_error) +{ + blk->on_read_error = on_read_error; + blk->on_write_error = on_write_error; +} + BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) { - return bdrv_get_on_error(blk->bs, is_read); + return is_read ? blk->on_read_error : blk->on_write_error; } BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, int error) { - return bdrv_get_error_action(blk->bs, is_read, error); + BlockdevOnError on_err = blk_get_on_error(blk, is_read); + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + return (error == ENOSPC) ? + BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_STOP: + return BLOCK_ERROR_ACTION_STOP; + case BLOCKDEV_ON_ERROR_REPORT: + return BLOCK_ERROR_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_IGNORE: + return BLOCK_ERROR_ACTION_IGNORE; + default: + abort(); + } } +static void send_qmp_error_event(BlockBackend *blk, + BlockErrorAction action, + bool is_read, int error) +{ + IoOperationType optype; + + optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; + qapi_event_send_block_io_error(blk_name(blk), optype, action, + blk_iostatus_is_enabled(blk), + error == ENOSPC, strerror(error), + &error_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 blk_error_action(BlockBackend *blk, BlockErrorAction action, bool is_read, int error) { - bdrv_error_action(blk->bs, action, is_read, error); + assert(error >= 0); + + if (action == BLOCK_ERROR_ACTION_STOP) { + /* First set the iostatus, so that "info block" returns an iostatus + * that matches the events raised so far (an additional error iostatus + * is fine, but not a lost one). + */ + blk_iostatus_set_err(blk, error); + + /* Then raise the request to stop the VM and the event. + * qemu_system_vmstop_request_prepare has two effects. First, + * it ensures that the STOP event always comes after the + * BLOCK_IO_ERROR event. Second, it ensures that even if management + * can observe the STOP event and do a "cont" before the STOP + * event is issued, the VM will not stop. In this case, vm_start() + * also ensures that the STOP/RESUME pair of events is emitted. + */ + qemu_system_vmstop_request_prepare(); + send_qmp_error_event(blk, action, is_read, error); + qemu_system_vmstop_request(RUN_STATE_IO_ERROR); + } else { + send_qmp_error_event(blk, action, is_read, error); + } } int blk_is_read_only(BlockBackend *blk) diff --git a/block/commit.c b/block/commit.c index d12e26fab6..fdebe87c16 100644 --- a/block/commit.c +++ b/block/commit.c @@ -17,6 +17,7 @@ #include "block/blockjob.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "sysemu/block-backend.h" enum { /* @@ -213,7 +214,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, if ((on_error == BLOCKDEV_ON_ERROR_STOP || on_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, "Invalid parameter combination"); return; } diff --git a/block/mirror.c b/block/mirror.c index 7e43511832..b1252a1b29 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -14,6 +14,7 @@ #include "trace.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" #include "qemu/bitmap.h" @@ -599,7 +600,9 @@ immediate_exit: g_free(s->cow_bitmap); g_free(s->in_flight_bitmap); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); - bdrv_iostatus_disable(s->target); + if (s->target->blk) { + blk_iostatus_disable(s->target->blk); + } data = g_malloc(sizeof(*data)); data->ret = ret; @@ -621,7 +624,9 @@ static void mirror_iostatus_reset(BlockJob *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); - bdrv_iostatus_reset(s->target); + if (s->target->blk) { + blk_iostatus_reset(s->target->blk); + } } static void mirror_complete(BlockJob *job, Error **errp) @@ -704,7 +709,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, QERR_INVALID_PARAMETER, "on-source-error"); return; } @@ -740,8 +745,10 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target, return; } bdrv_set_enable_write_cache(s->target, true); - bdrv_set_on_error(s->target, on_target_error, on_target_error); - bdrv_iostatus_enable(s->target); + if (s->target->blk) { + blk_set_on_error(s->target->blk, on_target_error, on_target_error); + blk_iostatus_enable(s->target->blk); + } s->common.co = qemu_coroutine_create(mirror_run); trace_mirror_start(bs, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); diff --git a/block/qapi.c b/block/qapi.c index 7c8209b0cb..3b46f97b8d 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -301,9 +301,9 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, info->tray_open = blk_dev_is_tray_open(blk); } - if (bdrv_iostatus_is_enabled(bs)) { + if (blk_iostatus_is_enabled(blk)) { info->has_io_status = true; - info->io_status = bs->iostatus; + info->io_status = blk_iostatus(blk); } if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { diff --git a/block/stream.c b/block/stream.c index 3f64fa2245..25af7eff62 100644 --- a/block/stream.c +++ b/block/stream.c @@ -16,6 +16,7 @@ #include "block/blockjob.h" #include "qapi/qmp/qerror.h" #include "qemu/ratelimit.h" +#include "sysemu/block-backend.h" enum { /* @@ -222,7 +223,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, if ((on_error == BLOCKDEV_ON_ERROR_STOP || on_error == BLOCKDEV_ON_ERROR_ENOSPC) && - !bdrv_iostatus_is_enabled(bs)) { + (!bs->blk || !blk_iostatus_is_enabled(bs->blk))) { error_setg(errp, QERR_INVALID_PARAMETER, "on-error"); return; } diff --git a/blockdev.c b/blockdev.c index 0785557550..433c4e2967 100644 --- a/blockdev.c +++ b/blockdev.c @@ -549,7 +549,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, bs->detect_zeroes = detect_zeroes; - bdrv_set_on_error(bs, on_read_error, on_write_error); + blk_set_on_error(blk, on_read_error, on_write_error); /* disk I/O throttling */ if (throttle_enabled(&cfg)) { @@ -2168,8 +2168,8 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) if (blk_get_attached_dev(blk)) { blk_hide_on_behalf_of_hmp_drive_del(blk); /* Further I/O must not pause the guest */ - bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT); + blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT); } else { blk_unref(blk); } diff --git a/blockjob.c b/blockjob.c index 1da5491228..c02fe598b8 100644 --- a/blockjob.c +++ b/blockjob.c @@ -29,6 +29,7 @@ #include "block/block.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "sysemu/block-backend.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" #include "qemu/coroutine.h" @@ -354,8 +355,8 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, job->user_paused = true; block_job_pause(job); block_job_iostatus_set_err(job, error); - if (bs != job->bs) { - bdrv_iostatus_set_err(bs, error); + if (bs->blk && bs != job->bs) { + blk_iostatus_set_err(bs->blk, error); } } return action; diff --git a/include/block/block.h b/include/block/block.h index a39cfe30a0..77e91bdc79 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -174,11 +174,6 @@ typedef enum BlockOpType { BLOCK_OP_TYPE_MAX, } BlockOpType; -void bdrv_iostatus_enable(BlockDriverState *bs); -void bdrv_iostatus_reset(BlockDriverState *bs); -void bdrv_iostatus_disable(BlockDriverState *bs); -bool bdrv_iostatus_is_enabled(const BlockDriverState *bs); -void bdrv_iostatus_set_err(BlockDriverState *bs, int error); void bdrv_info_print(Monitor *mon, const QObject *data); void bdrv_info(Monitor *mon, QObject **ret_data); void bdrv_stats_print(Monitor *mon, const QObject *data); @@ -389,12 +384,6 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); -void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, - BlockdevOnError on_write_error); -BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read); -BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error); -void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, - bool is_read, int error); int bdrv_is_read_only(BlockDriverState *bs); int bdrv_is_sg(BlockDriverState *bs); int bdrv_enable_write_cache(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index aa00aea5da..87215f8fc6 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -414,12 +414,6 @@ struct BlockDriverState { /* do we need to tell the quest if we have a volatile write cache? */ int enable_write_cache; - /* NOTE: the following infos are only hints for real hardware - drivers. They are not used by the block driver */ - BlockdevOnError on_read_error, on_write_error; - bool iostatus_enabled; - BlockDeviceIoStatus iostatus; - /* the following member gives a name to every node on the bs graph. */ char node_name[32]; /* element of the list of named nodes building the graph */ diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 1e19d1bb79..eafcef08c9 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -76,6 +76,11 @@ BlockDriverState *blk_bs(BlockBackend *blk); void blk_hide_on_behalf_of_hmp_drive_del(BlockBackend *blk); void blk_iostatus_enable(BlockBackend *blk); +bool blk_iostatus_is_enabled(const BlockBackend *blk); +BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); +void blk_iostatus_disable(BlockBackend *blk); +void blk_iostatus_reset(BlockBackend *blk); +void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_attach_dev(BlockBackend *blk, void *dev); void blk_attach_dev_nofail(BlockBackend *blk, void *dev); void blk_detach_dev(BlockBackend *blk, void *dev); @@ -120,6 +125,8 @@ int blk_flush(BlockBackend *blk); int blk_flush_all(void); void blk_drain(BlockBackend *blk); void blk_drain_all(void); +void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, + BlockdevOnError on_write_error); BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, int error); diff --git a/qmp.c b/qmp.c index d9ecedef93..ff54e5a765 100644 --- a/qmp.c +++ b/qmp.c @@ -24,6 +24,7 @@ #include "sysemu/arch_init.h" #include "hw/qdev.h" #include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" #include "qom/qom-qobject.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qobject.h" @@ -170,6 +171,7 @@ SpiceInfo *qmp_query_spice(Error **errp) void qmp_cont(Error **errp) { Error *local_err = NULL; + BlockBackend *blk; BlockDriverState *bs; if (runstate_needs_reset()) { @@ -179,8 +181,8 @@ void qmp_cont(Error **errp) return; } - for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { - bdrv_iostatus_reset(bs); + for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + blk_iostatus_reset(blk); } for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) { bdrv_add_key(bs, NULL, &local_err); -- cgit 1.4.1 From 5433c24f0f9306c82ad9bcc2b2b586e5f0ed8fa5 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 19 Oct 2015 17:53:29 +0200 Subject: block: Prepare for NULL BDS blk_bs() will not necessarily return a non-NULL value any more (unless blk_is_available() is true or it can be assumed to otherwise, e.g. because it is called immediately after a successful blk_new_with_bs() or blk_new_open()). Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block.c | 5 ++ block/qapi.c | 4 +- blockdev.c | 204 ++++++++++++++++++++++++++++++++++------------------ hw/block/xen_disk.c | 4 +- migration/block.c | 5 ++ monitor.c | 4 ++ 6 files changed, 154 insertions(+), 72 deletions(-) (limited to 'blockdev.c') diff --git a/block.c b/block.c index e28a32a201..e9f40dc768 100644 --- a/block.c +++ b/block.c @@ -2683,6 +2683,11 @@ BlockDriverState *bdrv_lookup_bs(const char *device, blk = blk_by_name(device); if (blk) { + if (!blk_bs(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + return NULL; + } + return blk_bs(blk); } } diff --git a/block/qapi.c b/block/qapi.c index 3b46f97b8d..ec0f5139e2 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -306,12 +306,12 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, info->io_status = blk_iostatus(blk); } - if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { + if (bs && !QLIST_EMPTY(&bs->dirty_bitmaps)) { info->has_dirty_bitmaps = true; info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); } - if (bs->drv) { + if (bs && bs->drv) { info->has_inserted = true; info->inserted = bdrv_block_device_info(bs, errp); if (info->inserted == NULL) { diff --git a/blockdev.c b/blockdev.c index 433c4e2967..c9271d2c96 100644 --- a/blockdev.c +++ b/blockdev.c @@ -124,14 +124,16 @@ void blockdev_mark_auto_del(BlockBackend *blk) return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + if (bs) { + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); - if (bs->job) { - block_job_cancel(bs->job); - } + if (bs->job) { + block_job_cancel(bs->job); + } - aio_context_release(aio_context); + aio_context_release(aio_context); + } dinfo->auto_del = 1; } @@ -229,8 +231,8 @@ bool drive_check_orphaned(void) dinfo->type != IF_NONE) { fprintf(stderr, "Warning: Orphaned drive without device: " "id=%s,file=%s,if=%s,bus=%d,unit=%d\n", - blk_name(blk), blk_bs(blk)->filename, if_name[dinfo->type], - dinfo->bus, dinfo->unit); + blk_name(blk), blk_bs(blk) ? blk_bs(blk)->filename : "", + if_name[dinfo->type], dinfo->bus, dinfo->unit); rs = true; } } @@ -1038,6 +1040,10 @@ void hmp_commit(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Device '%s' not found\n", device); return; } + if (!blk_is_available(blk)) { + monitor_printf(mon, "Device '%s' has no medium\n", device); + return; + } ret = bdrv_commit(blk_bs(blk)); } if (ret < 0) { @@ -1117,7 +1123,9 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, "Device '%s' not found", device); return NULL; } - bs = blk_bs(blk); + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); if (!has_id) { id = NULL; @@ -1129,11 +1137,14 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, if (!id && !name) { error_setg(errp, "Name or id must be provided"); - return NULL; + goto out_aio_context; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out_aio_context; + } + bs = blk_bs(blk); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { goto out_aio_context; @@ -1307,16 +1318,16 @@ static void internal_snapshot_prepare(BlkTransactionState *common, "Device '%s' not found", device); return; } - bs = blk_bs(blk); /* AioContext is released in .clean() */ - state->aio_context = bdrv_get_aio_context(bs); + state->aio_context = blk_get_aio_context(blk); aio_context_acquire(state->aio_context); - if (!bdrv_is_inserted(bs)) { + if (!blk_is_available(blk)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; } + bs = blk_bs(blk); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { return; @@ -1568,7 +1579,6 @@ typedef struct DriveBackupState { static void drive_backup_prepare(BlkTransactionState *common, Error **errp) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); - BlockDriverState *bs; BlockBackend *blk; DriveBackup *backup; Error *local_err = NULL; @@ -1582,10 +1592,9 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) "Device '%s' not found", backup->device); return; } - bs = blk_bs(blk); /* AioContext is released in .clean() */ - state->aio_context = bdrv_get_aio_context(bs); + state->aio_context = blk_get_aio_context(blk); aio_context_acquire(state->aio_context); qmp_drive_backup(backup->device, backup->target, @@ -1602,7 +1611,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) return; } - state->bs = bs; + state->bs = blk_bs(blk); state->job = state->bs->job; } @@ -1637,8 +1646,7 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) { BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackup *backup; - BlockDriverState *bs, *target; - BlockBackend *blk; + BlockBackend *blk, *target; Error *local_err = NULL; assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); @@ -1649,18 +1657,16 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) error_setg(errp, "Device '%s' not found", backup->device); return; } - bs = blk_bs(blk); - blk = blk_by_name(backup->target); - if (!blk) { + target = blk_by_name(backup->target); + if (!target) { error_setg(errp, "Device '%s' not found", backup->target); return; } - target = blk_bs(blk); /* AioContext is released in .clean() */ - state->aio_context = bdrv_get_aio_context(bs); - if (state->aio_context != bdrv_get_aio_context(target)) { + state->aio_context = blk_get_aio_context(blk); + if (state->aio_context != blk_get_aio_context(target)) { state->aio_context = NULL; error_setg(errp, "Backup between two IO threads is not implemented"); return; @@ -1678,7 +1684,7 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) return; } - state->bs = bs; + state->bs = blk_bs(blk); state->job = state->bs->job; } @@ -1816,6 +1822,11 @@ static void eject_device(BlockBackend *blk, int force, Error **errp) BlockDriverState *bs = blk_bs(blk); AioContext *aio_context; + if (!bs) { + /* No medium inserted, so there is nothing to do */ + return; + } + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -1882,7 +1893,8 @@ void qmp_block_passwd(bool has_device, const char *device, } /* Assumes AioContext is held */ -static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, +static void qmp_bdrv_open_encrypted(BlockDriverState **pbs, + const char *filename, int bdrv_flags, const char *format, const char *password, Error **errp) { @@ -1895,13 +1907,13 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, qdict_put(options, "driver", qstring_from_str(format)); } - ret = bdrv_open(&bs, filename, NULL, options, bdrv_flags, &local_err); + ret = bdrv_open(pbs, filename, NULL, options, bdrv_flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); return; } - bdrv_add_key(bs, password, errp); + bdrv_add_key(*pbs, password, errp); } void qmp_change_blockdev(const char *device, const char *filename, @@ -1911,6 +1923,7 @@ void qmp_change_blockdev(const char *device, const char *filename, BlockDriverState *bs; AioContext *aio_context; int bdrv_flags; + bool new_bs; Error *err = NULL; blk = blk_by_name(device); @@ -1920,8 +1933,9 @@ void qmp_change_blockdev(const char *device, const char *filename, return; } bs = blk_bs(blk); + new_bs = !bs; - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); eject_device(blk, 0, &err); @@ -1930,10 +1944,21 @@ void qmp_change_blockdev(const char *device, const char *filename, goto out; } - bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; - bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; + bdrv_flags = blk_is_read_only(blk) ? 0 : BDRV_O_RDWR; + bdrv_flags |= blk_get_root_state(blk)->open_flags & ~BDRV_O_RDWR; - qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, format, NULL, errp); + qmp_bdrv_open_encrypted(&bs, filename, bdrv_flags, format, NULL, &err); + if (err) { + error_propagate(errp, err); + goto out; + } + + if (new_bs) { + blk_insert_bs(blk, bs); + /* Has been sent automatically by bdrv_open() if blk_bs(blk) was not + * NULL */ + blk_dev_change_media_cb(blk, true); + } out: aio_context_release(aio_context); @@ -1973,7 +1998,15 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, "Device '%s' not found", device); return; } + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); + bs = blk_bs(blk); + if (!bs) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } memset(&cfg, 0, sizeof(cfg)); cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; @@ -2008,12 +2041,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, } if (!check_throttle_config(&cfg, errp)) { - return; + goto out; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - if (throttle_enabled(&cfg)) { /* Enable I/O limits if they're not enabled yet, otherwise * just update the throttling group. */ @@ -2029,6 +2059,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, bdrv_io_limits_disable(bs); } +out: aio_context_release(aio_context); } @@ -2141,7 +2172,6 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) error_report("Device '%s' not found", id); return; } - bs = blk_bs(blk); if (!blk_legacy_dinfo(blk)) { error_report("Deleting device added with blockdev-add" @@ -2149,16 +2179,19 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) return; } - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { - error_report_err(local_err); - aio_context_release(aio_context); - return; - } + bs = blk_bs(blk); + if (bs) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { + error_report_err(local_err); + aio_context_release(aio_context); + return; + } - bdrv_close(bs); + bdrv_close(bs); + } /* if we have a device attached to this BlockDriverState * then we need to make the drive anonymous until the device @@ -2291,11 +2324,16 @@ void qmp_block_stream(const char *device, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) { goto out; } @@ -2366,11 +2404,16 @@ void qmp_block_commit(const char *device, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { goto out; } @@ -2476,17 +2519,17 @@ void qmp_drive_backup(const char *device, const char *target, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); /* Although backup_run has this check too, we need to use bs->drv below, so * do an early check redundantly. */ - if (!bdrv_is_inserted(bs)) { + if (!blk_is_available(blk)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; } + bs = blk_bs(blk); if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; @@ -2583,7 +2626,7 @@ void qmp_blockdev_backup(const char *device, const char *target, BlockdevOnError on_target_error, Error **errp) { - BlockBackend *blk; + BlockBackend *blk, *target_blk; BlockDriverState *bs; BlockDriverState *target_bs; Error *local_err = NULL; @@ -2604,17 +2647,27 @@ void qmp_blockdev_backup(const char *device, const char *target, error_setg(errp, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - blk = blk_by_name(target); - if (!blk) { + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + + target_blk = blk_by_name(target); + if (!target_blk) { error_setg(errp, "Device '%s' not found", target); goto out; } - target_bs = blk_bs(blk); + + if (!blk_is_available(target_blk)) { + error_setg(errp, "Device '%s' has no medium", target); + goto out; + } + target_bs = blk_bs(target_blk); bdrv_ref(target_bs); bdrv_set_aio_context(target_bs, aio_context); @@ -2691,15 +2744,15 @@ void qmp_drive_mirror(const char *device, const char *target, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); - if (!bdrv_is_inserted(bs)) { + if (!blk_is_available(blk)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; } + bs = blk_bs(blk); if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; @@ -2829,17 +2882,22 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context, BlockBackend *blk; BlockDriverState *bs; + *aio_context = NULL; + blk = blk_by_name(device); if (!blk) { goto notfound; } - bs = blk_bs(blk); - *aio_context = bdrv_get_aio_context(bs); + *aio_context = blk_get_aio_context(blk); aio_context_acquire(*aio_context); + if (!blk_is_available(blk)) { + goto notfound; + } + bs = blk_bs(blk); + if (!bs->job) { - aio_context_release(*aio_context); goto notfound; } @@ -2848,7 +2906,10 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context, notfound: error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, "No active block job on device '%s'", device); - *aio_context = NULL; + if (*aio_context) { + aio_context_release(*aio_context); + *aio_context = NULL; + } return NULL; } @@ -2955,11 +3016,16 @@ void qmp_change_backing_file(const char *device, "Device '%s' not found", device); return; } - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); + aio_context = blk_get_aio_context(blk); aio_context_acquire(aio_context); + if (!blk_is_available(blk)) { + error_setg(errp, "Device '%s' has no medium", device); + goto out; + } + bs = blk_bs(blk); + image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 36d7398f4f..1bbc111939 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -931,9 +931,11 @@ static int blk_connect(struct XenDevice *xendev) blk_attach_dev_nofail(blkdev->blk, blkdev); blkdev->file_size = blk_getlength(blkdev->blk); if (blkdev->file_size < 0) { + BlockDriverState *bs = blk_bs(blkdev->blk); + const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), - bdrv_get_format_name(blk_bs(blkdev->blk)) ?: "-"); + drv_name ?: "-"); blkdev->file_size = 0; } diff --git a/migration/block.c b/migration/block.c index ed865ed23b..f7bb1e0126 100644 --- a/migration/block.c +++ b/migration/block.c @@ -808,6 +808,11 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) return -EINVAL; } bs = blk_bs(blk); + if (!bs) { + fprintf(stderr, "Block device %s has no medium\n", + device_name); + return -EINVAL; + } if (bs != bs_prev) { bs_prev = bs; diff --git a/monitor.c b/monitor.c index 4f1ba2f666..301a143ffa 100644 --- a/monitor.c +++ b/monitor.c @@ -4145,6 +4145,10 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, monitor_printf(mon, "Device not found %s\n", device); return -1; } + if (!blk_bs(blk)) { + monitor_printf(mon, "Device '%s' has no medium\n", device); + return -1; + } bdrv_add_key(blk_bs(blk), NULL, &err); if (err) { -- cgit 1.4.1 From 5ec18f8c83583b7e22ed4dd360cd937da801ca40 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 19 Oct 2015 17:53:30 +0200 Subject: blockdev: Do not create BDS for empty drive Do not use "rudimentary" BDSs for empty drives any longer (for freshly created drives). After a follow-up patch, empty drives will generally use a NULL BDS, not only the freshly created drives. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- blockdev.c | 68 ++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 28 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index c9271d2c96..faa5218188 100644 --- a/blockdev.c +++ b/blockdev.c @@ -512,16 +512,40 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, goto early_err; } + if (snapshot) { + /* always use cache=unsafe with snapshot */ + bdrv_flags &= ~BDRV_O_CACHE_MASK; + bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); + } + + if (copy_on_read) { + bdrv_flags |= BDRV_O_COPY_ON_READ; + } + + bdrv_flags |= ro ? 0 : BDRV_O_RDWR; + /* init */ if ((!file || !*file) && !has_driver_specific_opts) { - blk = blk_new_with_bs(qemu_opts_id(opts), errp); + BlockBackendRootState *blk_rs; + + blk = blk_new(qemu_opts_id(opts), errp); if (!blk) { goto early_err; } - bs = blk_bs(blk); - bs->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; - bs->read_only = ro; + blk_rs = blk_get_root_state(blk); + blk_rs->open_flags = bdrv_flags; + blk_rs->read_only = ro; + blk_rs->detect_zeroes = detect_zeroes; + + if (throttle_enabled(&cfg)) { + if (!throttling_group) { + throttling_group = blk_name(blk); + } + blk_rs->throttle_group = g_strdup(throttling_group); + blk_rs->throttle_state = throttle_group_incref(throttling_group); + blk_rs->throttle_state->cfg = cfg; + } QDECREF(bs_opts); } else { @@ -529,42 +553,30 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, file = NULL; } - if (snapshot) { - /* always use cache=unsafe with snapshot */ - bdrv_flags &= ~BDRV_O_CACHE_MASK; - bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); - } - - if (copy_on_read) { - bdrv_flags |= BDRV_O_COPY_ON_READ; - } - - bdrv_flags |= ro ? 0 : BDRV_O_RDWR; - blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags, errp); if (!blk) { goto err_no_bs_opts; } bs = blk_bs(blk); - } - bs->detect_zeroes = detect_zeroes; + bs->detect_zeroes = detect_zeroes; - blk_set_on_error(blk, on_read_error, on_write_error); + /* disk I/O throttling */ + if (throttle_enabled(&cfg)) { + if (!throttling_group) { + throttling_group = blk_name(blk); + } + bdrv_io_limits_enable(bs, throttling_group); + bdrv_set_io_limits(bs, &cfg); + } - /* disk I/O throttling */ - if (throttle_enabled(&cfg)) { - if (!throttling_group) { - throttling_group = blk_name(blk); + if (bdrv_key_required(bs)) { + autostart = 0; } - bdrv_io_limits_enable(bs, throttling_group); - bdrv_set_io_limits(bs, &cfg); } - if (bdrv_key_required(bs)) { - autostart = 0; - } + blk_set_on_error(blk, on_read_error, on_write_error); err_no_bs_opts: qemu_opts_del(opts); -- cgit 1.4.1 From fbf8175eac92cca541efb1ac4a202fba941b78df Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 19 Oct 2015 17:53:31 +0200 Subject: blockdev: Pull out blockdev option extraction Extract some of the blockdev option extraction code from blockdev_init() into its own function. This simplifies blockdev_init() and will allow reusing the code in a different function added in a follow-up patch. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- blockdev.c | 213 ++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 119 insertions(+), 94 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index faa5218188..dd4885ff94 100644 --- a/blockdev.c +++ b/blockdev.c @@ -350,25 +350,134 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType; +/* All parameters but @opts are optional and may be set to NULL. */ +static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, + const char **throttling_group, ThrottleConfig *throttle_cfg, + BlockdevDetectZeroesOptions *detect_zeroes, Error **errp) +{ + const char *discard; + Error *local_error = NULL; + const char *aio; + + if (bdrv_flags) { + if (!qemu_opt_get_bool(opts, "read-only", false)) { + *bdrv_flags |= BDRV_O_RDWR; + } + if (qemu_opt_get_bool(opts, "copy-on-read", false)) { + *bdrv_flags |= BDRV_O_COPY_ON_READ; + } + + if ((discard = qemu_opt_get(opts, "discard")) != NULL) { + if (bdrv_parse_discard_flags(discard, bdrv_flags) != 0) { + error_setg(errp, "Invalid discard option"); + return; + } + } + + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) { + *bdrv_flags |= BDRV_O_CACHE_WB; + } + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) { + *bdrv_flags |= BDRV_O_NOCACHE; + } + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { + *bdrv_flags |= BDRV_O_NO_FLUSH; + } + + if ((aio = qemu_opt_get(opts, "aio")) != NULL) { + if (!strcmp(aio, "native")) { + *bdrv_flags |= BDRV_O_NATIVE_AIO; + } else if (!strcmp(aio, "threads")) { + /* this is the default */ + } else { + error_setg(errp, "invalid aio option"); + return; + } + } + } + + /* disk I/O throttling */ + if (throttling_group) { + *throttling_group = qemu_opt_get(opts, "throttling.group"); + } + + if (throttle_cfg) { + memset(throttle_cfg, 0, sizeof(*throttle_cfg)); + throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg = + qemu_opt_get_number(opts, "throttling.bps-total", 0); + throttle_cfg->buckets[THROTTLE_BPS_READ].avg = + qemu_opt_get_number(opts, "throttling.bps-read", 0); + throttle_cfg->buckets[THROTTLE_BPS_WRITE].avg = + qemu_opt_get_number(opts, "throttling.bps-write", 0); + throttle_cfg->buckets[THROTTLE_OPS_TOTAL].avg = + qemu_opt_get_number(opts, "throttling.iops-total", 0); + throttle_cfg->buckets[THROTTLE_OPS_READ].avg = + qemu_opt_get_number(opts, "throttling.iops-read", 0); + throttle_cfg->buckets[THROTTLE_OPS_WRITE].avg = + qemu_opt_get_number(opts, "throttling.iops-write", 0); + + throttle_cfg->buckets[THROTTLE_BPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.bps-total-max", 0); + throttle_cfg->buckets[THROTTLE_BPS_READ].max = + qemu_opt_get_number(opts, "throttling.bps-read-max", 0); + throttle_cfg->buckets[THROTTLE_BPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.bps-write-max", 0); + throttle_cfg->buckets[THROTTLE_OPS_TOTAL].max = + qemu_opt_get_number(opts, "throttling.iops-total-max", 0); + throttle_cfg->buckets[THROTTLE_OPS_READ].max = + qemu_opt_get_number(opts, "throttling.iops-read-max", 0); + throttle_cfg->buckets[THROTTLE_OPS_WRITE].max = + qemu_opt_get_number(opts, "throttling.iops-write-max", 0); + + throttle_cfg->op_size = + qemu_opt_get_number(opts, "throttling.iops-size", 0); + + if (!check_throttle_config(throttle_cfg, errp)) { + return; + } + } + + if (detect_zeroes) { + *detect_zeroes = + qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, + qemu_opt_get(opts, "detect-zeroes"), + BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, + &local_error); + if (local_error) { + error_propagate(errp, local_error); + return; + } + + if (bdrv_flags && + *detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + !(*bdrv_flags & BDRV_O_UNMAP)) + { + error_setg(errp, "setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + return; + } + } +} + /* Takes the ownership of bs_opts */ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, Error **errp) { const char *buf; - int ro = 0; int bdrv_flags = 0; int on_read_error, on_write_error; BlockBackend *blk; BlockDriverState *bs; ThrottleConfig cfg; int snapshot = 0; - bool copy_on_read; Error *error = NULL; QemuOpts *opts; const char *id; bool has_driver_specific_opts; - BlockdevDetectZeroesOptions detect_zeroes; - const char *throttling_group; + BlockdevDetectZeroesOptions detect_zeroes = + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + const char *throttling_group = NULL; /* Check common options by copying from bs_opts to opts, all other options * stay in bs_opts for processing by bdrv_open(). */ @@ -393,35 +502,12 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, /* extract parameters */ snapshot = qemu_opt_get_bool(opts, "snapshot", 0); - ro = qemu_opt_get_bool(opts, "read-only", 0); - copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false); - - if ((buf = qemu_opt_get(opts, "discard")) != NULL) { - if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { - error_setg(errp, "invalid discard option"); - goto early_err; - } - } - - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) { - bdrv_flags |= BDRV_O_CACHE_WB; - } - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) { - bdrv_flags |= BDRV_O_NOCACHE; - } - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { - bdrv_flags |= BDRV_O_NO_FLUSH; - } - if ((buf = qemu_opt_get(opts, "aio")) != NULL) { - if (!strcmp(buf, "native")) { - bdrv_flags |= BDRV_O_NATIVE_AIO; - } else if (!strcmp(buf, "threads")) { - /* this is the default */ - } else { - error_setg(errp, "invalid aio option"); - goto early_err; - } + extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg, + &detect_zeroes, &error); + if (error) { + error_propagate(errp, error); + goto early_err; } if ((buf = qemu_opt_get(opts, "format")) != NULL) { @@ -439,43 +525,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, qdict_put(bs_opts, "driver", qstring_from_str(buf)); } - /* disk I/O throttling */ - memset(&cfg, 0, sizeof(cfg)); - cfg.buckets[THROTTLE_BPS_TOTAL].avg = - qemu_opt_get_number(opts, "throttling.bps-total", 0); - cfg.buckets[THROTTLE_BPS_READ].avg = - qemu_opt_get_number(opts, "throttling.bps-read", 0); - cfg.buckets[THROTTLE_BPS_WRITE].avg = - qemu_opt_get_number(opts, "throttling.bps-write", 0); - cfg.buckets[THROTTLE_OPS_TOTAL].avg = - qemu_opt_get_number(opts, "throttling.iops-total", 0); - cfg.buckets[THROTTLE_OPS_READ].avg = - qemu_opt_get_number(opts, "throttling.iops-read", 0); - cfg.buckets[THROTTLE_OPS_WRITE].avg = - qemu_opt_get_number(opts, "throttling.iops-write", 0); - - cfg.buckets[THROTTLE_BPS_TOTAL].max = - qemu_opt_get_number(opts, "throttling.bps-total-max", 0); - cfg.buckets[THROTTLE_BPS_READ].max = - qemu_opt_get_number(opts, "throttling.bps-read-max", 0); - cfg.buckets[THROTTLE_BPS_WRITE].max = - qemu_opt_get_number(opts, "throttling.bps-write-max", 0); - cfg.buckets[THROTTLE_OPS_TOTAL].max = - qemu_opt_get_number(opts, "throttling.iops-total-max", 0); - cfg.buckets[THROTTLE_OPS_READ].max = - qemu_opt_get_number(opts, "throttling.iops-read-max", 0); - cfg.buckets[THROTTLE_OPS_WRITE].max = - qemu_opt_get_number(opts, "throttling.iops-write-max", 0); - - cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); - - throttling_group = qemu_opt_get(opts, "throttling.group"); - - if (!check_throttle_config(&cfg, &error)) { - error_propagate(errp, error); - goto early_err; - } - on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { on_write_error = parse_block_error_action(buf, 0, &error); @@ -494,36 +543,12 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, } } - detect_zeroes = - qapi_enum_parse(BlockdevDetectZeroesOptions_lookup, - qemu_opt_get(opts, "detect-zeroes"), - BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, - BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, - &error); - if (error) { - error_propagate(errp, error); - goto early_err; - } - - if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && - !(bdrv_flags & BDRV_O_UNMAP)) { - error_setg(errp, "setting detect-zeroes to unmap is not allowed " - "without setting discard operation to unmap"); - goto early_err; - } - if (snapshot) { /* always use cache=unsafe with snapshot */ bdrv_flags &= ~BDRV_O_CACHE_MASK; bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); } - if (copy_on_read) { - bdrv_flags |= BDRV_O_COPY_ON_READ; - } - - bdrv_flags |= ro ? 0 : BDRV_O_RDWR; - /* init */ if ((!file || !*file) && !has_driver_specific_opts) { BlockBackendRootState *blk_rs; @@ -535,7 +560,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, blk_rs = blk_get_root_state(blk); blk_rs->open_flags = bdrv_flags; - blk_rs->read_only = ro; + blk_rs->read_only = !(bdrv_flags & BDRV_O_RDWR); blk_rs->detect_zeroes = detect_zeroes; if (throttle_enabled(&cfg)) { -- cgit 1.4.1 From bd745e238bb9432f40c875b6b4ad96a3d90e16a0 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Mon, 19 Oct 2015 17:53:32 +0200 Subject: blockdev: Allow more options for BB-less BDS tree Most of the options which blockdev_init() parses for both the BlockBackend and the root BDS are valid for just the root BDS as well (e.g. read-only). This patch allows specifying these options even if not creating a BlockBackend. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- blockdev.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 6 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index dd4885ff94..35e571980f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -614,6 +614,54 @@ err_no_opts: return NULL; } +static QemuOptsList qemu_root_bds_opts; + +/* Takes the ownership of bs_opts */ +static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) +{ + BlockDriverState *bs; + QemuOpts *opts; + Error *local_error = NULL; + BlockdevDetectZeroesOptions detect_zeroes; + int ret; + int bdrv_flags = 0; + + opts = qemu_opts_create(&qemu_root_bds_opts, NULL, 1, errp); + if (!opts) { + goto fail; + } + + qemu_opts_absorb_qdict(opts, bs_opts, &local_error); + if (local_error) { + error_propagate(errp, local_error); + goto fail; + } + + extract_common_blockdev_options(opts, &bdrv_flags, NULL, NULL, + &detect_zeroes, &local_error); + if (local_error) { + error_propagate(errp, local_error); + goto fail; + } + + bs = NULL; + ret = bdrv_open(&bs, NULL, NULL, bs_opts, bdrv_flags, errp); + if (ret < 0) { + goto fail_no_bs_opts; + } + + bs->detect_zeroes = detect_zeroes; + +fail_no_bs_opts: + qemu_opts_del(opts); + return bs; + +fail: + qemu_opts_del(opts); + QDECREF(bs_opts); + return NULL; +} + static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, Error **errp) { @@ -3171,18 +3219,14 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) bs = blk_bs(blk); } else { - int ret; - if (!qdict_get_try_str(qdict, "node-name")) { error_setg(errp, "'id' and/or 'node-name' need to be specified for " "the root node"); goto fail; } - bs = NULL; - ret = bdrv_open(&bs, NULL, NULL, qdict, BDRV_O_RDWR | BDRV_O_CACHE_WB, - errp); - if (ret < 0) { + bs = bds_tree_init(qdict, errp); + if (!bs) { goto fail; } } @@ -3337,6 +3381,47 @@ QemuOptsList qemu_common_drive_opts = { }, }; +static QemuOptsList qemu_root_bds_opts = { + .name = "root-bds", + .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), + .desc = { + { + .name = "discard", + .type = QEMU_OPT_STRING, + .help = "discard operation (ignore/off, unmap/on)", + },{ + .name = "cache.writeback", + .type = QEMU_OPT_BOOL, + .help = "enables writeback mode for any caches", + },{ + .name = "cache.direct", + .type = QEMU_OPT_BOOL, + .help = "enables use of O_DIRECT (bypass the host page cache)", + },{ + .name = "cache.no-flush", + .type = QEMU_OPT_BOOL, + .help = "ignore any flush requests for the device", + },{ + .name = "aio", + .type = QEMU_OPT_STRING, + .help = "host AIO implementation (threads, native)", + },{ + .name = "read-only", + .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", + },{ + .name = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + },{ + .name = "detect-zeroes", + .type = QEMU_OPT_STRING, + .help = "try to optimize zero writes (off, on, unmap)", + }, + { /* end of list */ } + }, +}; + QemuOptsList qemu_drive_opts = { .name = "drive", .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), -- cgit 1.4.1 From da763e83012c066ebe3effbaa8e7e7c8f78b0fbf Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 23 Oct 2015 11:08:10 +0800 Subject: block: Add "drained begin/end" for transactional external snapshot This ensures the atomicity of the transaction by avoiding processing of external requests such as those from ioeventfd. Signed-off-by: Fam Zheng Reviewed-by: Jeff Cody Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index 35e571980f..e4a5eb43b8 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1569,6 +1569,7 @@ static void external_snapshot_prepare(BlkTransactionState *common, /* Acquire AioContext now so any threads operating on old_bs stop */ state->aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(state->aio_context); + bdrv_drained_begin(state->old_bs); if (!bdrv_is_inserted(state->old_bs)) { error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); @@ -1638,8 +1639,6 @@ static void external_snapshot_commit(BlkTransactionState *common) * don't want to abort all of them if one of them fails the reopen */ bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR, NULL); - - aio_context_release(state->aio_context); } static void external_snapshot_abort(BlkTransactionState *common) @@ -1649,7 +1648,14 @@ static void external_snapshot_abort(BlkTransactionState *common) if (state->new_bs) { bdrv_unref(state->new_bs); } +} + +static void external_snapshot_clean(BlkTransactionState *common) +{ + ExternalSnapshotState *state = + DO_UPCAST(ExternalSnapshotState, common, common); if (state->aio_context) { + bdrv_drained_end(state->old_bs); aio_context_release(state->aio_context); } } @@ -1809,6 +1815,7 @@ static const BdrvActionOps actions[] = { .prepare = external_snapshot_prepare, .commit = external_snapshot_commit, .abort = external_snapshot_abort, + .clean = external_snapshot_clean, }, [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = { .instance_size = sizeof(DriveBackupState), -- cgit 1.4.1 From 1fdd4b7be3655d39c3594bc215eb1df5ce225c7d Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 23 Oct 2015 11:08:11 +0800 Subject: block: Add "drained begin/end" for transactional backup This ensures the atomicity of the transaction by avoiding processing of external requests such as those from ioeventfd. Move the assignment to state->bs up right after bdrv_drained_begin, so that we can use it in the clean callback. The abort callback will still check bs->job and state->job, so it's OK. Signed-off-by: Fam Zheng Reviewed-by: Jeff Cody Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index e4a5eb43b8..0a7848bd66 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1684,9 +1684,16 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) return; } + if (!blk_is_available(blk)) { + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, backup->device); + return; + } + /* AioContext is released in .clean() */ state->aio_context = blk_get_aio_context(blk); aio_context_acquire(state->aio_context); + bdrv_drained_begin(blk_bs(blk)); + state->bs = blk_bs(blk); qmp_drive_backup(backup->device, backup->target, backup->has_format, backup->format, @@ -1702,7 +1709,6 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) return; } - state->bs = blk_bs(blk); state->job = state->bs->job; } @@ -1722,6 +1728,7 @@ static void drive_backup_clean(BlkTransactionState *common) DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); if (state->aio_context) { + bdrv_drained_end(state->bs); aio_context_release(state->aio_context); } } -- cgit 1.4.1 From ff52bf36a3a6c63b7528fbe64e9ed9976b221e68 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 23 Oct 2015 11:08:12 +0800 Subject: block: Add "drained begin/end" for transactional blockdev-backup Similar to the previous patch, make sure that external events are not dispatched during transaction operations. Signed-off-by: Fam Zheng Reviewed-by: Jeff Cody Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index 0a7848bd66..52f44b2867 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1756,6 +1756,11 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) return; } + if (!blk_is_available(blk)) { + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, backup->device); + return; + } + target = blk_by_name(backup->target); if (!target) { error_setg(errp, "Device '%s' not found", backup->target); @@ -1770,6 +1775,8 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) return; } aio_context_acquire(state->aio_context); + state->bs = blk_bs(blk); + bdrv_drained_begin(state->bs); qmp_blockdev_backup(backup->device, backup->target, backup->sync, @@ -1782,7 +1789,6 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) return; } - state->bs = blk_bs(blk); state->job = state->bs->job; } @@ -1802,6 +1808,7 @@ static void blockdev_backup_clean(BlkTransactionState *common) BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); if (state->aio_context) { + bdrv_drained_end(state->bs); aio_context_release(state->aio_context); } } -- cgit 1.4.1 From 507306cc8ee7981894a96c380f42d80e2674cb04 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Fri, 23 Oct 2015 11:08:13 +0800 Subject: block: Add "drained begin/end" for internal snapshot This ensures the atomicity of the transaction by avoiding processing of external requests such as those from ioeventfd. state->bs is assigned right after bdrv_drained_begin. Because it was used as the flag for deletion or not in abort, now we need a separate flag - InternalSnapshotState.created. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- blockdev.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'blockdev.c') diff --git a/blockdev.c b/blockdev.c index 52f44b2867..18712d25cc 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1370,6 +1370,7 @@ typedef struct InternalSnapshotState { BlockDriverState *bs; AioContext *aio_context; QEMUSnapshotInfo sn; + bool created; } InternalSnapshotState; static void internal_snapshot_prepare(BlkTransactionState *common, @@ -1414,6 +1415,9 @@ static void internal_snapshot_prepare(BlkTransactionState *common, } bs = blk_bs(blk); + state->bs = bs; + bdrv_drained_begin(bs); + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { return; } @@ -1465,7 +1469,7 @@ static void internal_snapshot_prepare(BlkTransactionState *common, } /* 4. succeed, mark a snapshot is created */ - state->bs = bs; + state->created = true; } static void internal_snapshot_abort(BlkTransactionState *common) @@ -1476,7 +1480,7 @@ static void internal_snapshot_abort(BlkTransactionState *common) QEMUSnapshotInfo *sn = &state->sn; Error *local_error = NULL; - if (!bs) { + if (!state->created) { return; } @@ -1497,6 +1501,9 @@ static void internal_snapshot_clean(BlkTransactionState *common) common, common); if (state->aio_context) { + if (state->bs) { + bdrv_drained_end(state->bs); + } aio_context_release(state->aio_context); } } -- cgit 1.4.1