X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=block.c;h=ecd09dbbfd89acbc6fd44adc8eb3641fd110abd9;hb=ca1a6b708ba2d94f2dd64bb8ec04223a708c9b40;hp=ceb2ea23c50dfe964bca0aecf9bdb99bc59fe926;hpb=b23c580c946644b906af36e3f903b9fedd6ccd61;p=mirror_qemu.git diff --git a/block.c b/block.c index ceb2ea23c5..ecd09dbbfd 100644 --- a/block.c +++ b/block.c @@ -30,6 +30,7 @@ #include "block/qdict.h" #include "qemu/error-report.h" #include "module_block.h" +#include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" @@ -105,7 +106,7 @@ size_t bdrv_opt_mem_align(BlockDriverState *bs) { if (!bs || !bs->drv) { /* page size or 4k (hdd sector size) should be on the safe side */ - return MAX(4096, getpagesize()); + return MAX(4096, qemu_real_host_page_size); } return bs->bl.opt_mem_alignment; @@ -115,7 +116,7 @@ size_t bdrv_min_mem_align(BlockDriverState *bs) { if (!bs || !bs->drv) { /* page size or 4k (hdd sector size) should be on the safe side */ - return MAX(4096, getpagesize()); + return MAX(4096, qemu_real_host_page_size); } return bs->bl.min_mem_alignment; @@ -911,10 +912,11 @@ static bool bdrv_child_cb_drained_poll(BdrvChild *child) return bdrv_drain_poll(bs, false, NULL, false); } -static void bdrv_child_cb_drained_end(BdrvChild *child) +static void bdrv_child_cb_drained_end(BdrvChild *child, + int *drained_end_counter) { BlockDriverState *bs = child->opaque; - bdrv_drained_end(bs); + bdrv_drained_end_no_poll(bs, drained_end_counter); } static void bdrv_child_cb_attach(BdrvChild *child) @@ -1706,7 +1708,8 @@ static int bdrv_fill_options(QDict **options, const char *filename, static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, uint64_t perm, uint64_t shared, - GSList *ignore_children, Error **errp); + GSList *ignore_children, + bool *tighten_restrictions, 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, @@ -1716,7 +1719,7 @@ typedef struct BlockReopenQueueEntry { bool prepared; bool perms_checked; BDRVReopenState state; - QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry; + QTAILQ_ENTRY(BlockReopenQueueEntry) entry; } BlockReopenQueueEntry; /* @@ -1729,7 +1732,7 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) BlockReopenQueueEntry *entry; if (q != NULL) { - QSIMPLEQ_FOREACH(entry, q, entry) { + QTAILQ_FOREACH(entry, q, entry) { if (entry->state.bs == bs) { return entry->state.flags; } @@ -1781,18 +1784,43 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, * permissions of all its parents. This involves checking whether all necessary * permission changes to child nodes can be performed. * + * Will set *tighten_restrictions to true if and only if new permissions have to + * be taken or currently shared permissions are to be unshared. Otherwise, + * errors are not fatal as long as the caller accepts that the restrictions + * remain tighter than they need to be. The caller still has to abort the + * transaction. + * @tighten_restrictions cannot be used together with @q: When reopening, we may + * encounter fatal errors even though no restrictions are to be tightened. For + * example, changing a node from RW to RO will fail if the WRITE permission is + * to be kept. + * * A call to this function must always be followed by a call to bdrv_set_perm() * or bdrv_abort_perm_update(). */ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, uint64_t cumulative_perms, uint64_t cumulative_shared_perms, - GSList *ignore_children, Error **errp) + GSList *ignore_children, + bool *tighten_restrictions, Error **errp) { BlockDriver *drv = bs->drv; BdrvChild *c; int ret; + assert(!q || !tighten_restrictions); + + if (tighten_restrictions) { + uint64_t current_perms, current_shared; + uint64_t added_perms, removed_shared_perms; + + bdrv_get_cumulative_perm(bs, ¤t_perms, ¤t_shared); + + added_perms = cumulative_perms & ~current_perms; + removed_shared_perms = current_shared & ~cumulative_shared_perms; + + *tighten_restrictions = added_perms || removed_shared_perms; + } + /* Write permissions never work with read-only images */ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && !bdrv_is_writable_after_reopen(bs, q)) @@ -1833,11 +1861,18 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, /* Check all children */ QLIST_FOREACH(c, &bs->children, next) { uint64_t cur_perm, cur_shared; + bool child_tighten_restr; + bdrv_child_perm(bs, c->bs, c, c->role, q, cumulative_perms, cumulative_shared_perms, &cur_perm, &cur_shared); - ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, - ignore_children, errp); + ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children, + tighten_restrictions ? &child_tighten_restr + : NULL, + errp); + if (tighten_restrictions) { + *tighten_restrictions |= child_tighten_restr; + } if (ret < 0) { return ret; } @@ -1961,17 +1996,23 @@ char *bdrv_perm_names(uint64_t perm) * set, the BdrvChild objects in this list are ignored in the calculations; * this allows checking permission updates for an existing reference. * + * See bdrv_check_perm() for the semantics of @tighten_restrictions. + * * Needs to be followed by a call to either bdrv_set_perm() or * bdrv_abort_perm_update(). */ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, uint64_t new_used_perm, uint64_t new_shared_perm, - GSList *ignore_children, Error **errp) + GSList *ignore_children, + bool *tighten_restrictions, + Error **errp) { BdrvChild *c; uint64_t cumulative_perms = new_used_perm; uint64_t cumulative_shared_perms = new_shared_perm; + assert(!q || !tighten_restrictions); + /* There is no reason why anyone couldn't tolerate write_unchanged */ assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED); @@ -1983,6 +2024,11 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, if ((new_used_perm & c->shared_perm) != new_used_perm) { char *user = bdrv_child_user_desc(c); char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm); + + if (tighten_restrictions) { + *tighten_restrictions = true; + } + error_setg(errp, "Conflicts with use by %s as '%s', which does not " "allow '%s' on %s", user, c->name, perm_names, bdrv_get_node_name(c->bs)); @@ -1994,6 +2040,11 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, if ((c->perm & new_shared_perm) != c->perm) { char *user = bdrv_child_user_desc(c); char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm); + + if (tighten_restrictions) { + *tighten_restrictions = true; + } + error_setg(errp, "Conflicts with use by %s as '%s', which uses " "'%s' on %s", user, c->name, perm_names, bdrv_get_node_name(c->bs)); @@ -2007,19 +2058,21 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, } return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms, - ignore_children, errp); + ignore_children, tighten_restrictions, errp); } /* Needs to be followed by a call to either bdrv_child_set_perm() or * bdrv_child_abort_perm_update(). */ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, uint64_t perm, uint64_t shared, - GSList *ignore_children, Error **errp) + GSList *ignore_children, + bool *tighten_restrictions, Error **errp) { int ret; ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c); - ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp); + ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, + tighten_restrictions, errp); g_slist_free(ignore_children); if (ret < 0) { @@ -2070,11 +2123,26 @@ static void bdrv_child_abort_perm_update(BdrvChild *c) int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, Error **errp) { + Error *local_err = NULL; int ret; + bool tighten_restrictions; - ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, errp); + ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, + &tighten_restrictions, &local_err); if (ret < 0) { bdrv_child_abort_perm_update(c); + if (tighten_restrictions) { + error_propagate(errp, local_err); + } else { + /* + * Our caller may intend to only loosen restrictions and + * does not expect this function to fail. Errors are not + * fatal in such a case, so we can just hide them from our + * caller. + */ + error_free(local_err); + ret = 0; + } return ret; } @@ -2083,22 +2151,26 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, return 0; } +int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp) +{ + uint64_t parent_perms, parent_shared; + uint64_t perms, shared; + + bdrv_get_cumulative_perm(bs, &parent_perms, &parent_shared); + bdrv_child_perm(bs, c->bs, c, c->role, NULL, parent_perms, parent_shared, + &perms, &shared); + + return bdrv_child_try_set_perm(c, perms, shared, errp); +} + void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { - if (c == NULL) { - *nperm = perm & DEFAULT_PERM_PASSTHROUGH; - *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; - return; - } - - *nperm = (perm & DEFAULT_PERM_PASSTHROUGH) | - (c->perm & DEFAULT_PERM_UNCHANGED); - *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | - (c->shared_perm & DEFAULT_PERM_UNCHANGED); + *nperm = perm & DEFAULT_PERM_PASSTHROUGH; + *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; } void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, @@ -2155,17 +2227,49 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, *nshared = shared; } +uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) +{ + static const uint64_t permissions[] = { + [BLOCK_PERMISSION_CONSISTENT_READ] = BLK_PERM_CONSISTENT_READ, + [BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE, + [BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED, + [BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE, + [BLOCK_PERMISSION_GRAPH_MOD] = BLK_PERM_GRAPH_MOD, + }; + + QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX); + QEMU_BUILD_BUG_ON(1UL << ARRAY_SIZE(permissions) != BLK_PERM_ALL + 1); + + assert(qapi_perm < BLOCK_PERMISSION__MAX); + + return permissions[qapi_perm]; +} + static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) { BlockDriverState *old_bs = child->bs; - int i; + int new_bs_quiesce_counter; + int drain_saldo; assert(!child->frozen); if (old_bs && new_bs) { assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); } + + new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0); + drain_saldo = new_bs_quiesce_counter - child->parent_quiesce_counter; + + /* + * If the new child node is drained but the old one was not, flush + * all outstanding requests to the old child node. + */ + while (drain_saldo > 0 && child->role->drained_begin) { + bdrv_parent_drained_begin_single(child, true); + drain_saldo--; + } + if (old_bs) { /* Detach first so that the recursive drain sections coming from @child * are already gone and we only end the drain sections that came from @@ -2173,16 +2277,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child, if (child->role->detach) { child->role->detach(child); } - if (old_bs->quiesce_counter && child->role->drained_end) { - int num = old_bs->quiesce_counter; - if (child->role->parent_is_bds) { - num -= bdrv_drain_all_count; - } - assert(num >= 0); - for (i = 0; i < num; i++) { - child->role->drained_end(child); - } - } QLIST_REMOVE(child, next_parent); } @@ -2190,16 +2284,15 @@ static void bdrv_replace_child_noperm(BdrvChild *child, if (new_bs) { QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); - if (new_bs->quiesce_counter && child->role->drained_begin) { - int num = new_bs->quiesce_counter; - if (child->role->parent_is_bds) { - num -= bdrv_drain_all_count; - } - assert(num >= 0); - for (i = 0; i < num; i++) { - bdrv_parent_drained_begin_single(child, true); - } - } + + /* + * Detaching the old node may have led to the new node's + * quiesce_counter having been decreased. Not a problem, we + * just need to recognize this here and then invoke + * drained_end appropriately more often. + */ + assert(new_bs->quiesce_counter <= new_bs_quiesce_counter); + drain_saldo += new_bs->quiesce_counter - new_bs_quiesce_counter; /* Attach only after starting new drained sections, so that recursive * drain sections coming from @child don't get an extra .drained_begin @@ -2208,6 +2301,15 @@ static void bdrv_replace_child_noperm(BdrvChild *child, child->role->attach(child); } } + + /* + * If the old child node was drained but the new one is not, allow + * requests to come in only after the new node has been attached. + */ + while (drain_saldo < 0 && child->role->drained_end) { + bdrv_parent_drained_end_single(child); + drain_saldo++; + } } /* @@ -2228,23 +2330,41 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) bdrv_replace_child_noperm(child, new_bs); + /* + * Start with the new node's permissions. If @new_bs is a (direct + * or indirect) child of @old_bs, we must complete the permission + * update on @new_bs before we loosen the restrictions on @old_bs. + * Otherwise, bdrv_check_perm() on @old_bs would re-initiate + * updating the permissions of @new_bs, and thus not purely loosen + * restrictions. + */ + if (new_bs) { + bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm); + bdrv_set_perm(new_bs, perm, shared_perm); + } + if (old_bs) { /* Update permissions for old node. This is guaranteed to succeed * because we're just taking a parent away, so we're loosening * restrictions. */ + bool tighten_restrictions; + int ret; + bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm); - bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL, &error_abort); - bdrv_set_perm(old_bs, perm, shared_perm); + ret = bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL, + &tighten_restrictions, NULL); + assert(tighten_restrictions == false); + if (ret < 0) { + /* We only tried to loosen restrictions, so errors are not fatal */ + bdrv_abort_perm_update(old_bs); + } else { + bdrv_set_perm(old_bs, perm, shared_perm); + } /* When the parent requiring a non-default AioContext is removed, the * node moves back to the main AioContext */ bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL); } - - if (new_bs) { - bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm); - bdrv_set_perm(new_bs, perm, shared_perm); - } } /* @@ -2268,7 +2388,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, Error *local_err = NULL; int ret; - ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp); + ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, NULL, + errp); if (ret < 0) { bdrv_abort_perm_update(child_bs); bdrv_unref(child_bs); @@ -2375,18 +2496,20 @@ void bdrv_root_unref_child(BdrvChild *child) bdrv_unref(child_bs); } -void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) +/** + * Clear all inherits_from pointers from children and grandchildren of + * @root that point to @root, where necessary. + */ +static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child) { - if (child == NULL) { - return; - } - - if (child->bs->inherits_from == parent) { - BdrvChild *c; + BdrvChild *c; - /* Remove inherits_from only when the last reference between parent and - * child->bs goes away. */ - QLIST_FOREACH(c, &parent->children, next) { + if (child->bs->inherits_from == root) { + /* + * Remove inherits_from only when the last reference between root and + * child->bs goes away. + */ + QLIST_FOREACH(c, &root->children, next) { if (c != child && c->bs == child->bs) { break; } @@ -2396,6 +2519,18 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) } } + QLIST_FOREACH(c, &child->bs->children, next) { + bdrv_unset_inherits_from(root, c); + } +} + +void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) +{ + if (child == NULL) { + return; + } + + bdrv_unset_inherits_from(parent, child); bdrv_root_unref_child(child); } @@ -2902,6 +3037,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, "use \"backing\": null instead"); } flags |= BDRV_O_NO_BACKING; + qdict_del(bs->explicit_options, "backing"); + qdict_del(bs->options, "backing"); qdict_del(options, "backing"); } @@ -3132,7 +3269,7 @@ static bool bdrv_recurse_has_child(BlockDriverState *bs, * Adds a BlockDriverState to a simple queue for an atomic, transactional * reopen of multiple devices. * - * bs_queue can either be an existing BlockReopenQueue that has had QSIMPLE_INIT + * bs_queue can either be an existing BlockReopenQueue that has had QTAILQ_INIT * already performed, or alternatively may be NULL a new BlockReopenQueue will * be created and initialized. This newly created BlockReopenQueue should be * passed back in for subsequent calls that are intended to be of the same @@ -3173,7 +3310,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); - QSIMPLEQ_INIT(bs_queue); + QTAILQ_INIT(bs_queue); } if (!options) { @@ -3181,7 +3318,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, } /* Check if this BlockDriverState is already in the queue */ - QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + QTAILQ_FOREACH(bs_entry, bs_queue, entry) { if (bs == bs_entry->state.bs) { break; } @@ -3237,7 +3374,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, if (!bs_entry) { bs_entry = g_new0(BlockReopenQueueEntry, 1); - QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); + QTAILQ_INSERT_TAIL(bs_queue, bs_entry, entry); } else { qobject_unref(bs_entry->state.options); qobject_unref(bs_entry->state.explicit_options); @@ -3338,7 +3475,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) assert(bs_queue != NULL); - QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + QTAILQ_FOREACH(bs_entry, bs_queue, entry) { assert(bs_entry->state.bs->quiesce_counter > 0); if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) { goto cleanup; @@ -3346,10 +3483,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) bs_entry->prepared = true; } - QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + QTAILQ_FOREACH(bs_entry, bs_queue, entry) { BDRVReopenState *state = &bs_entry->state; ret = bdrv_check_perm(state->bs, bs_queue, state->perm, - state->shared_perm, NULL, errp); + state->shared_perm, NULL, NULL, errp); if (ret < 0) { goto cleanup_perm; } @@ -3361,7 +3498,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) state->perm, state->shared_perm, &nperm, &nshared); ret = bdrv_check_update_perm(state->new_backing_bs, NULL, - nperm, nshared, NULL, errp); + nperm, nshared, NULL, NULL, errp); if (ret < 0) { goto cleanup_perm; } @@ -3369,16 +3506,22 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) bs_entry->perms_checked = true; } - /* If we reach this point, we have success and just need to apply the - * changes + /* + * If we reach this point, we have success and just need to apply the + * changes. + * + * Reverse order is used to comfort qcow2 driver: on commit it need to write + * IN_USE flag to the image, to mark bitmaps in the image as invalid. But + * children are usually goes after parents in reopen-queue, so go from last + * to first element. */ - QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { bdrv_reopen_commit(&bs_entry->state); } ret = 0; cleanup_perm: - QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { BDRVReopenState *state = &bs_entry->state; if (!bs_entry->perms_checked) { @@ -3395,7 +3538,7 @@ cleanup_perm: } } cleanup: - QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { if (ret) { if (bs_entry->prepared) { bdrv_reopen_abort(&bs_entry->state); @@ -3435,7 +3578,7 @@ static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q, { BlockReopenQueueEntry *entry; - QSIMPLEQ_FOREACH(entry, q, entry) { + QTAILQ_FOREACH(entry, q, entry) { BlockDriverState *bs = entry->state.bs; BdrvChild *child; @@ -3812,16 +3955,12 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) BlockDriver *drv; BlockDriverState *bs; BdrvChild *child; - bool old_can_write, new_can_write; assert(reopen_state != NULL); bs = reopen_state->bs; drv = bs->drv; assert(drv != NULL); - old_can_write = - !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); - /* If there are any driver level actions to take */ if (drv->bdrv_reopen_commit) { drv->bdrv_reopen_commit(reopen_state); @@ -3865,21 +4004,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) } bdrv_refresh_limits(bs, NULL); - - new_can_write = - !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); - if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) { - Error *local_err = NULL; - if (drv->bdrv_reopen_bitmaps_rw(bs, &local_err) < 0) { - /* This is not fatal, bitmaps just left read-only, so all following - * writes will fail. User can remove read-only bitmaps to unblock - * writes. - */ - error_reportf_err(local_err, - "%s: Failed to make dirty bitmaps writable: ", - bdrv_get_node_name(bs)); - } - } } /* @@ -4048,7 +4172,6 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, { BdrvChild *c, *next; GSList *list = NULL, *p; - uint64_t old_perm, old_shared; uint64_t perm = 0, shared = BLK_PERM_ALL; int ret; @@ -4077,7 +4200,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, /* Check whether the required permissions can be granted on @to, ignoring * all BdrvChild in @list so that they can't block themselves. */ - ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp); + ret = bdrv_check_update_perm(to, NULL, perm, shared, list, NULL, errp); if (ret < 0) { bdrv_abort_perm_update(to); goto out; @@ -4094,8 +4217,8 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, bdrv_unref(from); } - bdrv_get_cumulative_perm(to, &old_perm, &old_shared); - bdrv_set_perm(to, old_perm | perm, old_shared | shared); + bdrv_get_cumulative_perm(to, &perm, &shared); + bdrv_set_perm(to, perm, shared); out: g_slist_free(list); @@ -4319,6 +4442,14 @@ int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, return -EPERM; } + for (i = bs; i != base; i = backing_bs(i)) { + if (i->backing && backing_bs(i)->never_freeze) { + error_setg(errp, "Cannot freeze '%s' link to '%s'", + i->backing->name, backing_bs(i)->node_name); + return -EPERM; + } + } + for (i = bs; i != base; i = backing_bs(i)) { if (i->backing) { i->backing->frozen = true; @@ -4384,6 +4515,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, int ret = -EIO; bdrv_ref(top); + bdrv_subtree_drained_begin(top); if (!top->drv || !base->drv) { goto exit; @@ -4424,7 +4556,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, /* Check whether we are allowed to switch c from top to base */ GSList *ignore_children = g_slist_prepend(NULL, c); ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, - ignore_children, &local_err); + ignore_children, NULL, &local_err); g_slist_free(ignore_children); if (ret < 0) { error_report_err(local_err); @@ -4455,6 +4587,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, ret = 0; exit: + bdrv_subtree_drained_end(top); bdrv_unref(top); return ret; } @@ -4739,36 +4872,23 @@ static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, const BdrvChild *child) { - typedef struct { - unsigned int flag; - BlockPermission num; - } PermissionMap; - - static const PermissionMap permissions[] = { - { BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ }, - { BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE }, - { BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED }, - { BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE }, - { BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD }, - { 0, 0 } - }; - const PermissionMap *p; + BlockPermission qapi_perm; XDbgBlockGraphEdge *edge; - QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1); - edge = g_new0(XDbgBlockGraphEdge, 1); edge->parent = xdbg_graph_node_num(gr, parent); edge->child = xdbg_graph_node_num(gr, child->bs); edge->name = g_strdup(child->name); - for (p = permissions; p->flag; p++) { - if (p->flag & child->perm) { - QAPI_LIST_ADD(edge->perm, p->num); + for (qapi_perm = 0; qapi_perm < BLOCK_PERMISSION__MAX; qapi_perm++) { + uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm); + + if (flag & child->perm) { + QAPI_LIST_ADD(edge->perm, qapi_perm); } - if (p->flag & child->shared_perm) { - QAPI_LIST_ADD(edge->shared_perm, p->num); + if (flag & child->shared_perm) { + QAPI_LIST_ADD(edge->shared_perm, qapi_perm); } } @@ -4951,6 +5071,27 @@ int bdrv_has_zero_init(BlockDriverState *bs) return 0; } +int bdrv_has_zero_init_truncate(BlockDriverState *bs) +{ + if (!bs->drv) { + return 0; + } + + if (bs->backing) { + /* Depends on the backing image length, but better safe than sorry */ + return 0; + } + if (bs->drv->bdrv_has_zero_init_truncate) { + return bs->drv->bdrv_has_zero_init_truncate(bs); + } + if (bs->file && bs->drv->is_filter) { + return bdrv_has_zero_init_truncate(bs->file->bs); + } + + /* safe default */ + return 0; +} + bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs) { BlockDriverInfo bdi; @@ -5008,6 +5149,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, return NULL; } +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv || !drv->bdrv_get_specific_stats) { + return NULL; + } + return drv->bdrv_get_specific_stats(bs); +} + void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) { if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) { @@ -5017,14 +5167,35 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) bs->drv->bdrv_debug_event(bs, event); } -int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, - const char *tag) +static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) { while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { - bs = bs->file ? bs->file->bs : NULL; + if (bs->file) { + bs = bs->file->bs; + continue; + } + + if (bs->drv->is_filter && bs->backing) { + bs = bs->backing->bs; + continue; + } + + break; } if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { + assert(bs->drv->bdrv_debug_remove_breakpoint); + return bs; + } + + return NULL; +} + +int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, + const char *tag) +{ + bs = bdrv_find_debug_node(bs); + if (bs) { return bs->drv->bdrv_debug_breakpoint(bs, event, tag); } @@ -5033,11 +5204,8 @@ int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) { - while (bs && bs->drv && !bs->drv->bdrv_debug_remove_breakpoint) { - bs = bs->file ? bs->file->bs : NULL; - } - - if (bs && bs->drv && bs->drv->bdrv_debug_remove_breakpoint) { + bs = bdrv_find_debug_node(bs); + if (bs) { return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); } @@ -5172,10 +5340,6 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, return; } - if (!(bs->open_flags & BDRV_O_INACTIVE)) { - return; - } - QLIST_FOREACH(child, &bs->children, next) { bdrv_co_invalidate_cache(child->bs, &local_err); if (local_err) { @@ -5197,36 +5361,36 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, * just keep the extended permissions for the next time that an activation * of the image is tried. */ - bs->open_flags &= ~BDRV_O_INACTIVE; - 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) { + if (bs->open_flags & BDRV_O_INACTIVE) { + bs->open_flags &= ~BDRV_O_INACTIVE; + bdrv_get_cumulative_perm(bs, &perm, &shared_perm); + ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, 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); - for (bm = bdrv_dirty_bitmap_next(bs, NULL); bm; - bm = bdrv_dirty_bitmap_next(bs, bm)) - { - bdrv_dirty_bitmap_set_migration(bm, false); - } + 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; + } + } - ret = refresh_total_sectors(bs, bs->total_sectors); - if (ret < 0) { - bs->open_flags |= BDRV_O_INACTIVE; - error_setg_errno(errp, -ret, "Could not refresh total sector count"); - return; + FOR_EACH_DIRTY_BITMAP(bs, bm) { + bdrv_dirty_bitmap_skip_store(bm, false); + } + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + bs->open_flags |= BDRV_O_INACTIVE; + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + return; + } } QLIST_FOREACH(parent, &bs->parents, next_parent) { @@ -5313,6 +5477,7 @@ static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) static int bdrv_inactivate_recurse(BlockDriverState *bs) { BdrvChild *child, *parent; + bool tighten_restrictions; uint64_t perm, shared_perm; int ret; @@ -5349,8 +5514,15 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) /* Update permissions, they may differ for inactive nodes */ bdrv_get_cumulative_perm(bs, &perm, &shared_perm); - bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort); - bdrv_set_perm(bs, perm, shared_perm); + ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, + &tighten_restrictions, NULL); + assert(tighten_restrictions == false); + if (ret < 0) { + /* We only tried to loosen restrictions, so errors are not fatal */ + bdrv_abort_perm_update(bs); + } else { + bdrv_set_perm(bs, perm, shared_perm); + } /* Recursively inactivate children */ @@ -5582,12 +5754,11 @@ void bdrv_img_create(const char *filename, const char *fmt, return; } + /* Create parameter list */ create_opts = qemu_opts_append(create_opts, drv->create_opts); create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); - /* Create parameter list with default values */ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); /* Parse -o options */ if (options) { @@ -5597,6 +5768,13 @@ void bdrv_img_create(const char *filename, const char *fmt, } } + if (!qemu_opt_get(opts, BLOCK_OPT_SIZE)) { + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); + } else if (img_size != UINT64_C(-1)) { + error_setg(errp, "The image size must be specified only once"); + goto out; + } + if (base_filename) { qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &local_err); if (local_err) { @@ -5791,6 +5969,8 @@ static void bdrv_attach_aio_context(BlockDriverState *bs, * Changes the AioContext used for fd handlers, timers, and BHs by this * BlockDriverState and all its children and parents. * + * Must be called from the main AioContext. + * * 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). @@ -5801,9 +5981,12 @@ static void bdrv_attach_aio_context(BlockDriverState *bs, void bdrv_set_aio_context_ignore(BlockDriverState *bs, AioContext *new_context, GSList **ignore) { + AioContext *old_context = bdrv_get_aio_context(bs); BdrvChild *child; - if (bdrv_get_aio_context(bs) == new_context) { + g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + + if (old_context == new_context) { return; } @@ -5827,13 +6010,31 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs, bdrv_detach_aio_context(bs); - /* This function executes in the old AioContext so acquire the new one in - * case it runs in a different thread. - */ - aio_context_acquire(new_context); + /* Acquire the new context, if necessary */ + if (qemu_get_aio_context() != new_context) { + aio_context_acquire(new_context); + } + bdrv_attach_aio_context(bs, new_context); + + /* + * If this function was recursively called from + * bdrv_set_aio_context_ignore(), there may be nodes in the + * subtree that have not yet been moved to the new AioContext. + * Release the old one so bdrv_drained_end() can poll them. + */ + if (qemu_get_aio_context() != old_context) { + aio_context_release(old_context); + } + bdrv_drained_end(bs); - aio_context_release(new_context); + + if (qemu_get_aio_context() != old_context) { + aio_context_acquire(old_context); + } + if (qemu_get_aio_context() != new_context) { + aio_context_release(new_context); + } } static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, @@ -6377,25 +6578,3 @@ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) parent_bs->drv->bdrv_del_child(parent_bs, child, errp); } - -bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, - uint32_t granularity, Error **errp) -{ - BlockDriver *drv = bs->drv; - - if (!drv) { - error_setg_errno(errp, ENOMEDIUM, - "Can't store persistent bitmaps to %s", - bdrv_get_device_or_node_name(bs)); - return false; - } - - if (!drv->bdrv_can_store_new_dirty_bitmap) { - error_setg_errno(errp, ENOTSUP, - "Can't store persistent bitmaps to %s", - bdrv_get_device_or_node_name(bs)); - return false; - } - - return drv->bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp); -}