#include "qemu/main-loop.h"
#include "qemu/throttle-options.h"
-static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
+QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
+void bdrv_set_monitor_owned(BlockDriverState *bs)
+{
+ QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
+}
+
static const char *const if_name[IF_COUNT] = {
[IF_NONE] = "none",
[IF_IDE] = "ide",
return NULL;
}
+void drive_mark_claimed_by_board(void)
+{
+ BlockBackend *blk;
+ DriveInfo *dinfo;
+
+ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
+ dinfo = blk_legacy_dinfo(blk);
+ if (dinfo && blk_get_attached_dev(blk)) {
+ dinfo->claimed_by_board = true;
+ }
+ }
+}
+
void drive_check_orphaned(void)
{
BlockBackend *blk;
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) {
dinfo = blk_legacy_dinfo(blk);
- if (!blk_get_attached_dev(blk) && !dinfo->is_default &&
- dinfo->type != IF_NONE) {
+ if (dinfo->is_default || dinfo->type == IF_NONE) {
+ continue;
+ }
+ if (!blk_get_attached_dev(blk)) {
loc_push_none(&loc);
qemu_opts_loc_restore(dinfo->opts);
error_report("machine type does not support"
if_name[dinfo->type], dinfo->bus, dinfo->unit);
loc_pop(&loc);
orphans = true;
+ continue;
+ }
+ if (!dinfo->claimed_by_board && dinfo->type != IF_VIRTIO) {
+ loc_push_none(&loc);
+ qemu_opts_loc_restore(dinfo->opts);
+ warn_report("bogus if=%s is deprecated, use if=none",
+ if_name[dinfo->type]);
+ loc_pop(&loc);
}
}
/* Check common options by copying from bs_opts to opts, all other options
* stay in bs_opts for processing by bdrv_open(). */
id = qdict_get_try_str(bs_opts, "id");
- opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
- if (error) {
- error_propagate(errp, error);
+ opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, errp);
+ if (!opts) {
goto err_no_opts;
}
- qemu_opts_absorb_qdict(opts, bs_opts, &error);
- if (error) {
- error_propagate(errp, error);
+ if (!qemu_opts_absorb_qdict(opts, bs_opts, errp)) {
goto early_err;
}
}
/* Takes the ownership of bs_opts */
-static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
+BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
{
int bdrv_flags = 0;
: QTAILQ_FIRST(&monitor_bdrv_states);
}
-static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
+static bool qemu_opt_rename(QemuOpts *opts, const char *from, const char *to,
Error **errp)
{
const char *value;
if (qemu_opt_find(opts, to)) {
error_setg(errp, "'%s' and its alias '%s' can't be used at the "
"same time", to, from);
- return;
+ return false;
}
}
qemu_opt_set(opts, to, value, &error_abort);
qemu_opt_unset(opts, from);
}
+ return true;
}
QemuOptsList qemu_legacy_drive_opts = {
bool read_only = false;
bool copy_on_read;
const char *filename;
- Error *local_err = NULL;
int i;
/* Change legacy command line options into QMP ones */
};
for (i = 0; i < ARRAY_SIZE(opt_renames); i++) {
- qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to,
- &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!qemu_opt_rename(all_opts, opt_renames[i].from,
+ opt_renames[i].to, errp)) {
return NULL;
}
}
legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
&error_abort);
- qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!qemu_opts_absorb_qdict(legacy_opts, bs_opts, errp)) {
goto fail;
}
}
/* Actual block device init: Functionality shared with blockdev-add */
- blk = blockdev_init(filename, bs_opts, &local_err);
+ blk = blockdev_init(filename, bs_opts, errp);
bs_opts = NULL;
if (!blk) {
- error_propagate(errp, local_err);
goto fail;
- } else {
- assert(!local_err);
}
/* Create legacy DriveInfo */
return bs;
}
-void hmp_commit(Monitor *mon, const QDict *qdict)
-{
- const char *device = qdict_get_str(qdict, "device");
- BlockBackend *blk;
- int ret;
-
- if (!strcmp(device, "all")) {
- ret = blk_commit_all();
- } else {
- BlockDriverState *bs;
- AioContext *aio_context;
-
- blk = blk_by_name(device);
- if (!blk) {
- error_report("Device '%s' not found", device);
- return;
- }
- if (!blk_is_available(blk)) {
- error_report("Device '%s' has no medium", device);
- return;
- }
-
- bs = blk_bs(blk);
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
-
- ret = bdrv_commit(bs);
-
- aio_context_release(aio_context);
- }
- if (ret < 0) {
- error_report("'commit' error for '%s': %s", device, strerror(-ret));
- }
-}
-
static void blockdev_do_action(TransactionAction *action, Error **errp)
{
TransactionActionList list;
return NULL;
}
-/**
- * block_dirty_bitmap_lookup:
- * Return a dirty bitmap (if present), after validating
- * the node reference and bitmap names.
- *
- * @node: The name of the BDS node to search for bitmaps
- * @name: The name of the bitmap to search for
- * @pbs: Output pointer for BDS lookup, if desired. Can be NULL.
- * @errp: Output pointer for error information. Can be NULL.
- *
- * @return: A bitmap object on success, or NULL on failure.
- */
-static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
- const char *name,
- BlockDriverState **pbs,
- Error **errp)
-{
- BlockDriverState *bs;
- BdrvDirtyBitmap *bitmap;
-
- if (!node) {
- error_setg(errp, "Node cannot be NULL");
- return NULL;
- }
- if (!name) {
- error_setg(errp, "Bitmap name cannot be NULL");
- return NULL;
- }
- bs = bdrv_lookup_bs(node, node, NULL);
- if (!bs) {
- error_setg(errp, "Node '%s' not found", node);
- return NULL;
- }
-
- bitmap = bdrv_find_dirty_bitmap(bs, name);
- if (!bitmap) {
- error_setg(errp, "Dirty bitmap '%s' not found", name);
- return NULL;
- }
-
- if (pbs) {
- *pbs = bs;
- }
-
- return bitmap;
-}
-
/* New and old BlockDriverState structs for atomic group operations */
typedef struct BlkActionState BlkActionState;
DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action;
AioContext *aio_context;
- AioContext *old_context;
- int ret;
+ uint64_t perm, shared;
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */
goto out;
}
- if (bdrv_has_blk(state->new_bs)) {
+ /*
+ * Allow attaching a backing file to an overlay that's already in use only
+ * if the parents don't assume that they are already seeing a valid image.
+ * (Specifically, allow it as a mirror target, which is write-only access.)
+ */
+ bdrv_get_cumulative_perm(state->new_bs, &perm, &shared);
+ if (perm & BLK_PERM_CONSISTENT_READ) {
error_setg(errp, "The overlay is already in use");
goto out;
}
- if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
- errp)) {
- goto out;
- }
-
if (state->new_bs->backing != NULL) {
error_setg(errp, "The overlay already has a backing image");
goto out;
goto out;
}
- /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
- old_context = bdrv_get_aio_context(state->new_bs);
- aio_context_release(aio_context);
- aio_context_acquire(old_context);
-
- ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp);
-
- aio_context_release(old_context);
- aio_context_acquire(aio_context);
-
- if (ret < 0) {
- goto out;
- }
-
/* This removes our old bs and adds the new bs. This is an operation that
* can fail, so we need to do it in .prepare; undoing it for abort is
* always possible. */
}
}
-static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(
- const char *node, const char *target,
- BlockDirtyBitmapMergeSourceList *bitmaps,
- HBitmap **backup, Error **errp);
-
static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
Error **errp)
{
action = common->action->u.block_dirty_bitmap_merge.data;
- state->bitmap = do_block_dirty_bitmap_merge(action->node, action->target,
- action->bitmaps, &state->backup,
- errp);
+ state->bitmap = block_dirty_bitmap_merge(action->node, action->target,
+ action->bitmaps, &state->backup,
+ errp);
}
-static BdrvDirtyBitmap *do_block_dirty_bitmap_remove(
- const char *node, const char *name, bool release,
- BlockDriverState **bitmap_bs, Error **errp);
-
static void block_dirty_bitmap_remove_prepare(BlkActionState *common,
Error **errp)
{
action = common->action->u.block_dirty_bitmap_remove.data;
- state->bitmap = do_block_dirty_bitmap_remove(action->node, action->name,
- false, &state->bs, errp);
+ state->bitmap = block_dirty_bitmap_remove(action->node, action->name,
+ false, &state->bs, errp);
if (state->bitmap) {
bdrv_dirty_bitmap_skip_store(state->bitmap, true);
bdrv_dirty_bitmap_set_busy(state->bitmap, true);
"Setting block passwords directly is no longer supported");
}
-void qmp_block_dirty_bitmap_add(const char *node, const char *name,
- bool has_granularity, uint32_t granularity,
- bool has_persistent, bool persistent,
- bool has_disabled, bool disabled,
- Error **errp)
-{
- BlockDriverState *bs;
- BdrvDirtyBitmap *bitmap;
- AioContext *aio_context;
-
- if (!name || name[0] == '\0') {
- error_setg(errp, "Bitmap name cannot be empty");
- return;
- }
-
- bs = bdrv_lookup_bs(node, node, errp);
- if (!bs) {
- return;
- }
-
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
-
- if (has_granularity) {
- if (granularity < 512 || !is_power_of_2(granularity)) {
- error_setg(errp, "Granularity must be power of 2 "
- "and at least 512");
- goto out;
- }
- } else {
- /* Default to cluster size, if available: */
- granularity = bdrv_get_default_bitmap_granularity(bs);
- }
-
- if (!has_persistent) {
- persistent = false;
- }
-
- if (!has_disabled) {
- disabled = false;
- }
-
- if (persistent &&
- !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
- {
- goto out;
- }
-
- bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
- if (bitmap == NULL) {
- goto out;
- }
-
- if (disabled) {
- bdrv_disable_dirty_bitmap(bitmap);
- }
-
- bdrv_dirty_bitmap_set_persistence(bitmap, persistent);
-
-out:
- aio_context_release(aio_context);
-}
-
-static BdrvDirtyBitmap *do_block_dirty_bitmap_remove(
- const char *node, const char *name, bool release,
- BlockDriverState **bitmap_bs, Error **errp)
-{
- BlockDriverState *bs;
- BdrvDirtyBitmap *bitmap;
- AioContext *aio_context;
-
- bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
- if (!bitmap || !bs) {
- return NULL;
- }
-
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
-
- if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO,
- errp)) {
- aio_context_release(aio_context);
- return NULL;
- }
-
- if (bdrv_dirty_bitmap_get_persistence(bitmap) &&
- bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0)
- {
- aio_context_release(aio_context);
- return NULL;
- }
-
- if (release) {
- bdrv_release_dirty_bitmap(bitmap);
- }
-
- if (bitmap_bs) {
- *bitmap_bs = bs;
- }
-
- aio_context_release(aio_context);
- return release ? NULL : bitmap;
-}
-
-void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
- Error **errp)
-{
- do_block_dirty_bitmap_remove(node, name, true, NULL, errp);
-}
-
-/**
- * Completely clear a bitmap, for the purposes of synchronizing a bitmap
- * immediately after a full backup operation.
- */
-void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
- Error **errp)
-{
- BdrvDirtyBitmap *bitmap;
- BlockDriverState *bs;
-
- bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
- if (!bitmap || !bs) {
- return;
- }
-
- if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
- return;
- }
-
- bdrv_clear_dirty_bitmap(bitmap, NULL);
-}
-
-void qmp_block_dirty_bitmap_enable(const char *node, const char *name,
- Error **errp)
-{
- BlockDriverState *bs;
- BdrvDirtyBitmap *bitmap;
-
- bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
- if (!bitmap) {
- return;
- }
-
- if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
- return;
- }
-
- bdrv_enable_dirty_bitmap(bitmap);
-}
-
-void qmp_block_dirty_bitmap_disable(const char *node, const char *name,
- Error **errp)
-{
- BlockDriverState *bs;
- BdrvDirtyBitmap *bitmap;
-
- bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
- if (!bitmap) {
- return;
- }
-
- if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
- return;
- }
-
- bdrv_disable_dirty_bitmap(bitmap);
-}
-
-static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(
- const char *node, const char *target,
- BlockDirtyBitmapMergeSourceList *bitmaps,
- HBitmap **backup, Error **errp)
-{
- BlockDriverState *bs;
- BdrvDirtyBitmap *dst, *src, *anon;
- BlockDirtyBitmapMergeSourceList *lst;
- Error *local_err = NULL;
-
- dst = block_dirty_bitmap_lookup(node, target, &bs, errp);
- if (!dst) {
- return NULL;
- }
-
- anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst),
- NULL, errp);
- if (!anon) {
- return NULL;
- }
-
- for (lst = bitmaps; lst; lst = lst->next) {
- switch (lst->value->type) {
- const char *name, *node;
- case QTYPE_QSTRING:
- name = lst->value->u.local;
- src = bdrv_find_dirty_bitmap(bs, name);
- if (!src) {
- error_setg(errp, "Dirty bitmap '%s' not found", name);
- dst = NULL;
- goto out;
- }
- break;
- case QTYPE_QDICT:
- node = lst->value->u.external.node;
- name = lst->value->u.external.name;
- src = block_dirty_bitmap_lookup(node, name, NULL, errp);
- if (!src) {
- dst = NULL;
- goto out;
- }
- break;
- default:
- abort();
- }
-
- bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- dst = NULL;
- goto out;
- }
- }
-
- /* Merge into dst; dst is unchanged on failure. */
- bdrv_merge_dirty_bitmap(dst, anon, backup, errp);
-
- out:
- bdrv_release_dirty_bitmap(anon);
- return dst;
-}
-
-void qmp_block_dirty_bitmap_merge(const char *node, const char *target,
- BlockDirtyBitmapMergeSourceList *bitmaps,
- Error **errp)
-{
- do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp);
-}
-
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
const char *name,
Error **errp)
return ret;
}
-void hmp_drive_del(Monitor *mon, const QDict *qdict)
-{
- const char *id = qdict_get_str(qdict, "id");
- BlockBackend *blk;
- BlockDriverState *bs;
- AioContext *aio_context;
- Error *local_err = NULL;
-
- bs = bdrv_find_node(id);
- if (bs) {
- qmp_blockdev_del(id, &local_err);
- if (local_err) {
- error_report_err(local_err);
- }
- return;
- }
-
- blk = blk_by_name(id);
- if (!blk) {
- error_report("Device '%s' not found", id);
- return;
- }
-
- if (!blk_legacy_dinfo(blk)) {
- error_report("Deleting device added with blockdev-add"
- " is not supported");
- return;
- }
-
- aio_context = blk_get_aio_context(blk);
- aio_context_acquire(aio_context);
-
- 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;
- }
-
- blk_remove_bs(blk);
- }
-
- /* Make the BlockBackend and the attached BlockDriverState anonymous */
- monitor_remove_blk(blk);
-
- /* If this BlockBackend has a device attached to it, its refcount will be
- * decremented when the device is removed; otherwise we have to do so here.
- */
- if (blk_get_attached_dev(blk)) {
- /* Further I/O must not pause the guest */
- blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT,
- BLOCKDEV_ON_ERROR_REPORT);
- } else {
- blk_unref(blk);
- }
-
- aio_context_release(aio_context);
-}
-
void qmp_block_resize(bool has_device, const char *device,
bool has_node_name, const char *node_name,
int64_t size, Error **errp)
BlockBackend *blk = NULL;
BlockDriverState *bs;
AioContext *aio_context;
- int ret;
bs = bdrv_lookup_bs(has_device ? device : NULL,
has_node_name ? node_name : NULL,
goto out;
}
- blk = blk_new(bdrv_get_aio_context(bs), BLK_PERM_RESIZE, BLK_PERM_ALL);
- ret = blk_insert_bs(blk, bs, errp);
- if (ret < 0) {
+ blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
+ if (!blk) {
goto out;
}
bdrv_drained_begin(bs);
- ret = blk_truncate(blk, size, false, PREALLOC_MODE_OFF, errp);
+ blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
bdrv_drained_end(bs);
out:
arg->has_copy_mode, arg->copy_mode,
arg->has_auto_finalize, arg->auto_finalize,
arg->has_auto_dismiss, arg->auto_dismiss,
- &local_err);
+ errp);
bdrv_unref(target_bs);
- error_propagate(errp, local_err);
out:
aio_context_release(aio_context);
}
AioContext *aio_context;
AioContext *old_context;
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
- Error *local_err = NULL;
bool zero_target;
int ret;
has_copy_mode, copy_mode,
has_auto_finalize, auto_finalize,
has_auto_dismiss, auto_dismiss,
- &local_err);
- error_propagate(errp, local_err);
+ errp);
out:
aio_context_release(aio_context);
}
}
trace_qmp_block_job_finalize(job);
+ job_ref(&job->job);
job_finalize(&job->job, errp);
+
+ /*
+ * Job's context might have changed via job_finalize (and job_txn_apply
+ * automatically acquires the new one), so make sure we release the correct
+ * one.
+ */
+ aio_context = blk_get_aio_context(job->blk);
+ job_unref(&job->job);
aio_context_release(aio_context);
}
}
ret = bdrv_change_backing_file(image_bs, backing_file,
- image_bs->drv ? image_bs->drv->format_name : "");
+ image_bs->drv ? image_bs->drv->format_name : "",
+ false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not change backing file to '%s'",
}
if (ro) {
- bdrv_reopen_set_read_only(image_bs, true, &local_err);
- error_propagate(errp, local_err);
+ bdrv_reopen_set_read_only(image_bs, true, errp);
}
out:
aio_context_release(aio_context);
}
-void hmp_drive_add_node(Monitor *mon, const char *optstr)
-{
- QemuOpts *opts;
- QDict *qdict;
- Error *local_err = NULL;
-
- opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false);
- if (!opts) {
- return;
- }
-
- qdict = qemu_opts_to_qdict(opts, NULL);
-
- if (!qdict_get_try_str(qdict, "node-name")) {
- qobject_unref(qdict);
- error_report("'node-name' needs to be specified");
- goto out;
- }
-
- BlockDriverState *bs = bds_tree_init(qdict, &local_err);
- if (!bs) {
- error_report_err(local_err);
- goto out;
- }
-
- QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
-
-out:
- qemu_opts_del(opts);
-}
-
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
{
BlockDriverState *bs;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
QDict *qdict;
- Error *local_err = NULL;
-
- visit_type_BlockdevOptions(v, NULL, &options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto fail;
- }
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
visit_complete(v, &obj);
qdict = qobject_to(QDict, obj);
goto fail;
}
- QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list);
+ bdrv_set_monitor_owned(bs);
fail:
visit_free(v);
AioContext *ctx;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
- Error *local_err = NULL;
BlockReopenQueue *queue;
QDict *qdict;
}
/* Put all options in a QDict and flatten it */
- visit_type_BlockdevOptions(v, NULL, &options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto fail;
- }
-
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
visit_complete(v, &obj);
qdict = qobject_to(QDict, obj);