Add possibility to merge allocation map of specified node into target bitmap.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- qapi/block-core.json | 31 +++++++++++++++++-- include/block/block_int.h | 4 +++ block/dirty-bitmap.c | 42 +++++++++++++++++++++++++ block/monitor/bitmap-qmp-cmds.c | 55 ++++++++++++++++++++++++++++----- 4 files changed, 122 insertions(+), 10 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 6d227924d0..0fafb043bc 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2006,6 +2006,32 @@ 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', '*persistent': 'bool', '*disabled': 'bool' } } +## +# @AllocationMapMode: +# +# An enumeration of possible allocation maps that could be merged into target +# bitmap. +# +# @top: The allocation status of the top layer of the attached storage node. +# +# Since: 6.1 +## +{ 'enum': 'AllocationMapMode', + 'data': ['top'] } + +## +# @BlockDirtyBitmapMergeExternalSource: +# +# @node: name of device/node which the bitmap is tracking +# +# @name: name of the dirty bitmap +# +# Since: 6.1 +## +{ 'struct': 'BlockDirtyBitmapMergeExternalSource', + 'data': { 'node': 'str', '*name': 'str', + '*allocation-map': 'AllocationMapMode' } } + ## # @BlockDirtyBitmapMergeSource: # @@ -2017,7 +2043,7 @@ ## { 'alternate': 'BlockDirtyBitmapMergeSource', 'data': { 'local': 'str', - 'external': 'BlockDirtyBitmap' } } + 'external': 'BlockDirtyBitmapMergeExternalSource' } } ## # @BlockDirtyBitmapMerge: @@ -2176,7 +2202,8 @@ # ## { 'command': 'block-dirty-bitmap-merge', - 'data': 'BlockDirtyBitmapMerge' } + 'data': 'BlockDirtyBitmapMerge', + 'coroutine': true } ## # @BlockDirtyBitmapSha256: diff --git a/include/block/block_int.h b/include/block/block_int.h index 88e4111939..b5aeaef425 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1361,6 +1361,10 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, HBitmap **backup, bool lock); +int bdrv_merge_allocation_top_to_dirty_bitmap(BdrvDirtyBitmap *dest, + BlockDriverState *bs, + Error **errp); + void bdrv_inc_in_flight(BlockDriverState *bs); void bdrv_dec_in_flight(BlockDriverState *bs); diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 68d295d6e3..78097e30c5 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -914,6 +914,48 @@ out: } } +int bdrv_merge_allocation_top_to_dirty_bitmap(BdrvDirtyBitmap *dest, + BlockDriverState *bs, + Error **errp) +{ + int ret; + int64_t offset = 0; + int64_t bytes = bdrv_getlength(bs); + + if (bytes < 0) { + error_setg(errp, "Failed to get length of node '%s'", + bdrv_get_node_name(bs)); + return bytes; + } + + if (bdrv_dirty_bitmap_size(dest) < bytes) { + error_setg(errp, "Bitmap is smaller than node '%s'", + bdrv_get_node_name(bs)); + return -EINVAL; + } + + while (bytes) { + int64_t cur_bytes = bytes; + + ret = bdrv_is_allocated(bs, offset, cur_bytes, &cur_bytes); + if (ret < 0) { + error_setg(errp, + "Failed to get block allocation status of node '%s'", + bdrv_get_node_name(bs)); + return ret; + } + + if (ret) { + bdrv_set_dirty_bitmap(dest, offset, cur_bytes); + } + + bytes -= cur_bytes; + offset += cur_bytes; + } + + return 0; +} + /** * bdrv_dirty_bitmap_merge_internal: merge src into dest. * Does NOT check bitmap permissions; not suitable for use as public API. diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index 9f11deec64..19845a22c4 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -34,6 +34,7 @@ #include "block/block_int.h" #include "qapi/qapi-commands-block.h" +#include "qapi/qapi-types-block-core.h" #include "qapi/error.h" /** @@ -273,8 +274,11 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, } for (lst = bms; lst; lst = lst->next) { + src = NULL; + switch (lst->value->type) { const char *name, *node; + bool has_alloc, has_name; case QTYPE_QSTRING: name = lst->value->u.local; src = bdrv_find_dirty_bitmap(bs, name); @@ -286,22 +290,57 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, break; case QTYPE_QDICT: node = lst->value->u.external.node; - name = lst->value->u.external.name; - src = block_dirty_bitmap_lookup(node, name, NULL, errp); - if (!src) { + has_name = lst->value->u.external.has_name; + has_alloc = lst->value->u.external.has_allocation_map; + if (has_name == has_alloc) { + error_setg(errp, "Exactly one of @name and @allocation-map " + "fields must be specified."); dst = NULL; goto out; } + if (has_name) { + name = lst->value->u.external.name; + src = block_dirty_bitmap_lookup(node, name, NULL, errp); + if (!src) { + dst = NULL; + goto out; + } + } else { + int ret; + AioContext *old_ctx; + assert(has_alloc); + /* The only existing mode currently is 'top' */ + assert(lst->value->u.external.allocation_map == + ALLOCATION_MAP_MODE_TOP); + + bs = bdrv_lookup_bs(node, node, NULL); + if (!bs) { + error_setg(errp, "Node '%s' not found", node); + dst = NULL; + goto out; + } + + old_ctx = bdrv_co_enter(bs); + ret = bdrv_merge_allocation_top_to_dirty_bitmap(anon, bs, errp); + bdrv_co_leave(bs, old_ctx); + + if (ret < 0) { + dst = NULL; + goto out; + } + } break; default: abort(); } - bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err); - if (local_err) { - error_propagate(errp, local_err); - dst = NULL; - goto out; + if (src) { + bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + dst = NULL; + goto out; + } } } -- 2.29.2