diff mbox

[2.4,v3,11/19] qmp: add block-dirty-bitmap-clear

Message ID 1426271419-8277-12-git-send-email-jsnow@redhat.com
State New
Headers show

Commit Message

John Snow March 13, 2015, 6:30 p.m. UTC
Add bdrv_clear_dirty_bitmap and a matching QMP command,
qmp_block_dirty_bitmap_clear that enables a user to reset
the bitmap attached to a drive.

This allows us to reset a bitmap in the event of a full
drive backup.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 block.c               |  8 ++++++++
 blockdev.c            | 34 ++++++++++++++++++++++++++++++++++
 include/block/block.h |  1 +
 qapi/block-core.json  | 14 ++++++++++++++
 qmp-commands.hx       | 29 +++++++++++++++++++++++++++++
 5 files changed, 86 insertions(+)

Comments

Max Reitz March 16, 2015, 8:57 p.m. UTC | #1
On 2015-03-13 at 14:30, John Snow wrote:
> Add bdrv_clear_dirty_bitmap and a matching QMP command,
> qmp_block_dirty_bitmap_clear that enables a user to reset
> the bitmap attached to a drive.
>
> This allows us to reset a bitmap in the event of a full
> drive backup.
>
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>   block.c               |  8 ++++++++
>   blockdev.c            | 34 ++++++++++++++++++++++++++++++++++
>   include/block/block.h |  1 +
>   qapi/block-core.json  | 14 ++++++++++++++
>   qmp-commands.hx       | 29 +++++++++++++++++++++++++++++
>   5 files changed, 86 insertions(+)
>
> diff --git a/block.c b/block.c
> index d276fdc..a98ff49 100644
> --- a/block.c
> +++ b/block.c
> @@ -62,6 +62,7 @@
>   struct BdrvDirtyBitmap {
>       HBitmap *bitmap;
>       BdrvDirtyBitmap *successor;
> +    int64_t size;
>       char *name;
>       bool disabled;
>       QLIST_ENTRY(BdrvDirtyBitmap) list;
> @@ -5460,6 +5461,7 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
>       }
>       bitmap = g_new0(BdrvDirtyBitmap, 1);
>       bitmap->bitmap = hbitmap_alloc(bitmap_size, ffs(sector_granularity) - 1);
> +    bitmap->size = bitmap_size;
>       bitmap->name = g_strdup(name);
>       bitmap->disabled = false;
>       QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
> @@ -5662,6 +5664,12 @@ void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
>       hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
>   }
>   
> +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap)
> +{
> +    assert(bdrv_dirty_bitmap_enabled(bitmap));
> +    hbitmap_reset(bitmap->bitmap, 0, bitmap->size);
> +}
> +
>   static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
>                              int nr_sectors)
>   {
> diff --git a/blockdev.c b/blockdev.c
> index 361c7a4..cba9f2c 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -2051,6 +2051,40 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
>       aio_context_release(aio_context);
>   }
>   
> +/**
> + * Completely clear a bitmap, for the purposes of synchronizing a bitmap
> + * immediately after a full backup operation.
> + */
> +void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
> +                                  Error **errp)
> +{
> +    AioContext *aio_context;
> +    BdrvDirtyBitmap *bitmap;
> +    BlockDriverState *bs;
> +
> +    bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
> +    if (!bitmap || !bs) {

Again, you need to release the AIO context here if you're not doing that 
in block_dirty_bitmap_lookup() already.

Max

> +        return;
> +    }
> +
> +    if (bdrv_dirty_bitmap_frozen(bitmap)) {
> +        error_setg(errp,
> +                   "Bitmap '%s' is currently frozen and cannot be modified",
> +                   name);
> +        goto out;
> +    } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
> +        error_setg(errp,
> +                   "Bitmap '%s' is currently disabled and cannot be cleared",
> +                   name);
> +        goto out;
> +    }
> +
> +    bdrv_clear_dirty_bitmap(bitmap);
> +
> + out:
> +    aio_context_release(aio_context);
> +}
> +
>   int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
>   {
>       const char *id = qdict_get_str(qdict, "id");
> diff --git a/include/block/block.h b/include/block/block.h
> index 3084b53..10442e0 100644
> --- a/include/block/block.h
> +++ b/include/block/block.h
> @@ -479,6 +479,7 @@ void bdrv_set_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
>                              int64_t cur_sector, int nr_sectors);
>   void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
>                                int64_t cur_sector, int nr_sectors);
> +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap);
>   void bdrv_dirty_iter_init(BlockDriverState *bs,
>                             BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
>   void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index f012de2..50970c4 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -1023,6 +1023,20 @@
>     'data': 'BlockDirtyBitmap' }
>   
>   ##
> +# @block-dirty-bitmap-clear
> +#
> +# Clear (reset) a dirty bitmap on the device
> +#
> +# Returns: nothing on success
> +#          If @node is not a valid block device, DeviceNotFound
> +#          If @name is not found, GenericError with an explanation
> +#
> +# Since 2.4
> +##
> +{ 'command': 'block-dirty-bitmap-clear',
> +  'data': 'BlockDirtyBitmap' }
> +
> +##
>   # @block_set_io_throttle:
>   #
>   # Change I/O throttle limits for a block drive.
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 72ac72f..db95e89 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -1301,6 +1301,35 @@ Example:
>   EQMP
>   
>       {
> +        .name       = "block-dirty-bitmap-clear",
> +        .args_type  = "node:B,name:s",
> +        .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_clear,
> +    },
> +
> +SQMP
> +
> +block-dirty-bitmap-clear
> +------------------------
> +Since 2.4
> +
> +Reset the dirty bitmap associated with a node so that an incremental backup
> +from this point in time forward will only backup clusters modified after this
> +clear operation.
> +
> +Arguments:
> +
> +- "node": device/node on which to remove dirty bitmap (json-string)
> +- "name": name of the dirty bitmap to remove (json-string)
> +
> +Example:
> +
> +-> { "execute": "block-dirty-bitmap-clear", "arguments": { "node": "drive0",
> +                                                           "name": "bitmap0" } }
> +<- { "return": {} }
> +
> +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_input_blockdev_snapshot_sync,
John Snow March 16, 2015, 9:03 p.m. UTC | #2
On 03/16/2015 04:57 PM, Max Reitz wrote:
> On 2015-03-13 at 14:30, John Snow wrote:
>> Add bdrv_clear_dirty_bitmap and a matching QMP command,
>> qmp_block_dirty_bitmap_clear that enables a user to reset
>> the bitmap attached to a drive.
>>
>> This allows us to reset a bitmap in the event of a full
>> drive backup.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>>   block.c               |  8 ++++++++
>>   blockdev.c            | 34 ++++++++++++++++++++++++++++++++++
>>   include/block/block.h |  1 +
>>   qapi/block-core.json  | 14 ++++++++++++++
>>   qmp-commands.hx       | 29 +++++++++++++++++++++++++++++
>>   5 files changed, 86 insertions(+)
>>
>> diff --git a/block.c b/block.c
>> index d276fdc..a98ff49 100644
>> --- a/block.c
>> +++ b/block.c
>> @@ -62,6 +62,7 @@
>>   struct BdrvDirtyBitmap {
>>       HBitmap *bitmap;
>>       BdrvDirtyBitmap *successor;
>> +    int64_t size;
>>       char *name;
>>       bool disabled;
>>       QLIST_ENTRY(BdrvDirtyBitmap) list;
>> @@ -5460,6 +5461,7 @@ BdrvDirtyBitmap
>> *bdrv_create_dirty_bitmap(BlockDriverState *bs,
>>       }
>>       bitmap = g_new0(BdrvDirtyBitmap, 1);
>>       bitmap->bitmap = hbitmap_alloc(bitmap_size,
>> ffs(sector_granularity) - 1);
>> +    bitmap->size = bitmap_size;
>>       bitmap->name = g_strdup(name);
>>       bitmap->disabled = false;
>>       QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
>> @@ -5662,6 +5664,12 @@ void bdrv_reset_dirty_bitmap(BlockDriverState
>> *bs, BdrvDirtyBitmap *bitmap,
>>       hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
>>   }
>> +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap)
>> +{
>> +    assert(bdrv_dirty_bitmap_enabled(bitmap));
>> +    hbitmap_reset(bitmap->bitmap, 0, bitmap->size);
>> +}
>> +
>>   static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
>>                              int nr_sectors)
>>   {
>> diff --git a/blockdev.c b/blockdev.c
>> index 361c7a4..cba9f2c 100644
>> --- a/blockdev.c
>> +++ b/blockdev.c
>> @@ -2051,6 +2051,40 @@ void qmp_block_dirty_bitmap_remove(const char
>> *node, const char *name,
>>       aio_context_release(aio_context);
>>   }
>> +/**
>> + * Completely clear a bitmap, for the purposes of synchronizing a bitmap
>> + * immediately after a full backup operation.
>> + */
>> +void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
>> +                                  Error **errp)
>> +{
>> +    AioContext *aio_context;
>> +    BdrvDirtyBitmap *bitmap;
>> +    BlockDriverState *bs;
>> +
>> +    bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context,
>> errp);
>> +    if (!bitmap || !bs) {
>
> Again, you need to release the AIO context here if you're not doing that
> in block_dirty_bitmap_lookup() already.
>
> Max
>

OK. Inclined to fix that in lookup() as I should've to begin with and 
leave this patch as-is.

--js

>> +        return;
>> +    }
>> +
>> +    if (bdrv_dirty_bitmap_frozen(bitmap)) {
>> +        error_setg(errp,
>> +                   "Bitmap '%s' is currently frozen and cannot be
>> modified",
>> +                   name);
>> +        goto out;
>> +    } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
>> +        error_setg(errp,
>> +                   "Bitmap '%s' is currently disabled and cannot be
>> cleared",
>> +                   name);
>> +        goto out;
>> +    }
>> +
>> +    bdrv_clear_dirty_bitmap(bitmap);
>> +
>> + out:
>> +    aio_context_release(aio_context);
>> +}
>> +
>>   int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
>>   {
>>       const char *id = qdict_get_str(qdict, "id");
>> diff --git a/include/block/block.h b/include/block/block.h
>> index 3084b53..10442e0 100644
>> --- a/include/block/block.h
>> +++ b/include/block/block.h
>> @@ -479,6 +479,7 @@ void bdrv_set_dirty_bitmap(BlockDriverState *bs,
>> BdrvDirtyBitmap *bitmap,
>>                              int64_t cur_sector, int nr_sectors);
>>   void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap
>> *bitmap,
>>                                int64_t cur_sector, int nr_sectors);
>> +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap);
>>   void bdrv_dirty_iter_init(BlockDriverState *bs,
>>                             BdrvDirtyBitmap *bitmap, struct
>> HBitmapIter *hbi);
>>   void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index f012de2..50970c4 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -1023,6 +1023,20 @@
>>     'data': 'BlockDirtyBitmap' }
>>   ##
>> +# @block-dirty-bitmap-clear
>> +#
>> +# Clear (reset) a dirty bitmap on the device
>> +#
>> +# Returns: nothing on success
>> +#          If @node is not a valid block device, DeviceNotFound
>> +#          If @name is not found, GenericError with an explanation
>> +#
>> +# Since 2.4
>> +##
>> +{ 'command': 'block-dirty-bitmap-clear',
>> +  'data': 'BlockDirtyBitmap' }
>> +
>> +##
>>   # @block_set_io_throttle:
>>   #
>>   # Change I/O throttle limits for a block drive.
>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>> index 72ac72f..db95e89 100644
>> --- a/qmp-commands.hx
>> +++ b/qmp-commands.hx
>> @@ -1301,6 +1301,35 @@ Example:
>>   EQMP
>>       {
>> +        .name       = "block-dirty-bitmap-clear",
>> +        .args_type  = "node:B,name:s",
>> +        .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_clear,
>> +    },
>> +
>> +SQMP
>> +
>> +block-dirty-bitmap-clear
>> +------------------------
>> +Since 2.4
>> +
>> +Reset the dirty bitmap associated with a node so that an incremental
>> backup
>> +from this point in time forward will only backup clusters modified
>> after this
>> +clear operation.
>> +
>> +Arguments:
>> +
>> +- "node": device/node on which to remove dirty bitmap (json-string)
>> +- "name": name of the dirty bitmap to remove (json-string)
>> +
>> +Example:
>> +
>> +-> { "execute": "block-dirty-bitmap-clear", "arguments": { "node":
>> "drive0",
>> +                                                           "name":
>> "bitmap0" } }
>> +<- { "return": {} }
>> +
>> +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_input_blockdev_snapshot_sync,
>
Max Reitz March 17, 2015, 1:11 p.m. UTC | #3
On 2015-03-16 at 17:03, John Snow wrote:
>
>
> On 03/16/2015 04:57 PM, Max Reitz wrote:
>> On 2015-03-13 at 14:30, John Snow wrote:
>>> Add bdrv_clear_dirty_bitmap and a matching QMP command,
>>> qmp_block_dirty_bitmap_clear that enables a user to reset
>>> the bitmap attached to a drive.
>>>
>>> This allows us to reset a bitmap in the event of a full
>>> drive backup.
>>>
>>> Signed-off-by: John Snow <jsnow@redhat.com>
>>> ---
>>>   block.c               |  8 ++++++++
>>>   blockdev.c            | 34 ++++++++++++++++++++++++++++++++++
>>>   include/block/block.h |  1 +
>>>   qapi/block-core.json  | 14 ++++++++++++++
>>>   qmp-commands.hx       | 29 +++++++++++++++++++++++++++++
>>>   5 files changed, 86 insertions(+)
>>>
>>> diff --git a/block.c b/block.c
>>> index d276fdc..a98ff49 100644
>>> --- a/block.c
>>> +++ b/block.c
>>> @@ -62,6 +62,7 @@
>>>   struct BdrvDirtyBitmap {
>>>       HBitmap *bitmap;
>>>       BdrvDirtyBitmap *successor;
>>> +    int64_t size;
>>>       char *name;
>>>       bool disabled;
>>>       QLIST_ENTRY(BdrvDirtyBitmap) list;
>>> @@ -5460,6 +5461,7 @@ BdrvDirtyBitmap
>>> *bdrv_create_dirty_bitmap(BlockDriverState *bs,
>>>       }
>>>       bitmap = g_new0(BdrvDirtyBitmap, 1);
>>>       bitmap->bitmap = hbitmap_alloc(bitmap_size,
>>> ffs(sector_granularity) - 1);
>>> +    bitmap->size = bitmap_size;
>>>       bitmap->name = g_strdup(name);
>>>       bitmap->disabled = false;
>>>       QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
>>> @@ -5662,6 +5664,12 @@ void bdrv_reset_dirty_bitmap(BlockDriverState
>>> *bs, BdrvDirtyBitmap *bitmap,
>>>       hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
>>>   }
>>> +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap)
>>> +{
>>> +    assert(bdrv_dirty_bitmap_enabled(bitmap));
>>> +    hbitmap_reset(bitmap->bitmap, 0, bitmap->size);
>>> +}
>>> +
>>>   static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
>>>                              int nr_sectors)
>>>   {
>>> diff --git a/blockdev.c b/blockdev.c
>>> index 361c7a4..cba9f2c 100644
>>> --- a/blockdev.c
>>> +++ b/blockdev.c
>>> @@ -2051,6 +2051,40 @@ void qmp_block_dirty_bitmap_remove(const char
>>> *node, const char *name,
>>>       aio_context_release(aio_context);
>>>   }
>>> +/**
>>> + * Completely clear a bitmap, for the purposes of synchronizing a 
>>> bitmap
>>> + * immediately after a full backup operation.
>>> + */
>>> +void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
>>> +                                  Error **errp)
>>> +{
>>> +    AioContext *aio_context;
>>> +    BdrvDirtyBitmap *bitmap;
>>> +    BlockDriverState *bs;
>>> +
>>> +    bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context,
>>> errp);
>>> +    if (!bitmap || !bs) {
>>
>> Again, you need to release the AIO context here if you're not doing that
>> in block_dirty_bitmap_lookup() already.
>>
>> Max
>>
>
> OK. Inclined to fix that in lookup() as I should've to begin with and 
> leave this patch as-is.

With that:

Reviewed-by: Max Reitz <mreitz@redhat.com>

>
> --js
>
>>> +        return;
>>> +    }
>>> +
>>> +    if (bdrv_dirty_bitmap_frozen(bitmap)) {
>>> +        error_setg(errp,
>>> +                   "Bitmap '%s' is currently frozen and cannot be
>>> modified",
>>> +                   name);
>>> +        goto out;
>>> +    } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
>>> +        error_setg(errp,
>>> +                   "Bitmap '%s' is currently disabled and cannot be
>>> cleared",
>>> +                   name);
>>> +        goto out;
>>> +    }
>>> +
>>> +    bdrv_clear_dirty_bitmap(bitmap);
>>> +
>>> + out:
>>> +    aio_context_release(aio_context);
>>> +}
>>> +
>>>   int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject 
>>> **ret_data)
>>>   {
>>>       const char *id = qdict_get_str(qdict, "id");
>>> diff --git a/include/block/block.h b/include/block/block.h
>>> index 3084b53..10442e0 100644
>>> --- a/include/block/block.h
>>> +++ b/include/block/block.h
>>> @@ -479,6 +479,7 @@ void bdrv_set_dirty_bitmap(BlockDriverState *bs,
>>> BdrvDirtyBitmap *bitmap,
>>>                              int64_t cur_sector, int nr_sectors);
>>>   void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap
>>> *bitmap,
>>>                                int64_t cur_sector, int nr_sectors);
>>> +void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap);
>>>   void bdrv_dirty_iter_init(BlockDriverState *bs,
>>>                             BdrvDirtyBitmap *bitmap, struct
>>> HBitmapIter *hbi);
>>>   void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
>>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>>> index f012de2..50970c4 100644
>>> --- a/qapi/block-core.json
>>> +++ b/qapi/block-core.json
>>> @@ -1023,6 +1023,20 @@
>>>     'data': 'BlockDirtyBitmap' }
>>>   ##
>>> +# @block-dirty-bitmap-clear
>>> +#
>>> +# Clear (reset) a dirty bitmap on the device
>>> +#
>>> +# Returns: nothing on success
>>> +#          If @node is not a valid block device, DeviceNotFound
>>> +#          If @name is not found, GenericError with an explanation
>>> +#
>>> +# Since 2.4
>>> +##
>>> +{ 'command': 'block-dirty-bitmap-clear',
>>> +  'data': 'BlockDirtyBitmap' }
>>> +
>>> +##
>>>   # @block_set_io_throttle:
>>>   #
>>>   # Change I/O throttle limits for a block drive.
>>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>>> index 72ac72f..db95e89 100644
>>> --- a/qmp-commands.hx
>>> +++ b/qmp-commands.hx
>>> @@ -1301,6 +1301,35 @@ Example:
>>>   EQMP
>>>       {
>>> +        .name       = "block-dirty-bitmap-clear",
>>> +        .args_type  = "node:B,name:s",
>>> +        .mhandler.cmd_new = 
>>> qmp_marshal_input_block_dirty_bitmap_clear,
>>> +    },
>>> +
>>> +SQMP
>>> +
>>> +block-dirty-bitmap-clear
>>> +------------------------
>>> +Since 2.4
>>> +
>>> +Reset the dirty bitmap associated with a node so that an incremental
>>> backup
>>> +from this point in time forward will only backup clusters modified
>>> after this
>>> +clear operation.
>>> +
>>> +Arguments:
>>> +
>>> +- "node": device/node on which to remove dirty bitmap (json-string)
>>> +- "name": name of the dirty bitmap to remove (json-string)
>>> +
>>> +Example:
>>> +
>>> +-> { "execute": "block-dirty-bitmap-clear", "arguments": { "node":
>>> "drive0",
>>> + "name":
>>> "bitmap0" } }
>>> +<- { "return": {} }
>>> +
>>> +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_input_blockdev_snapshot_sync,
>>
diff mbox

Patch

diff --git a/block.c b/block.c
index d276fdc..a98ff49 100644
--- a/block.c
+++ b/block.c
@@ -62,6 +62,7 @@ 
 struct BdrvDirtyBitmap {
     HBitmap *bitmap;
     BdrvDirtyBitmap *successor;
+    int64_t size;
     char *name;
     bool disabled;
     QLIST_ENTRY(BdrvDirtyBitmap) list;
@@ -5460,6 +5461,7 @@  BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
     }
     bitmap = g_new0(BdrvDirtyBitmap, 1);
     bitmap->bitmap = hbitmap_alloc(bitmap_size, ffs(sector_granularity) - 1);
+    bitmap->size = bitmap_size;
     bitmap->name = g_strdup(name);
     bitmap->disabled = false;
     QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list);
@@ -5662,6 +5664,12 @@  void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
     hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
 }
 
+void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap)
+{
+    assert(bdrv_dirty_bitmap_enabled(bitmap));
+    hbitmap_reset(bitmap->bitmap, 0, bitmap->size);
+}
+
 static void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
                            int nr_sectors)
 {
diff --git a/blockdev.c b/blockdev.c
index 361c7a4..cba9f2c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2051,6 +2051,40 @@  void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
     aio_context_release(aio_context);
 }
 
+/**
+ * Completely clear a bitmap, for the purposes of synchronizing a bitmap
+ * immediately after a full backup operation.
+ */
+void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
+                                  Error **errp)
+{
+    AioContext *aio_context;
+    BdrvDirtyBitmap *bitmap;
+    BlockDriverState *bs;
+
+    bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp);
+    if (!bitmap || !bs) {
+        return;
+    }
+
+    if (bdrv_dirty_bitmap_frozen(bitmap)) {
+        error_setg(errp,
+                   "Bitmap '%s' is currently frozen and cannot be modified",
+                   name);
+        goto out;
+    } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
+        error_setg(errp,
+                   "Bitmap '%s' is currently disabled and cannot be cleared",
+                   name);
+        goto out;
+    }
+
+    bdrv_clear_dirty_bitmap(bitmap);
+
+ out:
+    aio_context_release(aio_context);
+}
+
 int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
     const char *id = qdict_get_str(qdict, "id");
diff --git a/include/block/block.h b/include/block/block.h
index 3084b53..10442e0 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -479,6 +479,7 @@  void bdrv_set_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
                            int64_t cur_sector, int nr_sectors);
 void bdrv_reset_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
                              int64_t cur_sector, int nr_sectors);
+void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap);
 void bdrv_dirty_iter_init(BlockDriverState *bs,
                           BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi);
 void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset);
diff --git a/qapi/block-core.json b/qapi/block-core.json
index f012de2..50970c4 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1023,6 +1023,20 @@ 
   'data': 'BlockDirtyBitmap' }
 
 ##
+# @block-dirty-bitmap-clear
+#
+# Clear (reset) a dirty bitmap on the device
+#
+# Returns: nothing on success
+#          If @node is not a valid block device, DeviceNotFound
+#          If @name is not found, GenericError with an explanation
+#
+# Since 2.4
+##
+{ 'command': 'block-dirty-bitmap-clear',
+  'data': 'BlockDirtyBitmap' }
+
+##
 # @block_set_io_throttle:
 #
 # Change I/O throttle limits for a block drive.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 72ac72f..db95e89 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1301,6 +1301,35 @@  Example:
 EQMP
 
     {
+        .name       = "block-dirty-bitmap-clear",
+        .args_type  = "node:B,name:s",
+        .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_clear,
+    },
+
+SQMP
+
+block-dirty-bitmap-clear
+------------------------
+Since 2.4
+
+Reset the dirty bitmap associated with a node so that an incremental backup
+from this point in time forward will only backup clusters modified after this
+clear operation.
+
+Arguments:
+
+- "node": device/node on which to remove dirty bitmap (json-string)
+- "name": name of the dirty bitmap to remove (json-string)
+
+Example:
+
+-> { "execute": "block-dirty-bitmap-clear", "arguments": { "node": "drive0",
+                                                           "name": "bitmap0" } }
+<- { "return": {} }
+
+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_input_blockdev_snapshot_sync,