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 <pj@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
20.03.2021 12:32, Patrik Janoušek wrote: > Currently, dirty bitmaps are for internal use only As I said, that's not correct: for external use we have bitmap export through NBD protocol. Protocol description is here https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md (you need "Metadata querying" section) And qemu bitmap export through NBD "metadata querying" feature is described here: docs/interop/nbd.txt -- Best regards, Vladimir
Am 20.03.2021 um 10:32 hat Patrik Janoušek geschrieben: > 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 <pj@patrikjanousek.cz> People have already pointed you to NBD to get the block dirty status (and they are right), but I think nobody has explained yet why we decided against a QMP command to return bitmaps. The problem is that disk images can be huge, and this means that dirty bitmaps describing them get pretty large, too. So your new QMP command ends up reading many megabytes from disk and sending it to the socket. While it's doing all of this I/O, QEMU is blocked and the guest may appear to be hanging until it completes. The client would also have to download the whole bitmap even if it is only interested in specific blocks. (This may or may not be relevant for you specific use case.) With the NBD export, the client can query a specific block range and its request is processed asynchronously in the background while the guest and the QMP monitor are still available. Kevin
© 2016 - 2024 Red Hat, Inc.