return !(bs->open_flags & BDRV_O_RDWR);
}
-static int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
- bool ignore_allow_rdw, Error **errp)
+static int GRAPH_RDLOCK
+bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
+ bool ignore_allow_rdw, Error **errp)
{
IO_CODE();
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
BlockDriver *drv = bs->drv;
- BlockDriverState *filtered = bdrv_filter_bs(bs);
+ BlockDriverState *filtered;
+
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
if (drv && drv->bdrv_probe_geometry) {
return drv->bdrv_probe_geometry(bs, geo);
- } else if (filtered) {
+ }
+
+ filtered = bdrv_filter_bs(bs);
+ if (filtered) {
return bdrv_probe_geometry(filtered, geo);
}
return 0;
open_failed:
bs->drv = NULL;
+
+ bdrv_graph_wrlock(NULL);
if (bs->file != NULL) {
- bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, bs->file);
- bdrv_graph_wrunlock();
assert(!bs->file);
}
+ bdrv_graph_wrunlock(NULL);
+
g_free(bs->opaque);
bs->opaque = NULL;
return ret;
Error *local_err = NULL;
bool ro;
+ GLOBAL_STATE_CODE();
+
+ bdrv_graph_rdlock_main_loop();
assert(bs->file == NULL);
assert(options != NULL && bs->options != options);
- GLOBAL_STATE_CODE();
+ bdrv_graph_rdunlock_main_loop();
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) {
if (!ro && bdrv_is_whitelisted(drv, true)) {
+ bdrv_graph_rdlock_main_loop();
ret = bdrv_apply_auto_read_only(bs, NULL, NULL);
+ bdrv_graph_rdunlock_main_loop();
} else {
ret = -ENOTSUP;
}
{
assert(!child->bs);
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
assert(!child->next.le_prev); /* not in children list */
g_free(child->name);
GLOBAL_STATE_CODE();
- bdrv_graph_wrlock(child_bs);
-
child = bdrv_attach_child_common(child_bs, child_name, child_class,
child_role, perm, shared_perm, opaque,
tran, errp);
out:
tran_finalize(tran, ret);
- bdrv_graph_wrunlock();
- bdrv_unref(child_bs);
+ bdrv_schedule_unref(child_bs);
return ret < 0 ? NULL : child;
}
*
* If a backing child is already present (i.e. we're detaching a node), that
* child node must be drained.
- *
- * After calling this function, the transaction @tran may only be completed
- * while holding a writer lock for the graph.
*/
-static int GRAPH_WRLOCK
-bdrv_set_backing_noperm(BlockDriverState *bs,
- BlockDriverState *backing_hd,
- Transaction *tran, Error **errp)
-{
- GLOBAL_STATE_CODE();
- return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
-}
-
int bdrv_set_backing_hd_drained(BlockDriverState *bs,
BlockDriverState *backing_hd,
Error **errp)
if (bs->backing) {
assert(bs->backing->bs->quiesce_counter > 0);
}
- bdrv_graph_wrlock(backing_hd);
- ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
+ ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
if (ret < 0) {
goto out;
}
ret = bdrv_refresh_perms(bs, tran, errp);
out:
tran_finalize(tran, ret);
- bdrv_graph_wrunlock();
return ret;
}
int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp)
{
- BlockDriverState *drain_bs = bs->backing ? bs->backing->bs : bs;
+ BlockDriverState *drain_bs;
int ret;
GLOBAL_STATE_CODE();
+ bdrv_graph_rdlock_main_loop();
+ drain_bs = bs->backing ? bs->backing->bs : bs;
+ bdrv_graph_rdunlock_main_loop();
+
bdrv_ref(drain_bs);
bdrv_drained_begin(drain_bs);
+ bdrv_graph_wrlock(backing_hd);
ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp);
+ bdrv_graph_wrunlock(backing_hd);
bdrv_drained_end(drain_bs);
bdrv_unref(drain_bs);
Error *local_err = NULL;
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
if (bs->backing != NULL) {
goto free_exit;
implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file);
}
- bdrv_graph_rdlock_main_loop();
backing_filename = bdrv_get_full_backing_filename(bs, &local_err);
- bdrv_graph_rdunlock_main_loop();
-
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
}
if (implicit_backing) {
- bdrv_graph_rdlock_main_loop();
bdrv_refresh_filename(backing_hd);
- bdrv_graph_rdunlock_main_loop();
pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
backing_hd->filename);
}
child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role,
errp);
aio_context_release(ctx);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
return child;
}
bdrv_graph_wrlock(NULL);
tran_commit(tran);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
BlockDriverState *bs = bs_entry->state.bs;
abort:
bdrv_graph_wrlock(NULL);
tran_abort(tran);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
if (bs_entry->prepared) {
{
BlockDriverState *bs = reopen_state->bs;
BlockDriverState *new_child_bs;
- BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) :
- child_bs(bs->file);
+ BlockDriverState *old_child_bs;
+
const char *child_name = is_backing ? "backing" : "file";
QObject *value;
const char *str;
return 0;
}
+ bdrv_graph_rdlock_main_loop();
+
switch (qobject_type(value)) {
case QTYPE_QNULL:
assert(is_backing); /* The 'file' option does not allow a null value */
str = qstring_get_str(qobject_to(QString, value));
new_child_bs = bdrv_lookup_bs(NULL, str, errp);
if (new_child_bs == NULL) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_rdlock;
}
- bdrv_graph_rdlock_main_loop();
has_child = bdrv_recurse_has_child(new_child_bs, bs);
- bdrv_graph_rdunlock_main_loop();
-
if (has_child) {
error_setg(errp, "Making '%s' a %s child of '%s' would create a "
"cycle", str, child_name, bs->node_name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_rdlock;
}
break;
default:
g_assert_not_reached();
}
+ old_child_bs = is_backing ? child_bs(bs->backing) : child_bs(bs->file);
if (old_child_bs == new_child_bs) {
- return 0;
+ ret = 0;
+ goto out_rdlock;
}
if (old_child_bs) {
if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) {
- return 0;
+ ret = 0;
+ goto out_rdlock;
}
if (old_child_bs->implicit) {
error_setg(errp, "Cannot replace implicit %s child of %s",
child_name, bs->node_name);
- return -EPERM;
+ ret = -EPERM;
+ goto out_rdlock;
}
}
*/
error_setg(errp, "'%s' is a %s filter node that does not support a "
"%s child", bs->node_name, bs->drv->format_name, child_name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_rdlock;
}
if (is_backing) {
aio_context_acquire(ctx);
}
+ bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock(new_child_bs);
ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing,
tran, errp);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock_ctx(ctx);
if (old_ctx != ctx) {
aio_context_release(ctx);
}
return ret;
+
+out_rdlock:
+ bdrv_graph_rdunlock_main_loop();
+ return ret;
}
/*
* to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is
* not set, or if the BDS still has copy_on_read enabled */
read_only = !(reopen_state->flags & BDRV_O_RDWR);
+
+ bdrv_graph_rdlock_main_loop();
ret = bdrv_can_set_read_only(reopen_state->bs, read_only, true, &local_err);
+ bdrv_graph_rdunlock_main_loop();
if (local_err) {
error_propagate(errp, local_err);
goto error;
} else {
/* It is currently mandatory to have a bdrv_reopen_prepare()
* handler for each supported drv. */
+ bdrv_graph_rdlock_main_loop();
error_setg(errp, "Block format '%s' used by node '%s' "
"does not support reopening files", drv->format_name,
bdrv_get_device_or_node_name(reopen_state->bs));
+ bdrv_graph_rdunlock_main_loop();
ret = -1;
goto error;
}
* file or if the image file has a backing file name as part of
* its metadata. Otherwise the 'backing' option can be omitted.
*/
+ bdrv_graph_rdlock_main_loop();
if (drv->supports_backing && reopen_state->backing_missing &&
(reopen_state->bs->backing || reopen_state->bs->backing_file[0])) {
error_setg(errp, "backing is missing for '%s'",
reopen_state->bs->node_name);
+ bdrv_graph_rdunlock_main_loop();
ret = -EINVAL;
goto error;
}
+ bdrv_graph_rdunlock_main_loop();
/*
* Allow changing the 'backing' option. The new value can be
bs->drv = NULL;
}
- bdrv_graph_wrlock(NULL);
+ bdrv_graph_wrlock(bs);
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
bdrv_unref_child(bs, child);
}
- bdrv_graph_wrunlock();
assert(!bs->backing);
assert(!bs->file);
+ bdrv_graph_wrunlock(bs);
+
g_free(bs->opaque);
bs->opaque = NULL;
qatomic_set(&bs->copy_on_read, 0);
}
/*
+ * Switch all parents of @from to point to @to instead. @from and @to must be in
+ * the same AioContext and both must be drained.
+ *
* With auto_skip=true bdrv_replace_node_common skips updating from parents
* if it creates a parent-child relation loop or if parent is block-job.
*
* With @detach_subchain=true @to must be in a backing chain of @from. In this
* case backing link of the cow-parent of @to is removed.
*/
-static int bdrv_replace_node_common(BlockDriverState *from,
- BlockDriverState *to,
- bool auto_skip, bool detach_subchain,
- Error **errp)
+static int GRAPH_WRLOCK
+bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
+ bool auto_skip, bool detach_subchain, Error **errp)
{
Transaction *tran = tran_new();
g_autoptr(GSList) refresh_list = NULL;
GLOBAL_STATE_CODE();
+ assert(from->quiesce_counter);
+ assert(to->quiesce_counter);
+ assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
+
if (detach_subchain) {
assert(bdrv_chain_contains(from, to));
assert(from != to);
}
}
- /* Make sure that @from doesn't go away until we have successfully attached
- * all of its parents to @to. */
- bdrv_ref(from);
-
- assert(qemu_get_current_aio_context() == qemu_get_aio_context());
- assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
- bdrv_drained_begin(from);
- bdrv_drained_begin(to);
-
- bdrv_graph_wrlock(to);
-
/*
* Do the replacement without permission update.
* Replacement may influence the permissions, we should calculate new
out:
tran_finalize(tran, ret);
- bdrv_graph_wrunlock();
-
- bdrv_drained_end(to);
- bdrv_drained_end(from);
- bdrv_unref(from);
-
return ret;
}
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp)
{
- GLOBAL_STATE_CODE();
-
return bdrv_replace_node_common(from, to, true, false, errp);
}
int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
{
+ BlockDriverState *child_bs;
+ int ret;
+
GLOBAL_STATE_CODE();
- return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true,
- errp);
+ bdrv_graph_rdlock_main_loop();
+ child_bs = bdrv_filter_or_cow_bs(bs);
+ bdrv_graph_rdunlock_main_loop();
+
+ bdrv_drained_begin(child_bs);
+ bdrv_graph_wrlock(bs);
+ ret = bdrv_replace_node_common(bs, child_bs, true, true, errp);
+ bdrv_graph_wrunlock(bs);
+ bdrv_drained_end(child_bs);
+
+ return ret;
}
/*
GLOBAL_STATE_CODE();
+ bdrv_graph_rdlock_main_loop();
assert(!bs_new->backing);
+ bdrv_graph_rdunlock_main_loop();
old_context = bdrv_get_aio_context(bs_top);
bdrv_drained_begin(bs_top);
tran_finalize(tran, ret);
bdrv_refresh_limits(bs_top, NULL, NULL);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs_top);
bdrv_drained_end(bs_top);
bdrv_drained_end(bs_new);
tran_finalize(tran, ret);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(new_bs);
bdrv_drained_end(old_bs);
bdrv_drained_end(new_bs);
bdrv_unref(old_bs);
goto fail;
}
+ /*
+ * Make sure that @bs doesn't go away until we have successfully attached
+ * all of its parents to @new_node_bs and undrained it again.
+ */
+ bdrv_ref(bs);
bdrv_drained_begin(bs);
+ bdrv_drained_begin(new_node_bs);
+ bdrv_graph_wrlock(new_node_bs);
ret = bdrv_replace_node(bs, new_node_bs, errp);
+ bdrv_graph_wrunlock(new_node_bs);
+ bdrv_drained_end(new_node_bs);
bdrv_drained_end(bs);
+ bdrv_unref(bs);
if (ret < 0) {
error_prepend(errp, "Could not replace node: ");
* image file header
* -ENOTSUP - format driver doesn't support changing the backing file
*/
-int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
- const char *backing_fmt, bool require)
+int coroutine_fn
+bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file,
+ const char *backing_fmt, bool require)
{
BlockDriver *drv = bs->drv;
int ret;
- GLOBAL_STATE_CODE();
+ IO_CODE();
if (!drv) {
return -ENOMEDIUM;
return -EINVAL;
}
- if (drv->bdrv_change_backing_file != NULL) {
- ret = drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
+ if (drv->bdrv_co_change_backing_file != NULL) {
+ ret = drv->bdrv_co_change_backing_file(bs, backing_file, backing_fmt);
} else {
ret = -ENOTSUP;
}
* between @bs and @base is frozen. @errp is set if that's the case.
* @base must be reachable from @bs, or NULL.
*/
-bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
- Error **errp)
+static bool GRAPH_RDLOCK
+bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
+ Error **errp)
{
BlockDriverState *i;
BdrvChild *child;
bdrv_ref(top);
bdrv_drained_begin(base);
- bdrv_graph_rdlock_main_loop();
+ bdrv_graph_wrlock(base);
if (!top->drv || !base->drv) {
- goto exit;
+ goto exit_wrlock;
}
/* Make sure that base is in the backing chain of top */
if (!bdrv_chain_contains(top, base)) {
- goto exit;
+ goto exit_wrlock;
}
/* If 'base' recursively inherits from 'top' then we should set
* That's a FIXME.
*/
bdrv_replace_node_common(top, base, false, false, &local_err);
+ bdrv_graph_wrunlock(base);
+
if (local_err) {
error_report_err(local_err);
goto exit;
}
ret = 0;
+ goto exit;
+
+exit_wrlock:
+ bdrv_graph_wrunlock(base);
exit:
- bdrv_graph_rdunlock_main_loop();
bdrv_drained_end(base);
bdrv_unref(top);
return ret;
return 1;
}
-int bdrv_has_zero_init(BlockDriverState *bs)
+int coroutine_mixed_fn bdrv_has_zero_init(BlockDriverState *bs)
{
BlockDriverState *filtered;
GLOBAL_STATE_CODE();
bs->drv->bdrv_co_debug_event(bs, event);
}
-static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs)
+static BlockDriverState * GRAPH_RDLOCK
+bdrv_find_debug_node(BlockDriverState *bs)
{
GLOBAL_STATE_CODE();
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
const char *tag)
{
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
bs = bdrv_find_debug_node(bs);
if (bs) {
return bs->drv->bdrv_debug_breakpoint(bs, event, tag);
int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag)
{
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
bs = bdrv_find_debug_node(bs);
if (bs) {
return bs->drv->bdrv_debug_remove_breakpoint(bs, tag);
int bdrv_debug_resume(BlockDriverState *bs, const char *tag)
{
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) {
bs = bdrv_primary_bs(bs);
}
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag)
{
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) {
bs = bdrv_primary_bs(bs);
}
}
}
+static void bdrv_schedule_unref_bh(void *opaque)
+{
+ BlockDriverState *bs = opaque;
+ AioContext *ctx = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(ctx);
+ bdrv_unref(bs);
+ aio_context_release(ctx);
+}
+
/*
* Release a BlockDriverState reference while holding the graph write lock.
*
if (!bs) {
return;
}
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
- (QEMUBHFunc *) bdrv_unref, bs);
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_schedule_unref_bh, bs);
}
struct BdrvOpBlocker {
{
BdrvOpBlocker *blocker;
GLOBAL_STATE_CODE();
+
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
if (!QLIST_EMPTY(&bs->op_blockers[op])) {
blocker = QLIST_FIRST(&bs->op_blockers[op]);
/* Note: This function may return false positives; it may return true
* even if opening the backing file specified by bs's image header
* would result in exactly bs->backing. */
-static bool bdrv_backing_overridden(BlockDriverState *bs)
+static bool GRAPH_RDLOCK bdrv_backing_overridden(BlockDriverState *bs)
{
GLOBAL_STATE_CODE();
if (bs->backing) {
return found;
}
-static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs,
- bool stop_on_explicit_filter)
+static BlockDriverState * GRAPH_RDLOCK
+bdrv_do_skip_filters(BlockDriverState *bs, bool stop_on_explicit_filter)
{
BdrvChild *c;