bdrv_unref_child(bs, bs->file);
assert(!bs->file);
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
g_free(bs->opaque);
bs->opaque = NULL;
bdrv_drained_begin(drain_bs);
bdrv_graph_wrlock(backing_hd);
ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(backing_hd);
bdrv_drained_end(drain_bs);
bdrv_unref(drain_bs);
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) {
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);
assert(!bs->backing);
assert(!bs->file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
g_free(bs->opaque);
bs->opaque = NULL;
bdrv_drained_begin(child_bs);
bdrv_graph_wrlock(bs);
ret = bdrv_replace_node_common(bs, child_bs, true, true, errp);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
bdrv_drained_end(child_bs);
return ret;
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);
bdrv_drained_begin(new_node_bs);
bdrv_graph_wrlock(new_node_bs);
ret = bdrv_replace_node(bs, new_node_bs, errp);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(new_node_bs);
bdrv_drained_end(new_node_bs);
bdrv_drained_end(bs);
bdrv_unref(bs);
* That's a FIXME.
*/
bdrv_replace_node_common(top, base, false, false, &local_err);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(base);
if (local_err) {
error_report_err(local_err);
goto exit;
exit_wrlock:
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(base);
exit:
bdrv_drained_end(base);
bdrv_unref(top);
}
}
+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 {
bdrv_graph_wrlock(target);
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(target);
return &job->common;
if (ret < 0) {
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, s->log_file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
s->log_file = NULL;
}
fail:
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
}
static int64_t coroutine_fn GRAPH_RDLOCK
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, s->test_file);
s->test_file = NULL;
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
}
static int64_t coroutine_fn GRAPH_RDLOCK
{
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
BdrvChild *root;
+ AioContext *ctx;
GLOBAL_STATE_CODE();
root = blk->root;
blk->root = NULL;
+ ctx = bdrv_get_aio_context(root->bs);
bdrv_graph_wrlock(root->bs);
bdrv_root_unref_child(root);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock_ctx(ctx);
}
/*
int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
{
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
+ AioContext *ctx = bdrv_get_aio_context(bs);
+
GLOBAL_STATE_CODE();
bdrv_ref(bs);
bdrv_graph_wrlock(bs);
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
blk->perm, blk->shared_perm,
blk, errp);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock_ctx(ctx);
if (blk->root == NULL) {
return -EPERM;
}
bdrv_drained_begin(commit_top_backing_bs);
bdrv_graph_wrlock(commit_top_backing_bs);
bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(commit_top_backing_bs);
bdrv_drained_end(commit_top_backing_bs);
bdrv_unref(s->commit_top_bs);
ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
iter_shared_perms, errp);
if (ret < 0) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(top);
goto fail;
}
}
if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(top);
goto fail;
}
s->chain_frozen = true;
ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(top);
if (ret < 0) {
goto fail;
bdrv_drained_begin(top);
bdrv_graph_wrlock(top);
bdrv_replace_node(commit_top_bs, top, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(top);
bdrv_drained_end(top);
}
}
}
}
-void bdrv_graph_wrunlock(void)
+void no_coroutine_fn bdrv_graph_wrunlock_ctx(AioContext *ctx)
{
GLOBAL_STATE_CODE();
assert(qatomic_read(&has_writer));
+ /*
+ * Release only non-mainloop AioContext. The mainloop often relies on the
+ * BQL and doesn't lock the main AioContext before doing things.
+ */
+ if (ctx && ctx != qemu_get_aio_context()) {
+ aio_context_release(ctx);
+ } else {
+ ctx = NULL;
+ }
+
WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) {
/*
* No need for memory barriers, this works in pair with
* progress.
*/
aio_bh_poll(qemu_get_aio_context());
+
+ if (ctx) {
+ aio_context_acquire(ctx);
+ }
+}
+
+void no_coroutine_fn bdrv_graph_wrunlock(BlockDriverState *bs)
+{
+ AioContext *ctx = bs ? bdrv_get_aio_context(bs) : NULL;
+
+ bdrv_graph_wrunlock_ctx(ctx);
}
void coroutine_fn bdrv_graph_co_rdlock(void)
"would not lead to an abrupt change of visible data",
to_replace->node_name, target_bs->node_name);
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(target_bs);
bdrv_drained_end(to_replace);
if (local_err) {
error_report_err(local_err);
block_job_remove_all_bdrv(bjob);
bdrv_graph_wrlock(mirror_top_bs);
bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(mirror_top_bs);
bdrv_drained_end(target_bs);
bdrv_unref(target_bs);
BLK_PERM_CONSISTENT_READ,
errp);
if (ret < 0) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
goto fail;
}
ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
iter_shared_perms, errp);
if (ret < 0) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
goto fail;
}
}
if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
goto fail;
}
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
QTAILQ_INIT(&s->ops_in_flight);
bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing,
&error_abort);
bdrv_replace_node(mirror_top_bs, bs, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
bdrv_drained_end(bs);
bdrv_unref(mirror_top_bs);
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, s->data_file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
s->data_file = NULL;
bdrv_graph_rdlock_main_loop();
}
}
bdrv_unref_child(bs, s->children[i]);
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
g_free(s->children);
g_free(opened);
exit:
for (i = 0; i < s->num_children; i++) {
bdrv_unref_child(bs, s->children[i]);
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
g_free(s->children);
}
&local_err);
if (local_err) {
error_propagate(errp, local_err);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
aio_context_release(aio_context);
return;
}
BDRV_CHILD_DATA, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
aio_context_release(aio_context);
return;
}
if (!top_bs || !bdrv_is_root_node(top_bs) ||
!check_top_bs(top_bs, bs)) {
error_setg(errp, "No top_bs or it is invalid");
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
reopen_backing_file(bs, false, NULL);
aio_context_release(aio_context);
return;
bdrv_op_block_all(top_bs, s->blocker);
bdrv_op_unblock(top_bs, BLOCK_OP_TYPE_DATAPLANE, s->blocker);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
s->backup_job = backup_job_create(
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
s->secondary_disk = NULL;
bdrv_unref_child(bs, s->hidden_disk);
s->hidden_disk = NULL;
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
s->error = 0;
} else {
/* .bdrv_open() will re-attach it */
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, fallback);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp);
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
bdrv_graph_wrlock(base);
bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(base);
/*
* This call will do I/O, so the graph can change again from here on.
bdrv_graph_wrlock(bs);
if (block_job_add_bdrv(&s->common, "active node", bs, 0,
basic_flags | BLK_PERM_WRITE, errp)) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
goto fail;
}
ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0,
basic_flags, errp);
if (ret < 0) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
goto fail;
}
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
s->base_overlay = base_overlay;
s->above_base = above_base;
bdrv_unref_child(bs, e->file);
}
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
g_free(s->extents);
}
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, extent_file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
bdrv_graph_rdlock_main_loop();
goto out;
}
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, extent_file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
bdrv_graph_rdlock_main_loop();
goto out;
}
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, extent_file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
bdrv_graph_rdlock_main_loop();
goto out;
}
bdrv_graph_rdunlock_main_loop();
bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, extent_file);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
bdrv_graph_rdlock_main_loop();
ret = -ENOTSUP;
goto out;
bdrv_drained_begin(state->new_bs);
bdrv_graph_wrlock(state->old_bs);
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(state->old_bs);
bdrv_drained_end(state->new_bs);
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
}
out:
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
}
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
g_slist_free_1(l);
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock_ctx(job->job.aio_context);
}
bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs)
job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs),
flags, cb, opaque, errp);
if (job == NULL) {
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
return NULL;
}
goto fail;
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
return job;
fail:
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(bs);
job_early_fail(&job->job);
return NULL;
}
* bdrv_graph_wrunlock:
* Write finished, reset global has_writer to 0 and restart
* all readers that are waiting.
+ *
+ * If @bs is non-NULL, its AioContext is temporarily released.
+ */
+void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA
+bdrv_graph_wrunlock(BlockDriverState *bs);
+
+/*
+ * bdrv_graph_wrunlock_ctx:
+ * Write finished, reset global has_writer to 0 and restart
+ * all readers that are waiting.
+ *
+ * If @ctx is non-NULL, its lock is temporarily released.
*/
-void bdrv_graph_wrunlock(void) TSA_RELEASE(graph_lock) TSA_NO_TSA;
+void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA
+bdrv_graph_wrunlock_ctx(AioContext *ctx);
/*
* bdrv_graph_co_rdlock:
graph_unlock=' bdrv_graph_rdunlock_main_loop();'
elif func.graph_wrlock:
graph_lock=' bdrv_graph_wrlock(NULL);'
- graph_unlock=' bdrv_graph_wrunlock();'
+ graph_unlock=' bdrv_graph_wrunlock(NULL);'
return f"""\
/*
bdrv_graph_wrlock(target);
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(target);
switch (result) {
case TEST_JOB_SUCCESS:
QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) {
bdrv_unref_child(bs, c);
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
}
static int coroutine_fn GRAPH_RDLOCK
bdrv_graph_wrlock(NULL);
bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
/* This child will be the one to pass to requests through to, and
* it will stall until a drain occurs */
&child_of_bds,
BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY,
&error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
/* This child is just there to be deleted
* (for detach_instead_of_delete == true) */
bdrv_graph_wrlock(NULL);
bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA,
&error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL);
blk_insert_bs(blk, bs, &error_abort);
data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C",
&child_of_bds, BDRV_CHILD_DATA,
&error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
}
static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret)
bdrv_attach_child(parent_a, a, "PA-A",
by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class,
BDRV_CHILD_DATA, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
g_assert_cmpint(parent_a->refcnt, ==, 1);
g_assert_cmpint(parent_b->refcnt, ==, 1);
&chain_child_class, BDRV_CHILD_COW, &error_abort);
}
}
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
job = block_job_create("job", &test_simple_job_driver, NULL, job_node,
0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort);
bdrv_graph_wrlock(NULL);
bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds,
BDRV_CHILD_COW, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
parent_s->setup_completed = true;
for (i = 0; i < old_drain_count; i++) {
bdrv_drained_begin(new_child_bs);
bdrv_graph_wrlock(NULL);
bdrv_replace_node(old_child_bs, new_child_bs, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
bdrv_drained_end(new_child_bs);
bdrv_drained_end(old_child_bs);
g_assert(parent_bs->quiesce_counter == new_drain_count);
bdrv_graph_wrlock(NULL);
bdrv_attach_child(filter, bs, "child", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
aio_context_acquire(qemu_get_aio_context());
ret = bdrv_append(filter, bs, NULL);
g_assert(target->backing->bs == bs);
bdrv_attach_child(filter, target, "target", &child_of_bds,
BDRV_CHILD_DATA, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
aio_context_acquire(qemu_get_aio_context());
bdrv_append(filter, bs, &error_abort);
aio_context_release(qemu_get_aio_context());
&error_abort);
bdrv_replace_node(fl1, fl2, &error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
bdrv_drained_end(fl2);
bdrv_drained_end(fl1);
bdrv_attach_child(fl2, base, "backing", &child_of_bds,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
/* Select fl1 as first child to be active */
s->selected = c_fl1;
bdrv_attach_child(top, base, "backing", &child_of_bds,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
- bdrv_graph_wrunlock();
+ bdrv_graph_wrunlock(NULL);
aio_context_acquire(qemu_get_aio_context());
bdrv_append(fl, base, &error_abort);