aboutsummaryrefslogtreecommitdiff
path: root/block.c
diff options
context:
space:
mode:
Diffstat (limited to 'block.c')
-rw-r--r--block.c174
1 files changed, 158 insertions, 16 deletions
diff --git a/block.c b/block.c
index 6999aad446..cb11537029 100644
--- a/block.c
+++ b/block.c
@@ -936,6 +936,20 @@ static int bdrv_child_cb_inactivate(BdrvChild *child)
return 0;
}
+static bool bdrv_child_cb_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ BlockDriverState *bs = child->opaque;
+ return bdrv_can_set_aio_context(bs, ctx, ignore, errp);
+}
+
+static void bdrv_child_cb_set_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GSList **ignore)
+{
+ BlockDriverState *bs = child->opaque;
+ return bdrv_set_aio_context_ignore(bs, ctx, ignore);
+}
+
/*
* Returns the options and flags that a temporary snapshot should get, based on
* the originally requested flags (the originally requested image will have
@@ -1003,6 +1017,8 @@ const BdrvChildRole child_file = {
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
};
/*
@@ -1029,6 +1045,8 @@ const BdrvChildRole child_format = {
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
};
static void bdrv_backing_attach(BdrvChild *c)
@@ -1152,6 +1170,8 @@ const BdrvChildRole child_backing = {
.drained_end = bdrv_child_cb_drained_end,
.inactivate = bdrv_child_cb_inactivate,
.update_filename = bdrv_backing_update_filename,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
};
static int bdrv_open_flags(BlockDriverState *bs, int flags)
@@ -1689,6 +1709,8 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
GSList *ignore_children, Error **errp);
static void bdrv_child_abort_perm_update(BdrvChild *c);
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
+static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
+ uint64_t *shared_perm);
typedef struct BlockReopenQueueEntry {
bool prepared;
@@ -1775,7 +1797,20 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
!bdrv_is_writable_after_reopen(bs, q))
{
- error_setg(errp, "Block node is read-only");
+ if (!bdrv_is_writable_after_reopen(bs, NULL)) {
+ error_setg(errp, "Block node is read-only");
+ } else {
+ uint64_t current_perms, current_shared;
+ bdrv_get_cumulative_perm(bs, &current_perms, &current_shared);
+ if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
+ error_setg(errp, "Cannot make block node read-only, there is "
+ "a writer on it");
+ } else {
+ error_setg(errp, "Cannot make block node read-only and create "
+ "a writer on it");
+ }
+ }
+
return -EPERM;
}
@@ -5666,10 +5701,9 @@ static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban)
g_free(ban);
}
-void bdrv_detach_aio_context(BlockDriverState *bs)
+static void bdrv_detach_aio_context(BlockDriverState *bs)
{
BdrvAioNotifier *baf, *baf_tmp;
- BdrvChild *child;
assert(!bs->walking_aio_notifiers);
bs->walking_aio_notifiers = true;
@@ -5688,9 +5722,6 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
if (bs->drv && bs->drv->bdrv_detach_aio_context) {
bs->drv->bdrv_detach_aio_context(bs);
}
- QLIST_FOREACH(child, &bs->children, next) {
- bdrv_detach_aio_context(child->bs);
- }
if (bs->quiesce_counter) {
aio_enable_external(bs->aio_context);
@@ -5698,11 +5729,10 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
bs->aio_context = NULL;
}
-void bdrv_attach_aio_context(BlockDriverState *bs,
- AioContext *new_context)
+static void bdrv_attach_aio_context(BlockDriverState *bs,
+ AioContext *new_context)
{
BdrvAioNotifier *ban, *ban_tmp;
- BdrvChild *child;
if (bs->quiesce_counter) {
aio_disable_external(new_context);
@@ -5710,9 +5740,6 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
bs->aio_context = new_context;
- QLIST_FOREACH(child, &bs->children, next) {
- bdrv_attach_aio_context(child->bs, new_context);
- }
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
bs->drv->bdrv_attach_aio_context(bs, new_context);
}
@@ -5729,16 +5756,36 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
bs->walking_aio_notifiers = false;
}
-/* The caller must own the AioContext lock for the old AioContext of bs, but it
- * must not own the AioContext lock for new_context (unless new_context is
- * the same as the current context of bs). */
-void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
+/* @ignore will accumulate all visited BdrvChild object. The caller is
+ * responsible for freeing the list afterwards. */
+void bdrv_set_aio_context_ignore(BlockDriverState *bs,
+ AioContext *new_context, GSList **ignore)
{
+ BdrvChild *child;
+
if (bdrv_get_aio_context(bs) == new_context) {
return;
}
bdrv_drained_begin(bs);
+
+ QLIST_FOREACH(child, &bs->children, next) {
+ if (g_slist_find(*ignore, child)) {
+ continue;
+ }
+ *ignore = g_slist_prepend(*ignore, child);
+ bdrv_set_aio_context_ignore(child->bs, new_context, ignore);
+ }
+ QLIST_FOREACH(child, &bs->parents, next_parent) {
+ if (g_slist_find(*ignore, child)) {
+ continue;
+ }
+ if (child->role->set_aio_ctx) {
+ *ignore = g_slist_prepend(*ignore, child);
+ child->role->set_aio_ctx(child, new_context, ignore);
+ }
+ }
+
bdrv_detach_aio_context(bs);
/* This function executes in the old AioContext so acquire the new one in
@@ -5750,6 +5797,101 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
aio_context_release(new_context);
}
+/* The caller must own the AioContext lock for the old AioContext of bs, but it
+ * must not own the AioContext lock for new_context (unless new_context is
+ * the same as the current context of bs). */
+void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
+{
+ GSList *ignore_list = NULL;
+ bdrv_set_aio_context_ignore(bs, new_context, &ignore_list);
+ g_slist_free(ignore_list);
+}
+
+static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ if (g_slist_find(*ignore, c)) {
+ return true;
+ }
+ *ignore = g_slist_prepend(*ignore, c);
+
+ /* A BdrvChildRole that doesn't handle AioContext changes cannot
+ * tolerate any AioContext changes */
+ if (!c->role->can_set_aio_ctx) {
+ char *user = bdrv_child_user_desc(c);
+ error_setg(errp, "Changing iothreads is not supported by %s", user);
+ g_free(user);
+ return false;
+ }
+ if (!c->role->can_set_aio_ctx(c, ctx, ignore, errp)) {
+ assert(!errp || *errp);
+ return false;
+ }
+ return true;
+}
+
+bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ if (g_slist_find(*ignore, c)) {
+ return true;
+ }
+ *ignore = g_slist_prepend(*ignore, c);
+ return bdrv_can_set_aio_context(c->bs, ctx, ignore, errp);
+}
+
+/* @ignore will accumulate all visited BdrvChild object. The caller is
+ * responsible for freeing the list afterwards. */
+bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ BdrvChild *c;
+
+ if (bdrv_get_aio_context(bs) == ctx) {
+ return true;
+ }
+
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
+ if (!bdrv_parent_can_set_aio_context(c, ctx, ignore, errp)) {
+ return false;
+ }
+ }
+ QLIST_FOREACH(c, &bs->children, next) {
+ if (!bdrv_child_can_set_aio_context(c, ctx, ignore, errp)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp)
+{
+ GSList *ignore;
+ bool ret;
+
+ ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL;
+ ret = bdrv_can_set_aio_context(bs, ctx, &ignore, errp);
+ g_slist_free(ignore);
+
+ if (!ret) {
+ return -EPERM;
+ }
+
+ ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL;
+ bdrv_set_aio_context_ignore(bs, ctx, &ignore);
+ g_slist_free(ignore);
+
+ return 0;
+}
+
+int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
+ Error **errp)
+{
+ return bdrv_child_try_set_aio_context(bs, ctx, NULL, errp);
+}
+
void bdrv_add_aio_context_notifier(BlockDriverState *bs,
void (*attached_aio_context)(AioContext *new_context, void *opaque),
void (*detach_aio_context)(void *opaque), void *opaque)