#include "block/block_int.h"
#include "block/blockjob.h"
#include "block/nbd.h"
+#include "block/qdict.h"
#include "qemu/error-report.h"
#include "module_block.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qnull.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/qobject-output-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "qemu/notify.h"
#include "qemu/coroutine.h"
#include "block/qapi.h"
#include "qemu/timer.h"
-#include "qapi-event.h"
#include "qemu/cutils.h"
#include "qemu/id.h"
return bdrv_do_find_format(format_name);
}
-static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
+int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
{
static const char *whitelist_rw[] = {
CONFIG_BDRV_RW_WHITELIST
CreateCo *cco = opaque;
assert(cco->drv);
- ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
+ ret = cco->drv->bdrv_co_create_opts(cco->filename, cco->opts, &local_err);
error_propagate(&cco->err, local_err);
cco->ret = ret;
}
.err = NULL,
};
- if (!drv->bdrv_create) {
+ if (!drv->bdrv_co_create_opts) {
error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
ret = -ENOTSUP;
goto out;
static void bdrv_child_cb_drained_begin(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
- bdrv_drained_begin(bs);
+ bdrv_do_drained_begin_quiesce(bs, NULL);
+}
+
+static bool bdrv_child_cb_drained_poll(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ return bdrv_drain_poll(bs, false, NULL);
}
static void bdrv_child_cb_drained_end(BdrvChild *child)
.get_parent_desc = bdrv_child_get_parent_desc,
.inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin,
+ .drained_poll = bdrv_child_cb_drained_poll,
.drained_end = bdrv_child_cb_drained_end,
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.get_parent_desc = bdrv_child_get_parent_desc,
.inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin,
+ .drained_poll = bdrv_child_cb_drained_poll,
.drained_end = bdrv_child_cb_drained_end,
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.detach = bdrv_backing_detach,
.inherit_options = bdrv_backing_options,
.drained_begin = bdrv_child_cb_drained_begin,
+ .drained_poll = bdrv_child_cb_drained_poll,
.drained_end = bdrv_child_cb_drained_end,
.inactivate = bdrv_child_cb_inactivate,
.update_filename = bdrv_backing_update_filename,
ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
if (ret < 0) {
- QDECREF(bs->explicit_options);
+ qobject_unref(bs->explicit_options);
bs->explicit_options = NULL;
- QDECREF(bs->options);
+ qobject_unref(bs->options);
bs->options = NULL;
bdrv_unref(bs);
return NULL;
return NULL;
}
- options = qobject_to_qdict(options_obj);
+ options = qobject_to(QDict, options_obj);
if (!options) {
- qobject_decref(options_obj);
+ qobject_unref(options_obj);
error_setg(errp, "Invalid JSON object given");
return NULL;
}
/* Options given in the filename have lower priority than options
* specified directly */
qdict_join(options, json_options, false);
- QDECREF(json_options);
+ qobject_unref(json_options);
*pfilename = NULL;
}
/* Returns whether the image file can be written to after the reopen queue @q
* has been successfully applied, or right now if @q is NULL. */
-static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q)
+static bool bdrv_is_writable_after_reopen(BlockDriverState *bs,
+ BlockReopenQueue *q)
{
int flags = bdrv_reopen_get_flags(q, bs);
return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR;
}
+/*
+ * Return whether the BDS can be written to. This is not necessarily
+ * the same as !bdrv_is_read_only(bs), as inactivated images may not
+ * be written to but do not count as read-only images.
+ */
+bool bdrv_is_writable(BlockDriverState *bs)
+{
+ return bdrv_is_writable_after_reopen(bs, NULL);
+}
+
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
BdrvChild *c, const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
/* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
- !bdrv_is_writable(bs, q))
+ !bdrv_is_writable_after_reopen(bs, q))
{
error_setg(errp, "Block node is read-only");
return -EPERM;
&perm, &shared);
/* Format drivers may touch metadata even if the guest doesn't write */
- if (bdrv_is_writable(bs, reopen_queue)) {
+ if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
if (reference || qdict_haskey(options, "file.filename")) {
backing_filename[0] = '\0';
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
- QDECREF(options);
+ qobject_unref(options);
goto free_exit;
} else {
bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX,
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
- QDECREF(options);
+ qobject_unref(options);
goto free_exit;
}
}
if (!bs->drv || !bs->drv->supports_backing) {
ret = -EINVAL;
error_setg(errp, "Driver doesn't support backing files");
- QDECREF(options);
+ qobject_unref(options);
goto free_exit;
}
free_exit:
g_free(backing_filename);
- QDECREF(tmp_parent_options);
+ qobject_unref(tmp_parent_options);
return ret;
}
error_setg(errp, "A block device must be specified for \"%s\"",
bdref_key);
}
- QDECREF(image_options);
+ qobject_unref(image_options);
goto done;
}
return c;
}
+/* TODO Future callers may need to specify parent/child_role in order for
+ * option inheritance to work. Existing callers use it for the root node. */
+BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
+{
+ BlockDriverState *bs = NULL;
+ Error *local_err = NULL;
+ QObject *obj = NULL;
+ QDict *qdict = NULL;
+ const char *reference = NULL;
+ Visitor *v = NULL;
+
+ if (ref->type == QTYPE_QSTRING) {
+ reference = ref->u.reference;
+ } else {
+ BlockdevOptions *options = &ref->u.definition;
+ assert(ref->type == QTYPE_QDICT);
+
+ v = qobject_output_visitor_new(&obj);
+ visit_type_BlockdevOptions(v, NULL, &options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+ visit_complete(v, &obj);
+
+ qdict = qobject_to(QDict, obj);
+ qdict_flatten(qdict);
+
+ /* bdrv_open_inherit() defaults to the values in bdrv_flags (for
+ * compatibility with other callers) rather than what we want as the
+ * real defaults. Apply the defaults here instead. */
+ qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
+ qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
+ qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
+ }
+
+ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
+ obj = NULL;
+
+fail:
+ qobject_unref(obj);
+ visit_free(v);
+ return bs;
+}
+
static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
int flags,
QDict *snapshot_options,
}
out:
- QDECREF(snapshot_options);
+ qobject_unref(snapshot_options);
g_free(tmp_filename);
return bs_snapshot;
}
* options is a QDict of options to pass to the block drivers, or NULL for an
* empty set of options. The reference to the QDict belongs to the block layer
* after the call (even on failure), so if the caller intends to reuse the
- * dictionary, it needs to use QINCREF() before calling bdrv_open.
+ * dictionary, it needs to use qobject_ref() before calling bdrv_open.
*
* If *pbs is NULL, a new BDS will be created with a pointer to it stored there.
* If it is not NULL, the referenced BDS will be reused.
if (reference) {
bool options_non_empty = options ? qdict_size(options) : false;
- QDECREF(options);
+ qobject_unref(options);
if (filename || options_non_empty) {
error_setg(errp, "Cannot reference an existing block device with "
/* See cautionary note on accessing @options above */
backing = qdict_get_try_str(options, "backing");
- if (backing && *backing == '\0') {
+ if (qobject_to(QNull, qdict_get(options, "backing")) != NULL ||
+ (backing && *backing == '\0'))
+ {
+ if (backing) {
+ warn_report("Use of \"backing\": \"\" is deprecated; "
+ "use \"backing\": null instead");
+ }
flags |= BDRV_O_NO_BACKING;
qdict_del(options, "backing");
}
bdrv_parent_cb_change_media(bs, true);
- QDECREF(options);
+ qobject_unref(options);
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
* temporary snapshot afterwards. */
fail:
blk_unref(file);
- QDECREF(snapshot_options);
- QDECREF(bs->explicit_options);
- QDECREF(bs->options);
- QDECREF(options);
+ qobject_unref(snapshot_options);
+ qobject_unref(bs->explicit_options);
+ qobject_unref(bs->options);
+ qobject_unref(options);
bs->options = NULL;
bs->explicit_options = NULL;
bdrv_unref(bs);
close_and_fail:
bdrv_unref(bs);
- QDECREF(snapshot_options);
- QDECREF(options);
+ qobject_unref(snapshot_options);
+ qobject_unref(options);
error_propagate(errp, local_err);
return NULL;
}
old_options = qdict_clone_shallow(bs->explicit_options);
}
bdrv_join_options(bs, options, old_options);
- QDECREF(old_options);
+ qobject_unref(old_options);
explicit_options = qdict_clone_shallow(options);
/* Inherit from parent node */
if (parent_options) {
+ QemuOpts *opts;
+ QDict *options_copy;
assert(!flags);
role->inherit_options(&flags, options, parent_flags, parent_options);
+ options_copy = qdict_clone_shallow(options);
+ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options_copy, NULL);
+ update_flags_from_options(&flags, opts);
+ qemu_opts_del(opts);
+ qobject_unref(options_copy);
}
/* Old values are used for options that aren't set yet */
old_options = qdict_clone_shallow(bs->options);
bdrv_join_options(bs, options, old_options);
- QDECREF(old_options);
+ qobject_unref(old_options);
/* bdrv_open_inherit() sets and clears some additional flags internally */
flags &= ~BDRV_O_PROTOCOL;
bs_entry = g_new0(BlockReopenQueueEntry, 1);
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
} else {
- QDECREF(bs_entry->state.options);
- QDECREF(bs_entry->state.explicit_options);
+ qobject_unref(bs_entry->state.options);
+ qobject_unref(bs_entry->state.explicit_options);
}
bs_entry->state.bs = bs;
if (ret && bs_entry->prepared) {
bdrv_reopen_abort(&bs_entry->state);
} else if (ret) {
- QDECREF(bs_entry->state.explicit_options);
+ qobject_unref(bs_entry->state.explicit_options);
}
- QDECREF(bs_entry->state.options);
+ qobject_unref(bs_entry->state.options);
g_free(bs_entry);
}
g_free(bs_queue);
}
/* set BDS specific flags now */
- QDECREF(bs->explicit_options);
+ qobject_unref(bs->explicit_options);
bs->explicit_options = reopen_state->explicit_options;
bs->open_flags = reopen_state->flags;
drv->bdrv_reopen_abort(reopen_state);
}
- QDECREF(reopen_state->explicit_options);
+ qobject_unref(reopen_state->explicit_options);
bdrv_abort_perm_update(reopen_state->bs);
}
bs->total_sectors = 0;
bs->encrypted = false;
bs->sg = false;
- QDECREF(bs->options);
- QDECREF(bs->explicit_options);
+ qobject_unref(bs->options);
+ qobject_unref(bs->explicit_options);
bs->options = NULL;
bs->explicit_options = NULL;
- QDECREF(bs->full_open_options);
+ qobject_unref(bs->full_open_options);
bs->full_open_options = NULL;
bdrv_release_named_dirty_bitmaps(bs);
void bdrv_close_all(void)
{
- block_job_cancel_sync_all();
+ assert(job_next(NULL) == NULL);
nbd_export_close_all();
/* Drop references from requests still in flight, such as canceled block
* free of errors) or -errno when an internal error occurred. The results of the
* check are stored in res.
*/
-int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix)
+static int coroutine_fn bdrv_co_check(BlockDriverState *bs,
+ BdrvCheckResult *res, BdrvCheckMode fix)
{
if (bs->drv == NULL) {
return -ENOMEDIUM;
}
- if (bs->drv->bdrv_check == NULL) {
+ if (bs->drv->bdrv_co_check == NULL) {
return -ENOTSUP;
}
memset(res, 0, sizeof(*res));
- return bs->drv->bdrv_check(bs, res, fix);
+ return bs->drv->bdrv_co_check(bs, res, fix);
+}
+
+typedef struct CheckCo {
+ BlockDriverState *bs;
+ BdrvCheckResult *res;
+ BdrvCheckMode fix;
+ int ret;
+} CheckCo;
+
+static void bdrv_check_co_entry(void *opaque)
+{
+ CheckCo *cco = opaque;
+ cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
+}
+
+int bdrv_check(BlockDriverState *bs,
+ BdrvCheckResult *res, BdrvCheckMode fix)
+{
+ Coroutine *co;
+ CheckCo cco = {
+ .bs = bs,
+ .res = res,
+ .ret = -EINPROGRESS,
+ .fix = fix,
+ };
+
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_check_co_entry(&cco);
+ } else {
+ co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
+ qemu_coroutine_enter(co);
+ BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
+ }
+
+ return cco.ret;
}
/*
GSList *ignore_children = g_slist_prepend(NULL, c);
bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
ignore_children, &local_err);
+ g_slist_free(ignore_children);
if (local_err) {
ret = -EPERM;
error_report_err(local_err);
goto exit;
}
- g_slist_free(ignore_children);
/* If so, update the backing file path in the image file */
if (c->role->update_filename) {
error_setg(errp, "No medium inserted");
return -ENOMEDIUM;
}
+ if (offset < 0) {
+ error_setg(errp, "Image size cannot be negative");
+ return -EINVAL;
+ }
+
if (!drv->bdrv_truncate) {
if (bs->file && drv->is_filter) {
return bdrv_truncate(bs->file, offset, prealloc, errp);
bdrv_init();
}
-void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
+static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
+ Error **errp)
{
BdrvChild *child, *parent;
uint64_t perm, shared_perm;
}
QLIST_FOREACH(child, &bs->children, next) {
- bdrv_invalidate_cache(child->bs, &local_err);
+ bdrv_co_invalidate_cache(child->bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
bdrv_set_perm(bs, perm, shared_perm);
- if (bs->drv->bdrv_invalidate_cache) {
- bs->drv->bdrv_invalidate_cache(bs, &local_err);
+ if (bs->drv->bdrv_co_invalidate_cache) {
+ bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
if (local_err) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
}
}
+typedef struct InvalidateCacheCo {
+ BlockDriverState *bs;
+ Error **errp;
+ bool done;
+} InvalidateCacheCo;
+
+static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
+{
+ InvalidateCacheCo *ico = opaque;
+ bdrv_co_invalidate_cache(ico->bs, ico->errp);
+ ico->done = true;
+}
+
+void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
+{
+ Coroutine *co;
+ InvalidateCacheCo ico = {
+ .bs = bs,
+ .done = false,
+ .errp = errp
+ };
+
+ if (qemu_in_coroutine()) {
+ /* Fast-path if already in coroutine context */
+ bdrv_invalidate_cache_co_entry(&ico);
+ } else {
+ co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
+ qemu_coroutine_enter(co);
+ BDRV_POLL_WHILE(bs, !ico.done);
+ }
+}
+
void bdrv_invalidate_cache_all(Error **errp)
{
BlockDriverState *bs;
AioContext *bdrv_get_aio_context(BlockDriverState *bs)
{
- return bs->aio_context;
+ return bs ? bs->aio_context : qemu_get_aio_context();
+}
+
+AioWait *bdrv_get_aio_wait(BlockDriverState *bs)
+{
+ return bs ? &bs->wait : NULL;
}
void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
}
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
- BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
+ BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ Error **errp)
{
if (!bs->drv) {
+ error_setg(errp, "Node is ejected");
return -ENOMEDIUM;
}
if (!bs->drv->bdrv_amend_options) {
+ error_setg(errp, "Block driver '%s' does not support option amendment",
+ bs->drv->format_name);
return -ENOTSUP;
}
- return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque);
+ return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
}
/* This function will be called by the bdrv_recurse_is_first_non_filter method
continue;
}
- qobject_incref(qdict_entry_value(entry));
- qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
+ qdict_put_obj(d, qdict_entry_key(entry),
+ qobject_ref(qdict_entry_value(entry)));
found_any = true;
}
* information before refreshing it */
bs->exact_filename[0] = '\0';
if (bs->full_open_options) {
- QDECREF(bs->full_open_options);
+ qobject_unref(bs->full_open_options);
bs->full_open_options = NULL;
}
opts = qdict_new();
append_open_options(opts, bs);
drv->bdrv_refresh_filename(bs, opts);
- QDECREF(opts);
+ qobject_unref(opts);
} else if (bs->file) {
/* Try to reconstruct valid information from the underlying file */
bool has_open_options;
bs->exact_filename[0] = '\0';
if (bs->full_open_options) {
- QDECREF(bs->full_open_options);
+ qobject_unref(bs->full_open_options);
bs->full_open_options = NULL;
}
* suffices without querying the (exact_)filename of this BDS. */
if (bs->file->bs->full_open_options) {
qdict_put_str(opts, "driver", drv->format_name);
- QINCREF(bs->file->bs->full_open_options);
- qdict_put(opts, "file", bs->file->bs->full_open_options);
+ qdict_put(opts, "file",
+ qobject_ref(bs->file->bs->full_open_options));
bs->full_open_options = opts;
} else {
- QDECREF(opts);
+ qobject_unref(opts);
}
} else if (!bs->full_open_options && qdict_size(bs->options)) {
/* There is no underlying file BDS (at least referenced by BDS.file),
QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
snprintf(bs->filename, sizeof(bs->filename), "json:%s",
qstring_get_str(json));
- QDECREF(json);
+ qobject_unref(json);
}
}