mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 20:19:44 +00:00
bitmaps patches for 2020-05-18
- update bitmaps maintainers - add 'qemu-img bitmap' subcommand -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEccLMIrHEYCkn0vOqp6FrSiUnQ2oFAl7EHTcACgkQp6FrSiUn Q2pIqAf+NgFF62NxBGjpnVaZMp1UKegHow2RBSzDoPMuG/fT7Oj6QJ3k0GSDWrwM oEIYOC5WsaY6ngCdIOT3KqH4RXBGwjxIv8EuGUsbDw9DUvzpCDA7lm59RB7S4IXl EVLxzmfTlbRgSMxHVQICgAfBgs6geCEl+nNQubGW+UkOU0JCYejzBz6b4JBctR1Y H5/6/caR+VWj6kiYlYu0BSDqv4zDNExj/VyFzaSM9MzyRsAFhky6tmF6ZwnyHIoP 5AGifc+vMbrDLlrR9tMQD6mXIfOsBixEzyIOHxlgKTvYS676+VYynmNVvd5NZdK5 0pYMzWs6MnqyxZhC+SKnSxfITtNwXg== =a29h -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
8b9112e4ae
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1 +1,2 @@
|
||||
common-obj-y += block-hmp-cmds.o
|
||||
block-obj-y += bitmap-qmp-cmds.o
|
||||
|
321
block/monitor/bitmap-qmp-cmds.c
Normal file
321
block/monitor/bitmap-qmp-cmds.c
Normal file
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
303
blockdev.c
303
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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
250
qemu-img.c
250
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
|
||||
|
Loading…
Reference in New Issue
Block a user