diff mbox

qmp: add query-block-dirty-bitmap

Message ID 1453482459-80179-2-git-send-email-vsementsov@virtuozzo.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vladimir Sementsov-Ogievskiy Jan. 22, 2016, 5:07 p.m. UTC
Add qmp command to query dirty bitmap. This is needed for external
backup.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block.c               | 41 ++++++++++++++++++++++++++++++++++++++++
 blockdev.c            | 21 +++++++++++++++++++++
 include/block/block.h |  2 ++
 qapi/block-core.json  | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qmp-commands.hx       | 22 ++++++++++++++++++++++
 5 files changed, 138 insertions(+)

Comments

Denis V. Lunev Jan. 22, 2016, 5:22 p.m. UTC | #1
On 01/22/2016 08:07 PM, Vladimir Sementsov-Ogievskiy wrote:
> Add qmp command to query dirty bitmap. This is needed for external
> backup.
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   block.c               | 41 ++++++++++++++++++++++++++++++++++++++++
>   blockdev.c            | 21 +++++++++++++++++++++
>   include/block/block.h |  2 ++
>   qapi/block-core.json  | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   qmp-commands.hx       | 22 ++++++++++++++++++++++
>   5 files changed, 138 insertions(+)
>
> diff --git a/block.c b/block.c
> index 5709d3d..9a28589 100644
> --- a/block.c
> +++ b/block.c
> @@ -3717,6 +3717,47 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
>       }
>   }
>   
> +BlockDirtyBitmapInfo *bdrv_query_dirty_bitmap(BlockDriverState *bs,
> +                                              BdrvDirtyBitmap *bitmap)
> +{
> +    BlockDirtyBitmapInfo *info = g_new0(BlockDirtyBitmapInfo, 1);
> +    BlockDirtyRegionList **plist = &info->dirty_regions;
> +    uint64_t begin = 0, end = 0, size = bitmap->size;
> +    HBitmap *hb = bitmap->bitmap;
> +
> +    info->dirty_count = bdrv_get_dirty_count(bitmap);
> +    info->granularity = bdrv_dirty_bitmap_granularity(bitmap);
> +    info->size = bitmap->size;
> +    info->name = g_strdup(bitmap->name);
> +    info->disabled = bitmap->disabled;
> +    info->dirty_regions = NULL;
> +
> +    for (; begin < size; ++begin) {
> +        BlockDirtyRegion *region;
> +        BlockDirtyRegionList *entry;
> +
> +        if (!hbitmap_get(hb, begin)) {
> +            continue;
> +        }
> +
> +        for (end = begin + 1; end < size && hbitmap_get(hb, end); ++end) {
> +            ;
> +        }
let us implemented faster finder. This would be useful in other places. 
It could be
100 times faster!

> +
> +        region = g_new0(BlockDirtyRegion, 1);
> +        entry = g_new0(BlockDirtyRegionList, 1);
> +        region->start = begin;
> +        region->count = end - begin;
> +        entry->value = region;
> +        *plist = entry;
> +        plist = &entry->next;
> +
> +        begin = end;
> +    }
> +
> +    return info;
> +}
> +
>   /**
>    * Advance an HBitmapIter to an arbitrary offset.
>    */
> diff --git a/blockdev.c b/blockdev.c
> index 07cfe25..d2bc453 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -2734,6 +2734,27 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
>       aio_context_release(aio_context);
>   }
>   
> +BlockDirtyBitmapInfo *qmp_query_block_dirty_bitmap(const char *node,
> +                                                   const char *name,
> +                                                   Error **errp)
> +{
> +    AioContext *aio_context;
> +    BdrvDirtyBitmap *bitmap;
> +    BlockDriverState *bs;
> +    BlockDirtyBitmapInfo *ret = NULL;
> +
> +    bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
> +    if (!bitmap) {
> +        return NULL;
> +    }
> +
> +    ret = bdrv_query_dirty_bitmap(bs, bitmap);
> +
> +    aio_context_release(aio_context);
> +
> +    return ret;
> +}
> +
>   void hmp_drive_del(Monitor *mon, const QDict *qdict)
>   {
>       const char *id = qdict_get_str(qdict, "id");
> diff --git a/include/block/block.h b/include/block/block.h
> index 25f36dc..9d6bd33 100644
> --- a/include/block/block.h
> +++ b/include/block/block.h
> @@ -505,6 +505,8 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
>                              int64_t cur_sector, int nr_sectors);
>   void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
>                                int64_t cur_sector, int nr_sectors);
> +BlockDirtyBitmapInfo *bdrv_query_dirty_bitmap(BlockDriverState *bs,
> +                                              BdrvDirtyBitmap *bitmap);
>   void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
>   void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
>   int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 0a915ed..12ed759 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -414,6 +414,58 @@
>   ##
>   { 'command': 'query-block', 'returns': ['BlockInfo'] }
>   
> +##
> +# @BlockDirtyRegion:
> +#
> +# Region in bytes.
> +#
> +# @start: first byte
> +#
> +# @count: number of bytes in the region
> +#
> +# Since: 2.3
> +##
> +{ 'struct': 'BlockDirtyRegion',
> +  'data': { 'start': 'int', 'count': 'int' } }
> +
> +##
> +# @BlockDirtyBitmapInfo
> +#
> +# @name: the name of the dirty bitmap
> +#
> +# @size: size of the dirty bitmap in sectors
> +#
> +# @granularity: granularity of the dirty bitmap in bytes
> +#
> +# @disabled: whether the dirty bitmap is disabled
> +#
> +# @dirty-count: number of dirty bytes according to the dirty bitmap
> +#
> +# @dirty-regions: dirty regions of the bitmap
> +#
> +# Since 2.3
> +##
> +{ 'struct': 'BlockDirtyBitmapInfo',
> +  'data': { 'name': 'str',
> +            'size': 'int',
> +            'granularity': 'int',
> +            'disabled': 'bool',
> +            'dirty-count': 'int',
> +            'dirty-regions': ['BlockDirtyRegion'] } }
> +
> +##
> +# @query-block-dirty-bitmap
> +#
> +# Get a description for specified dirty bitmap including it's dirty regions.
> +# This command is in general for testing purposes.
> +#
> +# Returns: @BlockDirtyBitmapInfo
> +#
> +# Since: 2.3
> +##
> +{ 'command': 'query-block-dirty-bitmap',
> +  'data': 'BlockDirtyBitmap',
> +  'returns': 'BlockDirtyBitmapInfo' }
1) should we consider part-by-part retrieval? This could be useful for 
large discs.
2) Change to since 2.6
3) Do you think that content should be retrieved separately than data?


>   
>   ##
>   # @BlockDeviceTimedStats:
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index db072a6..75d9345 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -1457,6 +1457,28 @@ Example:
>   EQMP
>   
>       {
> +        .name       = "query-block-dirty-bitmap",
> +        .args_type  = "node:B,name:s",
> +        .mhandler.cmd_new = qmp_marshal_query_block_dirty_bitmap,
> +    },
> +
> +SQMP
> +
> +query-block-dirty-bitmap
> +------------------------
> +Since 2.6
> +
> +Get dirty bitmap info, including contents. Bitmap data are returned as array of
> +dirty regions
> +
> +Arguments:
> +
> +- "node": device/node on which to remove dirty bitmap (json-string)
> +- "name": name of the dirty bitmap to remove (json-string)
> +
> +EQMP
> +
> +    {
>           .name       = "blockdev-snapshot-sync",
>           .args_type  = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?",
>           .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_sync,
can you pls use den@openvz.org addr sending patches to me?
Vladimir Sementsov-Ogievskiy Jan. 22, 2016, 5:28 p.m. UTC | #2
On 22.01.2016 20:22, Denis V. Lunev wrote:
> On 01/22/2016 08:07 PM, Vladimir Sementsov-Ogievskiy wrote:
>> Add qmp command to query dirty bitmap. This is needed for external
>> backup.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   block.c               | 41 ++++++++++++++++++++++++++++++++++++++++
>>   blockdev.c            | 21 +++++++++++++++++++++
>>   include/block/block.h |  2 ++
>>   qapi/block-core.json  | 52 
>> +++++++++++++++++++++++++++++++++++++++++++++++++++
>>   qmp-commands.hx       | 22 ++++++++++++++++++++++
>>   5 files changed, 138 insertions(+)
>>
>> diff --git a/block.c b/block.c
>> index 5709d3d..9a28589 100644
>> --- a/block.c
>> +++ b/block.c
>> @@ -3717,6 +3717,47 @@ void bdrv_set_dirty(BlockDriverState *bs, 
>> int64_t cur_sector,
>>       }
>>   }
>>   +BlockDirtyBitmapInfo *bdrv_query_dirty_bitmap(BlockDriverState *bs,
>> +                                              BdrvDirtyBitmap *bitmap)
>> +{
>> +    BlockDirtyBitmapInfo *info = g_new0(BlockDirtyBitmapInfo, 1);
>> +    BlockDirtyRegionList **plist = &info->dirty_regions;
>> +    uint64_t begin = 0, end = 0, size = bitmap->size;
>> +    HBitmap *hb = bitmap->bitmap;
>> +
>> +    info->dirty_count = bdrv_get_dirty_count(bitmap);
>> +    info->granularity = bdrv_dirty_bitmap_granularity(bitmap);
>> +    info->size = bitmap->size;
>> +    info->name = g_strdup(bitmap->name);
>> +    info->disabled = bitmap->disabled;
>> +    info->dirty_regions = NULL;
>> +
>> +    for (; begin < size; ++begin) {
>> +        BlockDirtyRegion *region;
>> +        BlockDirtyRegionList *entry;
>> +
>> +        if (!hbitmap_get(hb, begin)) {
>> +            continue;
>> +        }
>> +
>> +        for (end = begin + 1; end < size && hbitmap_get(hb, end); 
>> ++end) {
>> +            ;
>> +        }
> let us implemented faster finder. This would be useful in other 
> places. It could be
> 100 times faster!

I'll use iterators here, if the whole way is ok (querying regions, not blob)

>
>> +
>> +        region = g_new0(BlockDirtyRegion, 1);
>> +        entry = g_new0(BlockDirtyRegionList, 1);
>> +        region->start = begin;
>> +        region->count = end - begin;
>> +        entry->value = region;
>> +        *plist = entry;
>> +        plist = &entry->next;
>> +
>> +        begin = end;
>> +    }
>> +
>> +    return info;
>> +}
>> +
>>   /**
>>    * Advance an HBitmapIter to an arbitrary offset.
>>    */
>> diff --git a/blockdev.c b/blockdev.c
>> index 07cfe25..d2bc453 100644
>> --- a/blockdev.c
>> +++ b/blockdev.c
>> @@ -2734,6 +2734,27 @@ void qmp_block_dirty_bitmap_clear(const char 
>> *node, const char *name,
>>       aio_context_release(aio_context);
>>   }
>>   +BlockDirtyBitmapInfo *qmp_query_block_dirty_bitmap(const char *node,
>> +                                                   const char *name,
>> +                                                   Error **errp)
>> +{
>> +    AioContext *aio_context;
>> +    BdrvDirtyBitmap *bitmap;
>> +    BlockDriverState *bs;
>> +    BlockDirtyBitmapInfo *ret = NULL;
>> +
>> +    bitmap = block_dirty_bitmap_lookup(node, name, &bs, 
>> &aio_context, errp);
>> +    if (!bitmap) {
>> +        return NULL;
>> +    }
>> +
>> +    ret = bdrv_query_dirty_bitmap(bs, bitmap);
>> +
>> +    aio_context_release(aio_context);
>> +
>> +    return ret;
>> +}
>> +
>>   void hmp_drive_del(Monitor *mon, const QDict *qdict)
>>   {
>>       const char *id = qdict_get_str(qdict, "id");
>> diff --git a/include/block/block.h b/include/block/block.h
>> index 25f36dc..9d6bd33 100644
>> --- a/include/block/block.h
>> +++ b/include/block/block.h
>> @@ -505,6 +505,8 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
>>                              int64_t cur_sector, int nr_sectors);
>>   void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
>>                                int64_t cur_sector, int nr_sectors);
>> +BlockDirtyBitmapInfo *bdrv_query_dirty_bitmap(BlockDriverState *bs,
>> +                                              BdrvDirtyBitmap *bitmap);
>>   void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct 
>> HBitmapIter *hbi);
>>   void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
>>   int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index 0a915ed..12ed759 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -414,6 +414,58 @@
>>   ##
>>   { 'command': 'query-block', 'returns': ['BlockInfo'] }
>>   +##
>> +# @BlockDirtyRegion:
>> +#
>> +# Region in bytes.
>> +#
>> +# @start: first byte
>> +#
>> +# @count: number of bytes in the region
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'struct': 'BlockDirtyRegion',
>> +  'data': { 'start': 'int', 'count': 'int' } }
>> +
>> +##
>> +# @BlockDirtyBitmapInfo
>> +#
>> +# @name: the name of the dirty bitmap
>> +#
>> +# @size: size of the dirty bitmap in sectors
>> +#
>> +# @granularity: granularity of the dirty bitmap in bytes
>> +#
>> +# @disabled: whether the dirty bitmap is disabled
>> +#
>> +# @dirty-count: number of dirty bytes according to the dirty bitmap
>> +#
>> +# @dirty-regions: dirty regions of the bitmap
>> +#
>> +# Since 2.3
>> +##
>> +{ 'struct': 'BlockDirtyBitmapInfo',
>> +  'data': { 'name': 'str',
>> +            'size': 'int',
>> +            'granularity': 'int',
>> +            'disabled': 'bool',
>> +            'dirty-count': 'int',
>> +            'dirty-regions': ['BlockDirtyRegion'] } }
>> +
>> +##
>> +# @query-block-dirty-bitmap
>> +#
>> +# Get a description for specified dirty bitmap including it's dirty 
>> regions.
>> +# This command is in general for testing purposes.
>> +#
>> +# Returns: @BlockDirtyBitmapInfo
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'command': 'query-block-dirty-bitmap',
>> +  'data': 'BlockDirtyBitmap',
>> +  'returns': 'BlockDirtyBitmapInfo' }
> 1) should we consider part-by-part retrieval? This could be useful for 
> large discs.
> 2) Change to since 2.6
> 3) Do you think that content should be retrieved separately than data?

3 - what do you mean?


>
>
>>     ##
>>   # @BlockDeviceTimedStats:
>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>> index db072a6..75d9345 100644
>> --- a/qmp-commands.hx
>> +++ b/qmp-commands.hx
>> @@ -1457,6 +1457,28 @@ Example:
>>   EQMP
>>         {
>> +        .name       = "query-block-dirty-bitmap",
>> +        .args_type  = "node:B,name:s",
>> +        .mhandler.cmd_new = qmp_marshal_query_block_dirty_bitmap,
>> +    },
>> +
>> +SQMP
>> +
>> +query-block-dirty-bitmap
>> +------------------------
>> +Since 2.6
>> +
>> +Get dirty bitmap info, including contents. Bitmap data are returned 
>> as array of
>> +dirty regions
>> +
>> +Arguments:
>> +
>> +- "node": device/node on which to remove dirty bitmap (json-string)
>> +- "name": name of the dirty bitmap to remove (json-string)
>> +
>> +EQMP
>> +
>> +    {
>>           .name       = "blockdev-snapshot-sync",
>>           .args_type  = 
>> "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?",
>>           .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_sync,
> can you pls use den@openvz.org addr sending patches to me?

ok, sorry.
Denis V. Lunev Jan. 22, 2016, 6:28 p.m. UTC | #3
On 01/22/2016 08:28 PM, Vladimir Sementsov-Ogievskiy wrote:
> On 22.01.2016 20:22, Denis V. Lunev wrote:
>> On 01/22/2016 08:07 PM, Vladimir Sementsov-Ogievskiy wrote:
>>   { 'command': 'query-block', 'returns': ['BlockInfo'] }
>>   +##
>> +# @BlockDirtyRegion:
>> +#
>> +# Region in bytes.
>> +#
>> +# @start: first byte
>> +#
>> +# @count: number of bytes in the region
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'struct': 'BlockDirtyRegion',
>> +  'data': { 'start': 'int', 'count': 'int' } }
>> +
>> +##
>> +# @BlockDirtyBitmapInfo
>> +#
>> +# @name: the name of the dirty bitmap
>> +#
>> +# @size: size of the dirty bitmap in sectors
>> +#
>> +# @granularity: granularity of the dirty bitmap in bytes
>> +#
>> +# @disabled: whether the dirty bitmap is disabled
>> +#
>> +# @dirty-count: number of dirty bytes according to the dirty bitmap
>> +#
>> +# @dirty-regions: dirty regions of the bitmap
>> +#
>> +# Since 2.3
>> +##
>> +{ 'struct': 'BlockDirtyBitmapInfo',
>> +  'data': { 'name': 'str',
>> +            'size': 'int',
>> +            'granularity': 'int',
>> +            'disabled': 'bool',
>> +            'dirty-count': 'int',
>> +            'dirty-regions': ['BlockDirtyRegion'] } }
>> +
>> +##
>> +# @query-block-dirty-bitmap
>> +#
>> +# Get a description for specified dirty bitmap including it's dirty 
>> regions.
>> +# This command is in general for testing purposes.
>> +#
>> +# Returns: @BlockDirtyBitmapInfo
>> +#
>> +# Since: 2.3
>> +##
>> +{ 'command': 'query-block-dirty-bitmap',
>> +  'data': 'BlockDirtyBitmap',
>> +  'returns': 'BlockDirtyBitmapInfo' }
>> 1) should we consider part-by-part retrieval? This could be useful 
>> for large discs.
>> 2) Change to since 2.6
>> 3) Do you think that content should be retrieved separately than data?
>
> 3 - what do you mean?
>

sorry, I mean bitmap description separately from bitmap bits aka data
Eric Blake Jan. 22, 2016, 6:43 p.m. UTC | #4
On 01/22/2016 10:07 AM, Vladimir Sementsov-Ogievskiy wrote:
> Add qmp command to query dirty bitmap. This is needed for external
> backup.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block.c               | 41 ++++++++++++++++++++++++++++++++++++++++
>  blockdev.c            | 21 +++++++++++++++++++++
>  include/block/block.h |  2 ++
>  qapi/block-core.json  | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  qmp-commands.hx       | 22 ++++++++++++++++++++++
>  5 files changed, 138 insertions(+)

Just an interface review at this time:

> +++ b/qapi/block-core.json
> @@ -414,6 +414,58 @@
>  ##
>  { 'command': 'query-block', 'returns': ['BlockInfo'] }
>  
> +##
> +# @BlockDirtyRegion:
> +#
> +# Region in bytes.
> +#
> +# @start: first byte
> +#
> +# @count: number of bytes in the region
> +#
> +# Since: 2.3

Don't you mean 2.6?

> +##
> +{ 'struct': 'BlockDirtyRegion',
> +  'data': { 'start': 'int', 'count': 'int' } }
> +
> +##
> +# @BlockDirtyBitmapInfo
> +#
> +# @name: the name of the dirty bitmap
> +#
> +# @size: size of the dirty bitmap in sectors
> +#
> +# @granularity: granularity of the dirty bitmap in bytes
> +#
> +# @disabled: whether the dirty bitmap is disabled
> +#
> +# @dirty-count: number of dirty bytes according to the dirty bitmap
> +#
> +# @dirty-regions: dirty regions of the bitmap
> +#
> +# Since 2.3

and again

> +##
> +{ 'struct': 'BlockDirtyBitmapInfo',
> +  'data': { 'name': 'str',
> +            'size': 'int',
> +            'granularity': 'int',
> +            'disabled': 'bool',
> +            'dirty-count': 'int',
> +            'dirty-regions': ['BlockDirtyRegion'] } }
> +
> +##
> +# @query-block-dirty-bitmap
> +#
> +# Get a description for specified dirty bitmap including it's dirty regions.
> +# This command is in general for testing purposes.
> +#
> +# Returns: @BlockDirtyBitmapInfo
> +#
> +# Since: 2.3

and again

> +##
> +{ 'command': 'query-block-dirty-bitmap',
> +  'data': 'BlockDirtyBitmap',
> +  'returns': 'BlockDirtyBitmapInfo' }

Seems reasonable for getting at the information for one bitmap.  But
would it be smarter to have:

{ 'command': 'query-block-dirty-bitmap',
  'data': { 'node':'str', '*name':'str' },
  'returns': [ 'BlockDirtyBitmapInfo' ] }

so that you could get ALL the dirty bitmaps for a single node (with
optional filtering to get an array of just one, if 'name' was provided)?
 Or, should BlockDirtyBitmapInfo also include a node name, then you
could query all dirty bitmaps for all nodes at once?  Is that too much
data for one QMP command?


> +
> +query-block-dirty-bitmap
> +------------------------
> +Since 2.6
> +
> +Get dirty bitmap info, including contents. Bitmap data are returned as array of
> +dirty regions
> +
> +Arguments:
> +
> +- "node": device/node on which to remove dirty bitmap (json-string)

Too much copy-and-paste; you aren't removing the bitmap.

> +- "name": name of the dirty bitmap to remove (json-string)
> +
> +EQMP

Worth showing example output?
diff mbox

Patch

diff --git a/block.c b/block.c
index 5709d3d..9a28589 100644
--- a/block.c
+++ b/block.c
@@ -3717,6 +3717,47 @@  void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
     }
 }
 
+BlockDirtyBitmapInfo *bdrv_query_dirty_bitmap(BlockDriverState *bs,
+                                              BdrvDirtyBitmap *bitmap)
+{
+    BlockDirtyBitmapInfo *info = g_new0(BlockDirtyBitmapInfo, 1);
+    BlockDirtyRegionList **plist = &info->dirty_regions;
+    uint64_t begin = 0, end = 0, size = bitmap->size;
+    HBitmap *hb = bitmap->bitmap;
+
+    info->dirty_count = bdrv_get_dirty_count(bitmap);
+    info->granularity = bdrv_dirty_bitmap_granularity(bitmap);
+    info->size = bitmap->size;
+    info->name = g_strdup(bitmap->name);
+    info->disabled = bitmap->disabled;
+    info->dirty_regions = NULL;
+
+    for (; begin < size; ++begin) {
+        BlockDirtyRegion *region;
+        BlockDirtyRegionList *entry;
+
+        if (!hbitmap_get(hb, begin)) {
+            continue;
+        }
+
+        for (end = begin + 1; end < size && hbitmap_get(hb, end); ++end) {
+            ;
+        }
+
+        region = g_new0(BlockDirtyRegion, 1);
+        entry = g_new0(BlockDirtyRegionList, 1);
+        region->start = begin;
+        region->count = end - begin;
+        entry->value = region;
+        *plist = entry;
+        plist = &entry->next;
+
+        begin = end;
+    }
+
+    return info;
+}
+
 /**
  * Advance an HBitmapIter to an arbitrary offset.
  */
diff --git a/blockdev.c b/blockdev.c
index 07cfe25..d2bc453 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2734,6 +2734,27 @@  void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
     aio_context_release(aio_context);
 }
 
+BlockDirtyBitmapInfo *qmp_query_block_dirty_bitmap(const char *node,
+                                                   const char *name,
+                                                   Error **errp)
+{
+    AioContext *aio_context;
+    BdrvDirtyBitmap *bitmap;
+    BlockDriverState *bs;
+    BlockDirtyBitmapInfo *ret = NULL;
+
+    bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
+    if (!bitmap) {
+        return NULL;
+    }
+
+    ret = bdrv_query_dirty_bitmap(bs, bitmap);
+
+    aio_context_release(aio_context);
+
+    return ret;
+}
+
 void hmp_drive_del(Monitor *mon, const QDict *qdict)
 {
     const char *id = qdict_get_str(qdict, "id");
diff --git a/include/block/block.h b/include/block/block.h
index 25f36dc..9d6bd33 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -505,6 +505,8 @@  void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
                            int64_t cur_sector, int nr_sectors);
 void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
                              int64_t cur_sector, int nr_sectors);
+BlockDirtyBitmapInfo *bdrv_query_dirty_bitmap(BlockDriverState *bs,
+                                              BdrvDirtyBitmap *bitmap);
 void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
 void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
 int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 0a915ed..12ed759 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -414,6 +414,58 @@ 
 ##
 { 'command': 'query-block', 'returns': ['BlockInfo'] }
 
+##
+# @BlockDirtyRegion:
+#
+# Region in bytes.
+#
+# @start: first byte
+#
+# @count: number of bytes in the region
+#
+# Since: 2.3
+##
+{ 'struct': 'BlockDirtyRegion',
+  'data': { 'start': 'int', 'count': 'int' } }
+
+##
+# @BlockDirtyBitmapInfo
+#
+# @name: the name of the dirty bitmap
+#
+# @size: size of the dirty bitmap in sectors
+#
+# @granularity: granularity of the dirty bitmap in bytes
+#
+# @disabled: whether the dirty bitmap is disabled
+#
+# @dirty-count: number of dirty bytes according to the dirty bitmap
+#
+# @dirty-regions: dirty regions of the bitmap
+#
+# Since 2.3
+##
+{ 'struct': 'BlockDirtyBitmapInfo',
+  'data': { 'name': 'str',
+            'size': 'int',
+            'granularity': 'int',
+            'disabled': 'bool',
+            'dirty-count': 'int',
+            'dirty-regions': ['BlockDirtyRegion'] } }
+
+##
+# @query-block-dirty-bitmap
+#
+# Get a description for specified dirty bitmap including it's dirty regions.
+# This command is in general for testing purposes.
+#
+# Returns: @BlockDirtyBitmapInfo
+#
+# Since: 2.3
+##
+{ 'command': 'query-block-dirty-bitmap',
+  'data': 'BlockDirtyBitmap',
+  'returns': 'BlockDirtyBitmapInfo' }
 
 ##
 # @BlockDeviceTimedStats:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index db072a6..75d9345 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1457,6 +1457,28 @@  Example:
 EQMP
 
     {
+        .name       = "query-block-dirty-bitmap",
+        .args_type  = "node:B,name:s",
+        .mhandler.cmd_new = qmp_marshal_query_block_dirty_bitmap,
+    },
+
+SQMP
+
+query-block-dirty-bitmap
+------------------------
+Since 2.6
+
+Get dirty bitmap info, including contents. Bitmap data are returned as array of
+dirty regions
+
+Arguments:
+
+- "node": device/node on which to remove dirty bitmap (json-string)
+- "name": name of the dirty bitmap to remove (json-string)
+
+EQMP
+
+    {
         .name       = "blockdev-snapshot-sync",
         .args_type  = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?",
         .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_sync,