diff mbox series

[1/2] qapi: block-dirty-bitmap-merge: support allocation maps

Message ID 20210427111126.84307-2-vsementsov@virtuozzo.com (mailing list archive)
State New, archived
Headers show
Series support allocation-map for block-dirty-bitmap-merge | expand

Commit Message

Vladimir Sementsov-Ogievskiy April 27, 2021, 11:11 a.m. UTC
Add possibility to merge allocation map of specified node into target
bitmap.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@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(-)

Comments

Vladimir Sementsov-Ogievskiy April 27, 2021, 11:59 a.m. UTC | #1
27.04.2021 14:11, Vladimir Sementsov-Ogievskiy wrote:
> Add possibility to merge allocation map of specified node into target
> bitmap.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy<vsementsov@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 }
>   
>   ##


So, what I propose makes possible to issue the following command:

block-dirty-bitmap-merge
   bitmaps: [{"allocation-map": "top", "node": "drive0"}]
   node: target-node-name
   target: target-bitmap-name


I've discussed it with Nikolay, and he requested a possibility of querying allocation status of base..top sub-chain (assume several snapshots was done without creating bitmaps, and we want to restore the bitmap for backup).

So, we actually want something like

block-dirty-bitmap-merge
   bitmaps: [{top: "tpp-node-name", bottom: "bottom-node-name"}]
   node: target-node-name
   target: target-bitmap-name
diff mbox series

Patch

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;
+            }
         }
     }