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();
* setting @errp. In all other cases, NULL will only be returned with
* @errp set.
*/
-static char *bdrv_make_absolute_filename(BlockDriverState *relative_to,
- const char *filename, Error **errp)
+static char * GRAPH_RDLOCK
+bdrv_make_absolute_filename(BlockDriverState *relative_to,
+ const char *filename, Error **errp)
{
char *dir, *full_name;
return g_strdup_printf("node '%s'", bdrv_get_node_name(parent));
}
-static void bdrv_child_cb_drained_begin(BdrvChild *child)
+static void GRAPH_RDLOCK bdrv_child_cb_drained_begin(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
bdrv_do_drained_begin_quiesce(bs, NULL);
}
-static bool bdrv_child_cb_drained_poll(BdrvChild *child)
+static bool GRAPH_RDLOCK bdrv_child_cb_drained_poll(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
return bdrv_drain_poll(bs, NULL, false);
}
-static void bdrv_child_cb_drained_end(BdrvChild *child)
+static void GRAPH_RDLOCK bdrv_child_cb_drained_end(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
bdrv_drained_end(bs);
*child_flags &= ~BDRV_O_NATIVE_AIO;
}
-static void bdrv_backing_attach(BdrvChild *c)
+static void GRAPH_WRLOCK bdrv_backing_attach(BdrvChild *c)
{
BlockDriverState *parent = c->opaque;
BlockDriverState *backing_hd = c->bs;
open_failed:
bs->drv = NULL;
if (bs->file != NULL) {
+ bdrv_graph_wrlock(NULL);
bdrv_unref_child(bs, bs->file);
+ bdrv_graph_wrunlock();
assert(!bs->file);
}
g_free(bs->opaque);
}
if (file != NULL) {
+ bdrv_graph_rdlock_main_loop();
bdrv_refresh_filename(blk_bs(file));
+ bdrv_graph_rdunlock_main_loop();
+
filename = blk_bs(file)->filename;
} else {
/*
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);
&local_err);
if (ret < 0 && child_class->change_aio_ctx) {
- Transaction *tran = tran_new();
+ Transaction *aio_ctx_tran = tran_new();
GHashTable *visited = g_hash_table_new(NULL, NULL);
bool ret_child;
g_hash_table_add(visited, new_child);
ret_child = child_class->change_aio_ctx(new_child, child_ctx,
- visited, tran, NULL);
+ visited, aio_ctx_tran,
+ NULL);
if (ret_child == true) {
error_free(local_err);
ret = 0;
}
- tran_finalize(tran, ret_child == true ? 0 : -1);
+ tran_finalize(aio_ctx_tran, ret_child == true ? 0 : -1);
g_hash_table_destroy(visited);
}
BlockDriverState *child_bs = child->bs;
GLOBAL_STATE_CODE();
- bdrv_graph_wrlock(NULL);
bdrv_replace_child_noperm(child, NULL);
bdrv_child_free(child);
NULL);
}
- bdrv_graph_wrunlock();
- bdrv_unref(child_bs);
+ bdrv_schedule_unref(child_bs);
}
typedef struct BdrvSetInheritsFrom {
* @root that point to @root, where necessary.
* @tran is allowed to be NULL. In this case no rollback is possible
*/
-static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
- Transaction *tran)
+static void GRAPH_WRLOCK
+bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
+ Transaction *tran)
{
BdrvChild *c;
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);
}
/*
* Returns true if @child can be reached recursively from @bs
*/
-static bool bdrv_recurse_has_child(BlockDriverState *bs,
- BlockDriverState *child)
+static bool GRAPH_RDLOCK
+bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child)
{
BdrvChild *c;
*
* To be called with bs->aio_context locked.
*/
-static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
- BlockDriverState *bs,
- QDict *options,
- const BdrvChildClass *klass,
- BdrvChildRole role,
- bool parent_is_format,
- QDict *parent_options,
- int parent_flags,
- bool keep_old_opts)
+static BlockReopenQueue * GRAPH_RDLOCK
+bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs,
+ QDict *options, const BdrvChildClass *klass,
+ BdrvChildRole role, bool parent_is_format,
+ QDict *parent_options, int parent_flags,
+ bool keep_old_opts)
{
assert(bs != NULL);
GLOBAL_STATE_CODE();
+ /*
+ * Strictly speaking, draining is illegal under GRAPH_RDLOCK. We know that
+ * we've been called with bdrv_graph_rdlock_main_loop(), though, so it's ok
+ * in practice.
+ */
bdrv_drained_begin(bs);
if (bs_queue == NULL) {
QDict *options, bool keep_old_opts)
{
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false,
NULL, 0, keep_old_opts);
* Callers must make sure that their AioContext locking is still correct after
* this.
*/
-static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
- bool is_backing, Transaction *tran,
- Error **errp)
+static int GRAPH_UNLOCKED
+bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
+ bool is_backing, Transaction *tran,
+ Error **errp)
{
BlockDriverState *bs = reopen_state->bs;
BlockDriverState *new_child_bs;
QObject *value;
const char *str;
AioContext *ctx, *old_ctx;
+ bool has_child;
int ret;
GLOBAL_STATE_CODE();
new_child_bs = bdrv_lookup_bs(NULL, str, errp);
if (new_child_bs == NULL) {
return -EINVAL;
- } else if (bdrv_recurse_has_child(new_child_bs, bs)) {
+ }
+
+ 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;
* After calling this function, the transaction @change_child_tran may only be
* completed while holding a writer lock for the graph.
*/
-static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
- BlockReopenQueue *queue,
- Transaction *change_child_tran, Error **errp)
+static int GRAPH_UNLOCKED
+bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
+ Transaction *change_child_tran, Error **errp)
{
int ret = -1;
int old_flags;
* 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;
if (local_err != NULL) {
error_propagate(errp, local_err);
} else {
+ bdrv_graph_rdlock_main_loop();
bdrv_refresh_filename(reopen_state->bs);
+ bdrv_graph_rdunlock_main_loop();
error_setg(errp, "failed while preparing to reopen image '%s'",
reopen_state->bs->filename);
}
} 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;
}
if (qdict_size(reopen_state->options)) {
const QDictEntry *entry = qdict_first(reopen_state->options);
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
do {
QObject *new = entry->value;
QObject *old = qdict_get(reopen_state->bs->options, entry->key);
* makes them final by swapping the staging BlockDriverState contents into
* the active BlockDriverState contents.
*/
-static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
+static void GRAPH_UNLOCKED bdrv_reopen_commit(BDRVReopenState *reopen_state)
{
BlockDriver *drv;
BlockDriverState *bs;
drv->bdrv_reopen_commit(reopen_state);
}
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
/* set BDS specific flags now */
qobject_unref(bs->explicit_options);
qobject_unref(bs->options);
qdict_del(bs->explicit_options, "backing");
qdict_del(bs->options, "backing");
- bdrv_graph_rdlock_main_loop();
bdrv_refresh_limits(bs, NULL, NULL);
- bdrv_graph_rdunlock_main_loop();
bdrv_refresh_total_sectors(bs, bs->total_sectors);
}
* Abort the reopen, and delete and free the staged changes in
* reopen_state
*/
-static void bdrv_reopen_abort(BDRVReopenState *reopen_state)
+static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state)
{
BlockDriver *drv;
bs->drv = NULL;
}
+ bdrv_graph_wrlock(NULL);
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
bdrv_unref_child(bs, child);
}
+ bdrv_graph_wrunlock();
assert(!bs->backing);
assert(!bs->file);
bdrv_ref(top);
bdrv_drained_begin(base);
+ bdrv_graph_rdlock_main_loop();
if (!top->drv || !base->drv) {
goto exit;
backing_file_str = base->filename;
}
- bdrv_graph_rdlock_main_loop();
QLIST_FOREACH(c, &top->parents, next_parent) {
updated_children = g_slist_prepend(updated_children, c);
}
- bdrv_graph_rdunlock_main_loop();
/*
* It seems correct to pass detach_subchain=true here, but it triggers
ret = 0;
exit:
+ bdrv_graph_rdunlock_main_loop();
bdrv_drained_end(base);
bdrv_unref(top);
return ret;
QLIST_FOREACH(drv, &bdrv_drivers, list) {
if (drv->format_name) {
bool found = false;
- int i = count;
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) {
continue;
}
+ i = count;
while (formats && i && !found) {
found = !strcmp(formats[--i], drv->format_name);
}
BlockDriverState *bs;
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
list = NULL;
QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) {
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);
}
BlockDriverState *bs_below;
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
if (!bs || !bs->drv || !backing_file) {
return NULL;
BdrvNextIterator it;
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
}
}
-static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active)
+static bool GRAPH_RDLOCK
+bdrv_has_bds_parent(BlockDriverState *bs, bool only_active)
{
BdrvChild *parent;
GLOBAL_STATE_CODE();
return false;
}
-static int bdrv_inactivate_recurse(BlockDriverState *bs)
+static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs)
{
BdrvChild *child, *parent;
int ret;
uint64_t cumulative_perms, cumulative_shared_perms;
GLOBAL_STATE_CODE();
- GRAPH_RDLOCK_GUARD_MAINLOOP();
if (!bs->drv) {
return -ENOMEDIUM;
GSList *aio_ctxs = NULL, *ctx;
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
{
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]);
return true;
}
+ bdrv_graph_rdlock_main_loop();
QLIST_FOREACH(c, &bs->parents, next_parent) {
if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) {
+ bdrv_graph_rdunlock_main_loop();
return false;
}
}
QLIST_FOREACH(c, &bs->children, next) {
if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) {
+ bdrv_graph_rdunlock_main_loop();
return false;
}
}
+ bdrv_graph_rdunlock_main_loop();
state = g_new(BdrvStateSetAioContext, 1);
*state = (BdrvStateSetAioContext) {