Currently, dirty bitmaps are for internal use only and there is no support for accessing their content from third party-apps. This patch implements new command block-dirty-bitmap-dump, which returns content of the dirty bitmap encoded in base64. This is very useful especially in combination with a drive that uses raw format because third-party apps can easily use it to create incremental or differential backup.
Signed-off-by: Patrik Janoušek <p...@patrikjanousek.cz> --- block/monitor/bitmap-qmp-cmds.c | 61 +++++++++++++++++++++++++++++++ qapi/block-core.json | 64 ++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index 9f11deec64..7f296e9ba7 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -146,6 +146,67 @@ out: aio_context_release(aio_context); } +BlockDirtyBitmapContent *qmp_block_dirty_bitmap_dump(const char *node, + const char *name, + bool has_clear, bool clear, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + BlockDirtyBitmapContent *bdbc; + HBitmap *hb; + AioContext *aio_context; + + if (!name || name[0] == '\0') { + error_setg(errp, "Bitmap name cannot be empty"); + return NULL; + } + + bs = bdrv_lookup_bs(node, node, errp); + if (!bs) { + return NULL; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap || !bs) { + return NULL; + } + + if (has_clear && clear) { + /** + * Transactions cannot return value, so "clear" functionality must be + * implemented here while holding AiO context + */ + + bdrv_clear_dirty_bitmap(bitmap, &hb); + + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); + uint64_t tb_size = hbitmap_serialization_size(hb, 0, bm_size); + uint8_t *buf = g_malloc(tb_size); + + hbitmap_serialize_part(hb, buf, 0, bm_size); + + bdbc = g_new0(BlockDirtyBitmapContent, 1); + bdbc->content = g_base64_encode((guchar *) buf, tb_size); + } else { + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); + uint64_t tb_size = bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size); + uint8_t *buf = g_malloc(tb_size); + + bdrv_dirty_bitmap_serialize_part(bitmap, buf, 0, bm_size); + + bdbc = g_new0(BlockDirtyBitmapContent, 1); + bdbc->content = g_base64_encode((guchar *) buf, tb_size); + } + + aio_context_release(aio_context); + + return bdbc; +} + BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, bool release, BlockDriverState **bitmap_bs, diff --git a/qapi/block-core.json b/qapi/block-core.json index 04ad80bc1e..cbe3dac384 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2031,6 +2031,14 @@ { 'struct': 'BlockDirtyBitmap', 'data': { 'node': 'str', 'name': 'str' } } +## +# @BlockDirtyBitmapContent: +# +# @content: content of dirty bitmap (encoded in base64) +## +{ 'struct': 'BlockDirtyBitmapContent', + 'data': { 'content': 'str' } } + ## # @BlockDirtyBitmapAdd: # @@ -2056,6 +2064,18 @@ 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', '*persistent': 'bool', '*disabled': 'bool' } } +## +# @BlockDirtyBitmapDump: +# +# @node: name of device/node which the bitmap is tracking +# +# @name: name of the dirty bitmap (must be less than 1024 bytes) +# +# @clear: true if bitmap should be cleared after dump +## +{ 'struct': 'BlockDirtyBitmapDump', + 'data': { 'node': 'str', 'name': 'str', '*clear': 'bool' } } + ## # @BlockDirtyBitmapMergeSource: # @@ -2086,6 +2106,26 @@ 'data': { 'node': 'str', 'target': 'str', 'bitmaps': ['BlockDirtyBitmapMergeSource'] } } +## +# @block-dirty-bitmap-dump: +# +# Dump a dirty bitmap with a name on the node. +# +# Returns: - nothing on success +# - If @node is not a valid block device or node, DeviceNotFound +# - If @name is already taken, GenericError with an explanation +# +# Example: +# +# -> { "execute": "block-dirty-bitmap-dump", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": { "content": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFt... (trunc)" } } +# +## +{ 'command': 'block-dirty-bitmap-dump', + 'data': 'BlockDirtyBitmapDump', + 'returns': 'BlockDirtyBitmapContent' } + ## # @block-dirty-bitmap-add: # @@ -3908,6 +3948,26 @@ '*x-dirty-bitmap': 'str', '*reconnect-delay': 'uint32' } } +## +# @BlockdevOptionsRawDirtyBitmap: +# +# Dirty bitmap options for the raw driver. +# +# @name: the name of the dirty bitmap (Since 2.4) +# +# @filename: the filename of the dirty bitmap +# +# @granularity: granularity of the dirty bitmap in bytes (since 1.4) +# +# @persistent: true if the bitmap was stored on disk, is scheduled to be stored +# on disk, or both. (since 4.0) +# +# @disabled: true if the bitmap should not be loaded (and saved) automatically +## +{ 'struct': 'BlockdevOptionsRawDirtyBitmap', + 'data': {'*name': 'str', 'filename': 'str', 'granularity': 'uint32', + 'persistent': 'bool', '*disabled': 'bool' } } + ## # @BlockdevOptionsRaw: # @@ -3915,12 +3975,14 @@ # # @offset: position where the block device starts # @size: the assumed size of the device +# @dirty-bitmaps: dirty bitmaps of the raw block device # # Since: 2.9 ## { 'struct': 'BlockdevOptionsRaw', 'base': 'BlockdevOptionsGenericFormat', - 'data': { '*offset': 'int', '*size': 'int' } } + 'data': { '*offset': 'int', '*size': 'int' , + '*dirty-bitmaps': ['BlockdevOptionsRawDirtyBitmap'] } } ## # @BlockdevOptionsThrottle: -- 2.31.0