* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+
#include "qemu/osdep.h"
#include "block/trace.h"
#include "block/block_int.h"
#include "qemu/error-report.h"
#include "module_block.h"
#include "qemu/module.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qbool.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qdict.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/option.h"
#include "qemu/coroutine.h"
#include "block/qapi.h"
-#include "qmp-commands.h"
#include "qemu/timer.h"
-#include "qapi-event.h"
#include "qemu/cutils.h"
#include "qemu/id.h"
return 0;
}
+/* TODO Remove (deprecated since 2.11)
+ * Block drivers are not supposed to automatically change bs->read_only.
+ * Instead, they should just check whether they can provide what the user
+ * explicitly requested and error out if read-write is requested, but they can
+ * only provide read-only access. */
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
{
int ret = 0;
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;
{
BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+
/* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
if (bdrv_is_sg(bs))
return 0;
bdrv_drained_end(bs);
}
+static void bdrv_child_cb_attach(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ bdrv_apply_subtree_drain(child, bs);
+}
+
+static void bdrv_child_cb_detach(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ bdrv_unapply_subtree_drain(child, bs);
+}
+
static int bdrv_child_cb_inactivate(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
.inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .attach = bdrv_child_cb_attach,
+ .detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
};
.inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .attach = bdrv_child_cb_attach,
+ .detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
};
parent->backing_blocker);
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
parent->backing_blocker);
+
+ bdrv_child_cb_attach(c);
}
static void bdrv_backing_detach(BdrvChild *c)
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
error_free(parent->backing_blocker);
parent->backing_blocker = NULL;
+
+ bdrv_child_cb_detach(c);
}
/*
return NULL;
}
- options = qobject_to_qdict(options_obj);
+ options = qobject_to(QDict, options_obj);
if (!options) {
qobject_decref(options_obj);
error_setg(errp, "Invalid JSON object given");
assert(role == &child_backing || role == &child_file);
if (!backing) {
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
+
/* Apart from the modifications below, the same permissions are
* forwarded and left alone as for filters */
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
/* bs->file always needs to be consistent because of the metadata. We
* can never allow other users to resize or write to it. */
- perm |= BLK_PERM_CONSISTENT_READ;
+ if (!(flags & BDRV_O_NO_IO)) {
+ perm |= BLK_PERM_CONSISTENT_READ;
+ }
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
} else {
/* We want consistent read from backing files if the parent needs it.
BlockDriverState *new_bs)
{
BlockDriverState *old_bs = child->bs;
+ int i;
if (old_bs && new_bs) {
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
}
if (old_bs) {
- if (old_bs->quiesce_counter && child->role->drained_end) {
- child->role->drained_end(child);
- }
+ /* Detach first so that the recursive drain sections coming from @child
+ * are already gone and we only end the drain sections that came from
+ * elsewhere. */
if (child->role->detach) {
child->role->detach(child);
}
+ if (old_bs->quiesce_counter && child->role->drained_end) {
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
+ child->role->drained_end(child);
+ }
+ }
QLIST_REMOVE(child, next_parent);
}
if (new_bs) {
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
if (new_bs->quiesce_counter && child->role->drained_begin) {
- child->role->drained_begin(child);
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
+ child->role->drained_begin(child);
+ }
}
+ /* Attach only after starting new drained sections, so that recursive
+ * drain sections coming from @child don't get an extra .drained_begin
+ * callback. */
if (child->role->attach) {
child->role->attach(child);
}
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_decref(obj);
+ visit_free(v);
+ return bs;
+}
+
static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
int flags,
QDict *snapshot_options,
/* 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");
}
goto fail;
}
if (file_bs != NULL) {
- file = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
+ /* Not requesting BLK_PERM_CONSISTENT_READ because we're only
+ * looking at the header to guess the image format. This works even
+ * in cases where a guest would not see a consistent state. */
+ file = blk_new(0, BLK_PERM_ALL);
blk_insert_bs(file, file_bs, &local_err);
bdrv_unref(file_bs);
if (local_err) {
* returns a pointer to bs_queue, which is either the newly allocated
* bs_queue, or the existing bs_queue being used.
*
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
*/
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
BlockDriverState *bs,
BdrvChild *child;
QDict *old_options, *explicit_options;
+ /* Make sure that the caller remembered to use a drained section. This is
+ * important to avoid graph changes between the recursive queuing here and
+ * bdrv_reopen_multiple(). */
+ assert(bs->quiesce_counter > 0);
+
if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1);
QSIMPLEQ_INIT(bs_queue);
/* 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);
+ QDECREF(options_copy);
}
/* Old values are used for options that aren't set yet */
* If all devices prepare successfully, then the changes are committed
* to all devices.
*
+ * All affected nodes must be drained between bdrv_reopen_queue() and
+ * bdrv_reopen_multiple().
*/
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
{
assert(bs_queue != NULL);
- aio_context_release(ctx);
- bdrv_drain_all_begin();
- aio_context_acquire(ctx);
-
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
+ assert(bs_entry->state.bs->quiesce_counter > 0);
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
error_propagate(errp, local_err);
goto cleanup;
}
g_free(bs_queue);
- bdrv_drain_all_end();
-
return ret;
}
{
int ret = -1;
Error *local_err = NULL;
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
+ BlockReopenQueue *queue;
+
+ bdrv_subtree_drained_begin(bs);
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
+
+ bdrv_subtree_drained_end(bs);
+
return ret;
}
const QDictEntry *entry = qdict_first(reopen_state->options);
do {
- QString *new_obj = qobject_to_qstring(entry->value);
- const char *new = qstring_get_str(new_obj);
+ QObject *new = entry->value;
+ QObject *old = qdict_get(reopen_state->bs->options, entry->key);
+
/*
- * Caution: while qdict_get_try_str() is fine, getting
- * non-string types would require more care. When
- * bs->options come from -blockdev or blockdev_add, its
- * members are typed according to the QAPI schema, but
- * when they come from -drive, they're all QString.
+ * TODO: When using -drive to specify blockdev options, all values
+ * will be strings; however, when using -blockdev, blockdev-add or
+ * filenames using the json:{} pseudo-protocol, they will be
+ * correctly typed.
+ * In contrast, reopening options are (currently) always strings
+ * (because you can only specify them through qemu-io; all other
+ * callers do not specify any options).
+ * Therefore, when using anything other than -drive to create a BDS,
+ * this cannot detect non-string options as unchanged, because
+ * qobject_is_equal() always returns false for objects of different
+ * type. In the future, this should be remedied by correctly typing
+ * all options. For now, this is not too big of an issue because
+ * the user can simply omit options which cannot be changed anyway,
+ * so they will stay unchanged.
*/
- const char *old = qdict_get_try_str(reopen_state->bs->options,
- entry->key);
-
- if (!old || strcmp(new, old)) {
+ if (!qobject_is_equal(new, old)) {
error_setg(errp, "Cannot change the option '%s'", entry->key);
ret = -EINVAL;
goto error;
static void bdrv_close(BlockDriverState *bs)
{
BdrvAioNotifier *ban, *ban_next;
+ BdrvChild *child, *next;
assert(!bs->job);
assert(!bs->refcnt);
bdrv_drain(bs); /* in case flush left pending I/O */
if (bs->drv) {
- BdrvChild *child, *next;
-
bs->drv->bdrv_close(bs);
bs->drv = NULL;
+ }
- bdrv_set_backing_hd(bs, NULL, &error_abort);
+ bdrv_set_backing_hd(bs, NULL, &error_abort);
- if (bs->file != NULL) {
- bdrv_unref_child(bs, bs->file);
- bs->file = NULL;
- }
+ if (bs->file != NULL) {
+ bdrv_unref_child(bs, bs->file);
+ bs->file = NULL;
+ }
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
- /* TODO Remove bdrv_unref() from drivers' close function and use
- * bdrv_unref_child() here */
- if (child->bs->inherits_from == bs) {
- child->bs->inherits_from = NULL;
- }
- bdrv_detach_child(child);
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
+ /* TODO Remove bdrv_unref() from drivers' close function and use
+ * bdrv_unref_child() here */
+ if (child->bs->inherits_from == bs) {
+ child->bs->inherits_from = NULL;
}
-
- g_free(bs->opaque);
- bs->opaque = NULL;
- atomic_set(&bs->copy_on_read, 0);
- bs->backing_file[0] = '\0';
- bs->backing_format[0] = '\0';
- bs->total_sectors = 0;
- bs->encrypted = false;
- bs->sg = false;
- QDECREF(bs->options);
- QDECREF(bs->explicit_options);
- bs->options = NULL;
- bs->explicit_options = NULL;
- QDECREF(bs->full_open_options);
- bs->full_open_options = NULL;
+ bdrv_detach_child(child);
}
+ g_free(bs->opaque);
+ bs->opaque = NULL;
+ atomic_set(&bs->copy_on_read, 0);
+ bs->backing_file[0] = '\0';
+ bs->backing_format[0] = '\0';
+ bs->total_sectors = 0;
+ bs->encrypted = false;
+ bs->sg = false;
+ QDECREF(bs->options);
+ QDECREF(bs->explicit_options);
+ bs->options = NULL;
+ bs->explicit_options = NULL;
+ QDECREF(bs->full_open_options);
+ bs->full_open_options = NULL;
+
bdrv_release_named_dirty_bitmaps(bs);
assert(QLIST_EMPTY(&bs->dirty_bitmaps));
* 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;
}
/*
BlockDriver *drv = bs->drv;
int ret;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+
/* Backing file format doesn't make sense without a backing file */
if (backing_fmt && !backing_file) {
return -EINVAL;
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);
int bdrv_has_zero_init(BlockDriverState *bs)
{
- assert(bs->drv);
+ if (!bs->drv) {
+ return 0;
+ }
/* If BS is a copy on write image, it is initialized to
the contents of the base image, which may not be zeroes. */
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs)
{
- BlockDriverInfo bdi;
-
if (!(bs->open_flags & BDRV_O_UNMAP)) {
return false;
}
- if (bdrv_get_info(bs, &bdi) == 0) {
- return bdi.can_write_zeroes_with_unmap;
- }
-
- return false;
+ return bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP;
}
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
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;
}
}
+ /*
+ * Update permissions, they may differ for inactive nodes.
+ *
+ * Note that the required permissions of inactive images are always a
+ * subset of the permissions required after activating the image. This
+ * allows us to just get the permissions upfront without restricting
+ * drv->bdrv_invalidate_cache().
+ *
+ * It also means that in error cases, we don't have to try and revert to
+ * the old permissions (which is an operation that could fail, too). We can
+ * just keep the extended permissions for the next time that an activation
+ * of the image is tried.
+ */
bs->open_flags &= ~BDRV_O_INACTIVE;
- if (bs->drv->bdrv_invalidate_cache) {
- bs->drv->bdrv_invalidate_cache(bs, &local_err);
+ bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
+ ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &local_err);
+ if (ret < 0) {
+ bs->open_flags |= BDRV_O_INACTIVE;
+ error_propagate(errp, local_err);
+ return;
+ }
+ bdrv_set_perm(bs, perm, shared_perm);
+
+ 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);
return;
}
- /* Update permissions, they may differ for inactive nodes */
- bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
- ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &local_err);
- if (ret < 0) {
- bs->open_flags |= BDRV_O_INACTIVE;
- error_propagate(errp, local_err);
- return;
- }
- bdrv_set_perm(bs, perm, shared_perm);
-
QLIST_FOREACH(parent, &bs->parents, next_parent) {
if (parent->role->activate) {
parent->role->activate(parent, &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;
aio_context_release(aio_context);
if (local_err) {
error_propagate(errp, local_err);
+ bdrv_next_cleanup(&it);
return;
}
}
BdrvChild *child, *parent;
int ret;
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+
if (!setting_flag && bs->drv->bdrv_inactivate) {
ret = bs->drv->bdrv_inactivate(bs);
if (ret < 0) {
BdrvNextIterator it;
int ret = 0;
int pass;
+ GSList *aio_ctxs = NULL, *ctx;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
- aio_context_acquire(bdrv_get_aio_context(bs));
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ if (!g_slist_find(aio_ctxs, aio_context)) {
+ aio_ctxs = g_slist_prepend(aio_ctxs, aio_context);
+ aio_context_acquire(aio_context);
+ }
}
/* We do two passes of inactivation. The first pass calls to drivers'
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
ret = bdrv_inactivate_recurse(bs, pass);
if (ret < 0) {
+ bdrv_next_cleanup(&it);
goto out;
}
}
}
out:
- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
- aio_context_release(bdrv_get_aio_context(bs));
+ for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
+ AioContext *aio_context = ctx->data;
+ aio_context_release(aio_context);
}
+ g_slist_free(aio_ctxs);
return ret;
}
back_flags = flags;
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+ backing_options = qdict_new();
if (backing_fmt) {
- backing_options = qdict_new();
qdict_put_str(backing_options, "driver", backing_fmt);
}
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
&local_err);
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)
AioContext *ctx = bdrv_get_aio_context(bs);
aio_disable_external(ctx);
- bdrv_parent_drained_begin(bs);
+ bdrv_parent_drained_begin(bs, NULL);
bdrv_drain(bs); /* ensure there are no in-flight requests */
while (aio_poll(ctx, false)) {
*/
aio_context_acquire(new_context);
bdrv_attach_aio_context(bs, new_context);
- bdrv_parent_drained_end(bs);
+ bdrv_parent_drained_end(bs, NULL);
aio_enable_external(ctx);
aio_context_release(new_context);
}
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
{
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
if (!bs->drv->bdrv_amend_options) {
return -ENOTSUP;
}
/* candidate is the first non filter */
if (perm) {
+ bdrv_next_cleanup(&it);
return true;
}
}