diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-05-21 11:11:10 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-05-21 11:11:10 +0100 |
commit | 8b9112e4aef0651feff82e49d1e58947cceab66a (patch) | |
tree | a742938cb03ca2ea67c322ab74280f1d60269a9d | |
parent | 956ae3e9265fd36cb1c487cb3c868e906bd55897 (diff) | |
parent | 3b51ab4bf0f49a01cc2db7b954e0669e081719b5 (diff) |
Merge remote-tracking branch 'remotes/ericb/tags/pull-bitmaps-2020-05-18-v3' into staging
bitmaps patches for 2020-05-18
- update bitmaps maintainers
- add 'qemu-img bitmap' subcommand
# gpg: Signature made Tue 19 May 2020 18:53:59 BST
# gpg: using RSA key 71C2CC22B1C4602927D2F3AAA7A16B4A2527436A
# gpg: Good signature from "Eric Blake <eblake@redhat.com>" [full]
# gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" [full]
# gpg: aka "[jpeg image of size 6874]" [full]
# Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A
* remotes/ericb/tags/pull-bitmaps-2020-05-18-v3:
qemu-img: Add bitmap sub-command
blockdev: Split off basic bitmap operations for qemu-img
blockdev: Promote several bitmap functions to non-static
block: Make it easier to learn which BDS support bitmaps
qemu-img: Fix stale comments on doc location
docs: Sort sections on qemu-img subcommand parameters
bitmaps: Update maintainer
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | Makefile.objs | 3 | ||||
-rw-r--r-- | block/dirty-bitmap.c | 9 | ||||
-rw-r--r-- | block/monitor/Makefile.objs | 1 | ||||
-rw-r--r-- | block/monitor/bitmap-qmp-cmds.c | 321 | ||||
-rw-r--r-- | block/qcow2-bitmap.c | 7 | ||||
-rw-r--r-- | block/qcow2.c | 2 | ||||
-rw-r--r-- | block/qcow2.h | 1 | ||||
-rw-r--r-- | blockdev.c | 303 | ||||
-rw-r--r-- | docs/tools/qemu-img.rst | 72 | ||||
-rw-r--r-- | include/block/block_int.h | 13 | ||||
-rw-r--r-- | include/block/dirty-bitmap.h | 1 | ||||
-rw-r--r-- | qemu-img-cmds.hx | 9 | ||||
-rw-r--r-- | qemu-img.c | 250 |
14 files changed, 671 insertions, 329 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 47ef3139e6..87a412c229 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2011,19 +2011,21 @@ F: qapi/transaction.json T: git https://repo.or.cz/qemu/armbru.git block-next Dirty Bitmaps -M: John Snow <jsnow@redhat.com> -R: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> +M: Eric Blake <eblake@redhat.com> +M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> +R: John Snow <jsnow@redhat.com> L: qemu-block@nongnu.org S: Supported F: include/qemu/hbitmap.h F: include/block/dirty-bitmap.h +F: block/monitor/bitmap-qmp-cmds.c F: block/dirty-bitmap.c F: block/qcow2-bitmap.c F: migration/block-dirty-bitmap.c F: util/hbitmap.c F: tests/test-hbitmap.c F: docs/interop/bitmaps.rst -T: git https://github.com/jnsnow/qemu.git bitmaps +T: git https://repo.or.cz/qemu/ericb.git bitmaps Character device backends M: Marc-André Lureau <marcandre.lureau@redhat.com> diff --git a/Makefile.objs b/Makefile.objs index a7c967633a..99774cfd25 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -13,9 +13,8 @@ chardev-obj-y = chardev/ authz-obj-y = authz/ -block-obj-y = nbd/ +block-obj-y = block/ block/monitor/ nbd/ scsi/ block-obj-y += block.o blockjob.o job.o -block-obj-y += block/ scsi/ block-obj-y += qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) += replication.o diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 063793e316..f9bfc77985 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -478,6 +478,15 @@ int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, } } +bool +bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs) +{ + if (bs->drv && bs->drv->bdrv_supports_persistent_dirty_bitmap) { + return bs->drv->bdrv_supports_persistent_dirty_bitmap(bs); + } + return false; +} + static bool coroutine_fn bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, uint32_t granularity, Error **errp) diff --git a/block/monitor/Makefile.objs b/block/monitor/Makefile.objs index 0a74f9a8b5..39acf85022 100644 --- a/block/monitor/Makefile.objs +++ b/block/monitor/Makefile.objs @@ -1 +1,2 @@ common-obj-y += block-hmp-cmds.o +block-obj-y += bitmap-qmp-cmds.o diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c new file mode 100644 index 0000000000..9f11deec64 --- /dev/null +++ b/block/monitor/bitmap-qmp-cmds.c @@ -0,0 +1,321 @@ +/* + * QEMU block dirty bitmap QMP commands + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "block/block_int.h" +#include "qapi/qapi-commands-block.h" +#include "qapi/error.h" + +/** + * block_dirty_bitmap_lookup: + * Return a dirty bitmap (if present), after validating + * the node reference and bitmap names. + * + * @node: The name of the BDS node to search for bitmaps + * @name: The name of the bitmap to search for + * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. + * @errp: Output pointer for error information. Can be NULL. + * + * @return: A bitmap object on success, or NULL on failure. + */ +BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, + const char *name, + BlockDriverState **pbs, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + if (!node) { + error_setg(errp, "Node cannot be NULL"); + return NULL; + } + if (!name) { + error_setg(errp, "Bitmap name cannot be NULL"); + return NULL; + } + bs = bdrv_lookup_bs(node, node, NULL); + if (!bs) { + error_setg(errp, "Node '%s' not found", node); + return NULL; + } + + bitmap = bdrv_find_dirty_bitmap(bs, name); + if (!bitmap) { + error_setg(errp, "Dirty bitmap '%s' not found", name); + return NULL; + } + + if (pbs) { + *pbs = bs; + } + + return bitmap; +} + +void qmp_block_dirty_bitmap_add(const char *node, const char *name, + bool has_granularity, uint32_t granularity, + bool has_persistent, bool persistent, + bool has_disabled, bool disabled, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + AioContext *aio_context; + + if (!name || name[0] == '\0') { + error_setg(errp, "Bitmap name cannot be empty"); + return; + } + + bs = bdrv_lookup_bs(node, node, errp); + if (!bs) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (has_granularity) { + if (granularity < 512 || !is_power_of_2(granularity)) { + error_setg(errp, "Granularity must be power of 2 " + "and at least 512"); + goto out; + } + } else { + /* Default to cluster size, if available: */ + granularity = bdrv_get_default_bitmap_granularity(bs); + } + + if (!has_persistent) { + persistent = false; + } + + if (!has_disabled) { + disabled = false; + } + + if (persistent && + !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) + { + goto out; + } + + bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); + if (bitmap == NULL) { + goto out; + } + + if (disabled) { + bdrv_disable_dirty_bitmap(bitmap); + } + + bdrv_dirty_bitmap_set_persistence(bitmap, persistent); + +out: + aio_context_release(aio_context); +} + +BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, + bool release, + BlockDriverState **bitmap_bs, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + AioContext *aio_context; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap || !bs) { + return NULL; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, + errp)) { + aio_context_release(aio_context); + return NULL; + } + + if (bdrv_dirty_bitmap_get_persistence(bitmap) && + bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) + { + aio_context_release(aio_context); + return NULL; + } + + if (release) { + bdrv_release_dirty_bitmap(bitmap); + } + + if (bitmap_bs) { + *bitmap_bs = bs; + } + + aio_context_release(aio_context); + return release ? NULL : bitmap; +} + +void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + Error **errp) +{ + block_dirty_bitmap_remove(node, name, true, NULL, errp); +} + +/** + * 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) +{ + BdrvDirtyBitmap *bitmap; + BlockDriverState *bs; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap || !bs) { + return; + } + + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { + return; + } + + bdrv_clear_dirty_bitmap(bitmap, NULL); +} + +void qmp_block_dirty_bitmap_enable(const char *node, const char *name, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap) { + return; + } + + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + + bdrv_enable_dirty_bitmap(bitmap); +} + +void qmp_block_dirty_bitmap_disable(const char *node, const char *name, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap) { + return; + } + + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + + bdrv_disable_dirty_bitmap(bitmap); +} + +BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, + BlockDirtyBitmapMergeSourceList *bms, + HBitmap **backup, Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *dst, *src, *anon; + BlockDirtyBitmapMergeSourceList *lst; + Error *local_err = NULL; + + dst = block_dirty_bitmap_lookup(node, target, &bs, errp); + if (!dst) { + return NULL; + } + + anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst), + NULL, errp); + if (!anon) { + return NULL; + } + + for (lst = bms; lst; lst = lst->next) { + switch (lst->value->type) { + const char *name, *node; + case QTYPE_QSTRING: + name = lst->value->u.local; + src = bdrv_find_dirty_bitmap(bs, name); + if (!src) { + error_setg(errp, "Dirty bitmap '%s' not found", name); + dst = NULL; + goto out; + } + 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) { + 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; + } + } + + /* Merge into dst; dst is unchanged on failure. */ + bdrv_merge_dirty_bitmap(dst, anon, backup, errp); + + out: + bdrv_release_dirty_bitmap(anon); + return dst; +} + +void qmp_block_dirty_bitmap_merge(const char *node, const char *target, + BlockDirtyBitmapMergeSourceList *bitmaps, + Error **errp) +{ + block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); +} diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index cb06954b4a..1cf6d2ab77 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1748,3 +1748,10 @@ fail: name, bdrv_get_device_or_node_name(bs)); return false; } + +bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + + return s->qcow_version >= 3; +} diff --git a/block/qcow2.c b/block/qcow2.c index fe0ce39799..dfab8d2f6c 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5791,6 +5791,8 @@ BlockDriver bdrv_qcow2 = { .bdrv_detach_aio_context = qcow2_detach_aio_context, .bdrv_attach_aio_context = qcow2_attach_aio_context, + .bdrv_supports_persistent_dirty_bitmap = + qcow2_supports_persistent_dirty_bitmap, .bdrv_co_can_store_new_dirty_bitmap = qcow2_co_can_store_new_dirty_bitmap, .bdrv_co_remove_persistent_dirty_bitmap = qcow2_co_remove_persistent_dirty_bitmap, diff --git a/block/qcow2.h b/block/qcow2.h index 6a8b82e6cc..402e8acb1c 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -782,6 +782,7 @@ bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, int qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, Error **errp); +bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs); ssize_t coroutine_fn qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, diff --git a/blockdev.c b/blockdev.c index b3c840ec03..72df193ca7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1185,53 +1185,6 @@ out_aio_context: return NULL; } -/** - * block_dirty_bitmap_lookup: - * Return a dirty bitmap (if present), after validating - * the node reference and bitmap names. - * - * @node: The name of the BDS node to search for bitmaps - * @name: The name of the bitmap to search for - * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. - * @errp: Output pointer for error information. Can be NULL. - * - * @return: A bitmap object on success, or NULL on failure. - */ -static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, - const char *name, - BlockDriverState **pbs, - Error **errp) -{ - BlockDriverState *bs; - BdrvDirtyBitmap *bitmap; - - if (!node) { - error_setg(errp, "Node cannot be NULL"); - return NULL; - } - if (!name) { - error_setg(errp, "Bitmap name cannot be NULL"); - return NULL; - } - bs = bdrv_lookup_bs(node, node, NULL); - if (!bs) { - error_setg(errp, "Node '%s' not found", node); - return NULL; - } - - bitmap = bdrv_find_dirty_bitmap(bs, name); - if (!bitmap) { - error_setg(errp, "Dirty bitmap '%s' not found", name); - return NULL; - } - - if (pbs) { - *pbs = bs; - } - - return bitmap; -} - /* New and old BlockDriverState structs for atomic group operations */ typedef struct BlkActionState BlkActionState; @@ -2171,11 +2124,6 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common) } } -static BdrvDirtyBitmap *do_block_dirty_bitmap_merge( - const char *node, const char *target, - BlockDirtyBitmapMergeSourceList *bitmaps, - HBitmap **backup, Error **errp); - static void block_dirty_bitmap_merge_prepare(BlkActionState *common, Error **errp) { @@ -2189,15 +2137,11 @@ static void block_dirty_bitmap_merge_prepare(BlkActionState *common, action = common->action->u.block_dirty_bitmap_merge.data; - state->bitmap = do_block_dirty_bitmap_merge(action->node, action->target, - action->bitmaps, &state->backup, - errp); + state->bitmap = block_dirty_bitmap_merge(action->node, action->target, + action->bitmaps, &state->backup, + errp); } -static BdrvDirtyBitmap *do_block_dirty_bitmap_remove( - const char *node, const char *name, bool release, - BlockDriverState **bitmap_bs, Error **errp); - static void block_dirty_bitmap_remove_prepare(BlkActionState *common, Error **errp) { @@ -2211,8 +2155,8 @@ static void block_dirty_bitmap_remove_prepare(BlkActionState *common, action = common->action->u.block_dirty_bitmap_remove.data; - state->bitmap = do_block_dirty_bitmap_remove(action->node, action->name, - false, &state->bs, errp); + state->bitmap = block_dirty_bitmap_remove(action->node, action->name, + false, &state->bs, errp); if (state->bitmap) { bdrv_dirty_bitmap_skip_store(state->bitmap, true); bdrv_dirty_bitmap_set_busy(state->bitmap, true); @@ -2441,243 +2385,6 @@ void qmp_block_passwd(bool has_device, const char *device, "Setting block passwords directly is no longer supported"); } -void qmp_block_dirty_bitmap_add(const char *node, const char *name, - bool has_granularity, uint32_t granularity, - bool has_persistent, bool persistent, - bool has_disabled, bool disabled, - Error **errp) -{ - BlockDriverState *bs; - BdrvDirtyBitmap *bitmap; - AioContext *aio_context; - - if (!name || name[0] == '\0') { - error_setg(errp, "Bitmap name cannot be empty"); - return; - } - - bs = bdrv_lookup_bs(node, node, errp); - if (!bs) { - return; - } - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - if (has_granularity) { - if (granularity < 512 || !is_power_of_2(granularity)) { - error_setg(errp, "Granularity must be power of 2 " - "and at least 512"); - goto out; - } - } else { - /* Default to cluster size, if available: */ - granularity = bdrv_get_default_bitmap_granularity(bs); - } - - if (!has_persistent) { - persistent = false; - } - - if (!has_disabled) { - disabled = false; - } - - if (persistent && - !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) - { - goto out; - } - - bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); - if (bitmap == NULL) { - goto out; - } - - if (disabled) { - bdrv_disable_dirty_bitmap(bitmap); - } - - bdrv_dirty_bitmap_set_persistence(bitmap, persistent); - -out: - aio_context_release(aio_context); -} - -static BdrvDirtyBitmap *do_block_dirty_bitmap_remove( - const char *node, const char *name, bool release, - BlockDriverState **bitmap_bs, Error **errp) -{ - BlockDriverState *bs; - BdrvDirtyBitmap *bitmap; - AioContext *aio_context; - - bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); - if (!bitmap || !bs) { - return NULL; - } - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, - errp)) { - aio_context_release(aio_context); - return NULL; - } - - if (bdrv_dirty_bitmap_get_persistence(bitmap) && - bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) - { - aio_context_release(aio_context); - return NULL; - } - - if (release) { - bdrv_release_dirty_bitmap(bitmap); - } - - if (bitmap_bs) { - *bitmap_bs = bs; - } - - aio_context_release(aio_context); - return release ? NULL : bitmap; -} - -void qmp_block_dirty_bitmap_remove(const char *node, const char *name, - Error **errp) -{ - do_block_dirty_bitmap_remove(node, name, true, NULL, errp); -} - -/** - * 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) -{ - BdrvDirtyBitmap *bitmap; - BlockDriverState *bs; - - bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); - if (!bitmap || !bs) { - return; - } - - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { - return; - } - - bdrv_clear_dirty_bitmap(bitmap, NULL); -} - -void qmp_block_dirty_bitmap_enable(const char *node, const char *name, - Error **errp) -{ - BlockDriverState *bs; - BdrvDirtyBitmap *bitmap; - - bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); - if (!bitmap) { - return; - } - - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { - return; - } - - bdrv_enable_dirty_bitmap(bitmap); -} - -void qmp_block_dirty_bitmap_disable(const char *node, const char *name, - Error **errp) -{ - BlockDriverState *bs; - BdrvDirtyBitmap *bitmap; - - bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); - if (!bitmap) { - return; - } - - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { - return; - } - - bdrv_disable_dirty_bitmap(bitmap); -} - -static BdrvDirtyBitmap *do_block_dirty_bitmap_merge( - const char *node, const char *target, - BlockDirtyBitmapMergeSourceList *bitmaps, - HBitmap **backup, Error **errp) -{ - BlockDriverState *bs; - BdrvDirtyBitmap *dst, *src, *anon; - BlockDirtyBitmapMergeSourceList *lst; - Error *local_err = NULL; - - dst = block_dirty_bitmap_lookup(node, target, &bs, errp); - if (!dst) { - return NULL; - } - - anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst), - NULL, errp); - if (!anon) { - return NULL; - } - - for (lst = bitmaps; lst; lst = lst->next) { - switch (lst->value->type) { - const char *name, *node; - case QTYPE_QSTRING: - name = lst->value->u.local; - src = bdrv_find_dirty_bitmap(bs, name); - if (!src) { - error_setg(errp, "Dirty bitmap '%s' not found", name); - dst = NULL; - goto out; - } - 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) { - 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; - } - } - - /* Merge into dst; dst is unchanged on failure. */ - bdrv_merge_dirty_bitmap(dst, anon, backup, errp); - - out: - bdrv_release_dirty_bitmap(anon); - return dst; -} - -void qmp_block_dirty_bitmap_merge(const char *node, const char *target, - BlockDirtyBitmapMergeSourceList *bitmaps, - Error **errp) -{ - do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); -} - BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index f4ffe528ea..38d464ea3f 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -142,30 +142,6 @@ by the used format or see the format descriptions below for details. the documentation of the emulator's ``-drive cache=...`` option for allowed values. -Parameters to snapshot subcommand: - -.. program:: qemu-img-snapshot - -.. option:: snapshot - - Is the name of the snapshot to create, apply or delete - -.. option:: -a - - Applies a snapshot (revert disk to saved state) - -.. option:: -c - - Creates a snapshot - -.. option:: -d - - Deletes a snapshot - -.. option:: -l - - Lists all snapshots in the given image - Parameters to compare subcommand: .. program:: qemu-img-compare @@ -245,6 +221,30 @@ Parameters to dd subcommand: Sets the number of input blocks to skip +Parameters to snapshot subcommand: + +.. program:: qemu-img-snapshot + +.. option:: snapshot + + Is the name of the snapshot to create, apply or delete + +.. option:: -a + + Applies a snapshot (revert disk to saved state) + +.. option:: -c + + Creates a snapshot + +.. option:: -d + + Deletes a snapshot + +.. option:: -l + + Lists all snapshots in the given image + Command description: .. program:: qemu-img-commands @@ -281,6 +281,30 @@ Command description: For write tests, by default a buffer filled with zeros is written. This can be overridden with a pattern byte specified by *PATTERN*. +.. option:: bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b SOURCE_FILE [-F SOURCE_FMT]] [-g GRANULARITY] [--object OBJECTDEF] [--image-opts | -f FMT] FILENAME BITMAP + + Perform one or more modifications of the persistent bitmap *BITMAP* + in the disk image *FILENAME*. The various modifications are: + + ``--add`` to create *BITMAP*, enabled to record future edits. + + ``--remove`` to remove *BITMAP*. + + ``--clear`` to clear *BITMAP*. + + ``--enable`` to change *BITMAP* to start recording future edits. + + ``--disable`` to change *BITMAP* to stop recording future edits. + + ``--merge`` to merge the contents of *SOURCE_BITMAP* into *BITMAP*. + + Additional options include ``-g`` which sets a non-default + *GRANULARITY* for ``--add``, and ``-b`` and ``-F`` which select an + alternative source file for all *SOURCE* bitmaps used by + ``--merge``. + + To see what bitmaps are present in an image, use ``qemu-img info``. + .. option:: check [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [--output=OFMT] [-r [leaks | all]] [-T SRC_CACHE] [-U] FILENAME Perform a consistency check on the disk image *FILENAME*. The command can diff --git a/include/block/block_int.h b/include/block/block_int.h index 5e4f4c348c..791de6a59c 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -568,6 +568,7 @@ struct BlockDriver { uint64_t parent_perm, uint64_t parent_shared, uint64_t *nperm, uint64_t *nshared); + bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs); bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs, const char *name, uint32_t granularity, @@ -1343,4 +1344,16 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, Error **errp); extern QemuOptsList bdrv_create_opts_simple; +BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, + const char *name, + BlockDriverState **pbs, + Error **errp); +BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, + BlockDirtyBitmapMergeSourceList *bms, + HBitmap **backup, Error **errp); +BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, + bool release, + BlockDriverState **bitmap_bs, + Error **errp); + #endif /* BLOCK_INT_H */ diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 8a10029418..5a8d52e4de 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -16,6 +16,7 @@ typedef enum BitmapCheckFlags { #define BDRV_BITMAP_MAX_NAME_SIZE 1023 +bool bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs); BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, const char *name, diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 35f832816f..a87d3cb264 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -7,7 +7,7 @@ HXCOMM command structures and help message. HXCOMM HXCOMM can be used for comments, discarded from both rST and C HXCOMM When amending the rST sections, please remember to copy the usage -HXCOMM over to the per-command sections in qemu-img.texi. +HXCOMM over to the per-command sections in docs/tools/qemu-img.rst. DEF("amend", img_amend, "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename") @@ -20,6 +20,13 @@ DEF("bench", img_bench, SRST .. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-i AIO] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME ERST + +DEF("bitmap", img_bitmap, + "bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b source_file [-F source_fmt]] [-g granularity] [--object objectdef] [--image-opts | -f fmt] filename bitmap") +SRST +.. option:: bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b SOURCE_FILE [-F SOURCE_FMT]] [-g GRANULARITY] [--object OBJECTDEF] [--image-opts | -f FMT] FILENAME BITMAP +ERST + DEF("check", img_check, "check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") SRST diff --git a/qemu-img.c b/qemu-img.c index 2a9186139d..2d30682f12 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -28,6 +28,7 @@ #include "qemu-common.h" #include "qemu-version.h" #include "qapi/error.h" +#include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qmp/qjson.h" @@ -71,6 +72,12 @@ enum { OPTION_SHRINK = 266, OPTION_SALVAGE = 267, OPTION_TARGET_IS_ZERO = 268, + OPTION_ADD = 269, + OPTION_REMOVE = 270, + OPTION_CLEAR = 271, + OPTION_ENABLE = 272, + OPTION_DISABLE = 273, + OPTION_MERGE = 274, }; typedef enum OutputFormat { @@ -108,7 +115,7 @@ static void QEMU_NORETURN unrecognized_option(const char *option) error_exit("unrecognized option '%s'", option); } -/* Please keep in synch with qemu-img.texi */ +/* Please keep in synch with docs/tools/qemu-img.rst */ static void QEMU_NORETURN help(void) { const char *help_msg = @@ -169,6 +176,14 @@ static void QEMU_NORETURN help(void) " '-n' skips the target volume creation (useful if the volume is created\n" " prior to running qemu-img)\n" "\n" + "Parameters to bitmap subcommand:\n" + " 'bitmap' is the name of the bitmap to manipulate, through one or more\n" + " actions from '--add', '--remove', '--clear', '--enable', '--disable',\n" + " or '--merge source'\n" + " '-g granularity' sets the granularity for '--add' actions\n" + " '-b source' and '-F src_fmt' tell '--merge' actions to find the source\n" + " bitmaps from an alternative file\n" + "\n" "Parameters to check subcommand:\n" " '-r' tries to repair any inconsistencies that are found during the check.\n" " '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n" @@ -4502,6 +4517,239 @@ out: return 0; } +enum ImgBitmapAct { + BITMAP_ADD, + BITMAP_REMOVE, + BITMAP_CLEAR, + BITMAP_ENABLE, + BITMAP_DISABLE, + BITMAP_MERGE, +}; +typedef struct ImgBitmapAction { + enum ImgBitmapAct act; + const char *src; /* only used for merge */ + QSIMPLEQ_ENTRY(ImgBitmapAction) next; +} ImgBitmapAction; + +static int img_bitmap(int argc, char **argv) +{ + Error *err = NULL; + int c, ret = 1; + QemuOpts *opts = NULL; + const char *fmt = NULL, *src_fmt = NULL, *src_filename = NULL; + const char *filename, *bitmap; + BlockBackend *blk = NULL, *src = NULL; + BlockDriverState *bs = NULL, *src_bs = NULL; + bool image_opts = false; + int64_t granularity = 0; + bool add = false, merge = false; + QSIMPLEQ_HEAD(, ImgBitmapAction) actions; + ImgBitmapAction *act, *act_next; + const char *op; + + QSIMPLEQ_INIT(&actions); + + for (;;) { + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"add", no_argument, 0, OPTION_ADD}, + {"remove", no_argument, 0, OPTION_REMOVE}, + {"clear", no_argument, 0, OPTION_CLEAR}, + {"enable", no_argument, 0, OPTION_ENABLE}, + {"disable", no_argument, 0, OPTION_DISABLE}, + {"merge", required_argument, 0, OPTION_MERGE}, + {"granularity", required_argument, 0, 'g'}, + {"source-file", required_argument, 0, 'b'}, + {"source-format", required_argument, 0, 'F'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, ":b:f:F:g:h", long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case ':': + missing_argument(argv[optind - 1]); + break; + case '?': + unrecognized_option(argv[optind - 1]); + break; + case 'h': + help(); + break; + case 'b': + src_filename = optarg; + break; + case 'f': + fmt = optarg; + break; + case 'F': + src_fmt = optarg; + break; + case 'g': + granularity = cvtnum("granularity", optarg); + if (granularity < 0) { + return 1; + } + break; + case OPTION_ADD: + act = g_new0(ImgBitmapAction, 1); + act->act = BITMAP_ADD; + QSIMPLEQ_INSERT_TAIL(&actions, act, next); + add = true; + break; + case OPTION_REMOVE: + act = g_new0(ImgBitmapAction, 1); + act->act = BITMAP_REMOVE; + QSIMPLEQ_INSERT_TAIL(&actions, act, next); + break; + case OPTION_CLEAR: + act = g_new0(ImgBitmapAction, 1); + act->act = BITMAP_CLEAR; + QSIMPLEQ_INSERT_TAIL(&actions, act, next); + break; + case OPTION_ENABLE: + act = g_new0(ImgBitmapAction, 1); + act->act = BITMAP_ENABLE; + QSIMPLEQ_INSERT_TAIL(&actions, act, next); + break; + case OPTION_DISABLE: + act = g_new0(ImgBitmapAction, 1); + act->act = BITMAP_DISABLE; + QSIMPLEQ_INSERT_TAIL(&actions, act, next); + break; + case OPTION_MERGE: + act = g_new0(ImgBitmapAction, 1); + act->act = BITMAP_MERGE; + act->src = optarg; + QSIMPLEQ_INSERT_TAIL(&actions, act, next); + merge = true; + break; + case OPTION_OBJECT: + opts = qemu_opts_parse_noisily(&qemu_object_opts, optarg, true); + if (!opts) { + goto out; + } + break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; + } + } + + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + qemu_img_object_print_help, &error_fatal)) { + goto out; + } + + if (QSIMPLEQ_EMPTY(&actions)) { + error_report("Need at least one of --add, --remove, --clear, " + "--enable, --disable, or --merge"); + goto out; + } + + if (granularity && !add) { + error_report("granularity only supported with --add"); + goto out; + } + if (src_fmt && !src_filename) { + error_report("-F only supported with -b"); + goto out; + } + if (src_filename && !merge) { + error_report("Merge bitmap source file only supported with " + "--merge"); + goto out; + } + + if (optind != argc - 2) { + error_report("Expecting filename and bitmap name"); + goto out; + } + + filename = argv[optind]; + bitmap = argv[optind + 1]; + + blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR, false, false, + false); + if (!blk) { + goto out; + } + bs = blk_bs(blk); + if (src_filename) { + src = img_open(false, src_filename, src_fmt, 0, false, false, false); + if (!src) { + goto out; + } + src_bs = blk_bs(src); + } else { + src_bs = bs; + } + + QSIMPLEQ_FOREACH_SAFE(act, &actions, next, act_next) { + switch (act->act) { + case BITMAP_ADD: + qmp_block_dirty_bitmap_add(bs->node_name, bitmap, + !!granularity, granularity, true, true, + false, false, &err); + op = "add"; + break; + case BITMAP_REMOVE: + qmp_block_dirty_bitmap_remove(bs->node_name, bitmap, &err); + op = "remove"; + break; + case BITMAP_CLEAR: + qmp_block_dirty_bitmap_clear(bs->node_name, bitmap, &err); + op = "clear"; + break; + case BITMAP_ENABLE: + qmp_block_dirty_bitmap_enable(bs->node_name, bitmap, &err); + op = "enable"; + break; + case BITMAP_DISABLE: + qmp_block_dirty_bitmap_disable(bs->node_name, bitmap, &err); + op = "disable"; + break; + case BITMAP_MERGE: { + BlockDirtyBitmapMergeSource *merge_src; + BlockDirtyBitmapMergeSourceList *list; + + merge_src = g_new0(BlockDirtyBitmapMergeSource, 1); + merge_src->type = QTYPE_QDICT; + merge_src->u.external.node = g_strdup(src_bs->node_name); + merge_src->u.external.name = g_strdup(act->src); + list = g_new0(BlockDirtyBitmapMergeSourceList, 1); + list->value = merge_src; + qmp_block_dirty_bitmap_merge(bs->node_name, bitmap, list, &err); + qapi_free_BlockDirtyBitmapMergeSourceList(list); + op = "merge"; + break; + } + default: + g_assert_not_reached(); + } + + if (err) { + error_reportf_err(err, "Operation %s on bitmap %s failed: ", + op, bitmap); + goto out; + } + g_free(act); + } + + ret = 0; + + out: + blk_unref(src); + blk_unref(blk); + qemu_opts_del(opts); + return ret; +} + #define C_BS 01 #define C_COUNT 02 #define C_IF 04 |