Pull request
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAlyIFSwACgkQfe+BBqr8 OQ7wghAAm16eCEr57oTO7QXR3y8uVFsKqXBn9cNH6nbrFp2PUQSglwMDKBls1Z5m olF23X/JaqSlSmkL9BBuzDZ6Up+kkHKuxPq4/5RKXfiDI0pr3R0eqts0COAlaN9q Bew3ipj99m8gzMi2093AW4+Ob0N3658fuDTGLe1M1Uoy7CEg1QJ7rVOBBEui7vIl RbZ8l/Zmb4ldNpB3lnE4Nh9ue8fy0RAj3Nai161nCnNeXNF/VzD3Ye8bojSBbnux PIMX6/RWmykX4feIf9QP8apDpxX4HkyuPq5EdwT9PD8PwdyXPAXZtsYUNCuNtQuk n5VKFVgFYgqUclBeVHmrMYPU4K4iCFQp4/Fua7wzPEC0iG05NiiDv91oVkEJCp3L ManHeuGfNLCcXaIntKZhuJl1cK8yMM3yDww6/pPTehrPjcyvKa0NOqhQBExektcD R6q7maJRzFaxSxdcs+Zzuog9zESvH1mlJxQCKzeYhAP0kkxInyTELE/Vbx37xuqR RFfZYyVQ6x87Q/sxHx4EMiV97WUM8elZOQdSEC/okt5WUUNpgIu0WF9nSQ1VKZ8C CZmv5xh9ogfwvB/kOm6IVwNkLvVagJQcLwddORI5LLXLbSIUcuwVSuyMp/7iDtQ/ hnHkGs2mIJ2JUYbSSNsSJNs6oTurn8eTFCeGoYKJgd9l4QxaThU= =ekU+ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jnsnow/tags/bitmaps-pull-request' into staging Pull request # gpg: Signature made Tue 12 Mar 2019 20:23:08 GMT # gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full] # Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB # Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E * remotes/jnsnow/tags/bitmaps-pull-request: (22 commits) tests/qemu-iotests: add bitmap resize test 246 block/qcow2-bitmap: Allow resizes with persistent bitmaps block/qcow2-bitmap: Don't check size for IN_USE bitmap docs/interop/qcow2: Improve bitmap flag in_use specification bitmaps: Fix typo in function name block/dirty-bitmaps: implement inconsistent bit block/dirty-bitmaps: disallow busy bitmaps as merge source block/dirty-bitmaps: prohibit removing readonly bitmaps block/dirty-bitmaps: prohibit readonly bitmaps for backups block/dirty-bitmaps: add block_dirty_bitmap_check function block/dirty-bitmap: add inconsistent status block/dirty-bitmaps: add inconsistent bit iotests: add busy/recording bit test to 124 blockdev: remove unused paio parameter documentation block/dirty-bitmaps: move comment block block/dirty-bitmaps: unify qmp_locked and user_locked calls block/dirty-bitmap: explicitly lock bitmaps with successors nbd: change error checking order for bitmaps block/dirty-bitmap: change semantics of enabled predicate block/dirty-bitmap: remove set/reset assertions against enabled bit ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org> # Conflicts: # tests/qemu-iotests/group
This commit is contained in:
commit
523a2a42c3
@ -28,29 +28,12 @@
|
||||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
|
||||
/**
|
||||
* A BdrvDirtyBitmap can be in four possible user-visible states:
|
||||
* (1) Active: successor is NULL, and disabled is false: full r/w mode
|
||||
* (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode,
|
||||
* guest writes are dropped, but monitor writes are possible,
|
||||
* through commands like merge and clear.
|
||||
* (3) Frozen: successor is not NULL.
|
||||
* A frozen bitmap cannot be renamed, deleted, cleared, set,
|
||||
* enabled, merged to, etc. A frozen bitmap can only abdicate()
|
||||
* or reclaim().
|
||||
* In this state, the anonymous successor bitmap may be either
|
||||
* Active and recording writes from the guest (e.g. backup jobs),
|
||||
* but it can be Disabled and not recording writes.
|
||||
* (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap
|
||||
* in any way from the monitor.
|
||||
*/
|
||||
struct BdrvDirtyBitmap {
|
||||
QemuMutex *mutex;
|
||||
HBitmap *bitmap; /* Dirty bitmap implementation */
|
||||
HBitmap *meta; /* Meta dirty bitmap */
|
||||
bool qmp_locked; /* Bitmap is locked, it can't be modified
|
||||
through QMP */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
|
||||
bool busy; /* Bitmap is busy, it can't be used via QMP */
|
||||
BdrvDirtyBitmap *successor; /* Anonymous child, if any. */
|
||||
char *name; /* Optional non-empty unique ID */
|
||||
int64_t size; /* Size of the bitmap, in bytes */
|
||||
bool disabled; /* Bitmap is disabled. It ignores all writes to
|
||||
@ -63,6 +46,9 @@ struct BdrvDirtyBitmap {
|
||||
and this bitmap must remain unchanged while
|
||||
this flag is set. */
|
||||
bool persistent; /* bitmap must be saved to owner disk image */
|
||||
bool inconsistent; /* bitmap is persistent, but inconsistent.
|
||||
It cannot be used at all in any way, except
|
||||
a QMP user can remove it. */
|
||||
bool migration; /* Bitmap is selected for migration, it should
|
||||
not be stored on the next inactivation
|
||||
(persistent flag doesn't matter until next
|
||||
@ -183,41 +169,58 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap)
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
/* Both conditions disallow user-modification via QMP. */
|
||||
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) {
|
||||
return bdrv_dirty_bitmap_frozen(bitmap) ||
|
||||
bdrv_dirty_bitmap_qmp_locked(bitmap);
|
||||
static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->busy;
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
|
||||
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bitmap->qmp_locked = qmp_locked;
|
||||
bitmap->busy = busy;
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->qmp_locked;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !(bitmap->disabled || bitmap->successor);
|
||||
return !bitmap->disabled;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
/**
|
||||
* bdrv_dirty_bitmap_status: This API is now deprecated.
|
||||
* Called with BQL taken.
|
||||
*
|
||||
* A BdrvDirtyBitmap can be in four possible user-visible states:
|
||||
* (1) Active: successor is NULL, and disabled is false: full r/w mode
|
||||
* (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode,
|
||||
* guest writes are dropped, but monitor writes are possible,
|
||||
* through commands like merge and clear.
|
||||
* (3) Frozen: successor is not NULL.
|
||||
* A frozen bitmap cannot be renamed, deleted, cleared, set,
|
||||
* enabled, merged to, etc. A frozen bitmap can only abdicate()
|
||||
* or reclaim().
|
||||
* In this state, the anonymous successor bitmap may be either
|
||||
* Active and recording writes from the guest (e.g. backup jobs),
|
||||
* or it can be Disabled and not recording writes.
|
||||
* (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap
|
||||
* in any way from the monitor.
|
||||
* (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and
|
||||
* is unusable by QEMU. It can be deleted to remove it from
|
||||
* the qcow2.
|
||||
*/
|
||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_INCONSISTENT;
|
||||
} else if (bdrv_dirty_bitmap_has_successor(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_FROZEN;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
} else if (bdrv_dirty_bitmap_busy(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_LOCKED;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
return DIRTY_BITMAP_STATUS_DISABLED;
|
||||
@ -226,9 +229,44 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !bitmap->disabled || (bitmap->successor &&
|
||||
!bitmap->successor->disabled);
|
||||
}
|
||||
|
||||
int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
||||
Error **errp)
|
||||
{
|
||||
if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) {
|
||||
error_setg(errp, "Bitmap '%s' is currently in use by another"
|
||||
" operation and cannot be used", bitmap->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
||||
bitmap->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((flags & BDRV_BITMAP_INCONSISTENT) &&
|
||||
bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||
error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used",
|
||||
bitmap->name);
|
||||
error_append_hint(errp, "Try block-dirty-bitmap-remove to delete"
|
||||
" this bitmap from disk");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a successor bitmap destined to replace this bitmap after an operation.
|
||||
* Requires that the bitmap is not frozen and has no successor.
|
||||
* Requires that the bitmap is not marked busy and has no successor.
|
||||
* The successor will be enabled if the parent bitmap was.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
@ -237,12 +275,14 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
uint64_t granularity;
|
||||
BdrvDirtyBitmap *child;
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp, "Cannot create a successor for a bitmap that is "
|
||||
"currently frozen");
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
|
||||
return -1;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_has_successor(bitmap)) {
|
||||
error_setg(errp, "Cannot create a successor for a bitmap that already "
|
||||
"has one");
|
||||
return -1;
|
||||
}
|
||||
assert(!bitmap->successor);
|
||||
|
||||
/* Create an anonymous successor */
|
||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||
@ -253,15 +293,16 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
|
||||
/* Successor will be on or off based on our current state. */
|
||||
child->disabled = bitmap->disabled;
|
||||
bitmap->disabled = true;
|
||||
|
||||
/* Install the successor and freeze the parent */
|
||||
/* Install the successor and mark the parent as busy */
|
||||
bitmap->successor = child;
|
||||
bitmap->busy = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
}
|
||||
|
||||
@ -278,7 +319,8 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
||||
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bitmap->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_busy(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_has_successor(bitmap));
|
||||
assert(!bitmap->meta);
|
||||
QLIST_REMOVE(bitmap, list);
|
||||
hbitmap_free(bitmap->bitmap);
|
||||
@ -310,6 +352,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
bitmap->successor = NULL;
|
||||
successor->persistent = bitmap->persistent;
|
||||
bitmap->persistent = false;
|
||||
bitmap->busy = false;
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
|
||||
return successor;
|
||||
@ -318,7 +361,8 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
/**
|
||||
* In cases of failure where we can no longer safely delete the parent,
|
||||
* we may wish to re-join the parent and child/successor.
|
||||
* The merged parent will be un-frozen, but not explicitly re-enabled.
|
||||
* The merged parent will be marked as not busy.
|
||||
* The marged parent will be enabled if and only if the successor was enabled.
|
||||
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
||||
*/
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
@ -336,6 +380,9 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent->disabled = successor->disabled;
|
||||
parent->busy = false;
|
||||
bdrv_release_dirty_bitmap_locked(successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
@ -366,7 +413,8 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_busy(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_has_successor(bitmap));
|
||||
assert(!bitmap->active_iterators);
|
||||
hbitmap_truncate(bitmap->bitmap, bytes);
|
||||
bitmap->size = bytes;
|
||||
@ -384,7 +432,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
||||
|
||||
/**
|
||||
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
* There must not be any busy bitmaps attached.
|
||||
* This function does not remove persistent bitmaps from the storage.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
@ -421,7 +469,6 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = true;
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
@ -448,7 +495,11 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||
info->has_name = !!bm->name;
|
||||
info->name = g_strdup(bm->name);
|
||||
info->status = bdrv_dirty_bitmap_status(bm);
|
||||
info->recording = bdrv_dirty_bitmap_recording(bm);
|
||||
info->busy = bdrv_dirty_bitmap_busy(bm);
|
||||
info->persistent = bm->persistent;
|
||||
info->has_inconsistent = bm->inconsistent;
|
||||
info->inconsistent = bm->inconsistent;
|
||||
entry->value = info;
|
||||
*plist = entry;
|
||||
plist = &entry->next;
|
||||
@ -531,7 +582,6 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
|
||||
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
int64_t offset, int64_t bytes)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||
hbitmap_set(bitmap->bitmap, offset, bytes);
|
||||
}
|
||||
@ -548,7 +598,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
int64_t offset, int64_t bytes)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||
hbitmap_reset(bitmap->bitmap, offset, bytes);
|
||||
}
|
||||
@ -691,13 +740,23 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
|
||||
void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bitmap->persistent = persistent;
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
assert(bitmap->persistent == true);
|
||||
bitmap->inconsistent = true;
|
||||
bitmap->disabled = true;
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
|
||||
{
|
||||
@ -706,11 +765,16 @@ void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
|
||||
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->persistent && !bitmap->migration;
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->inconsistent;
|
||||
}
|
||||
|
||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
@ -757,15 +821,11 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||
|
||||
qemu_mutex_lock(dest->mutex);
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(dest)) {
|
||||
error_setg(errp, "Bitmap '%s' is currently in use by another"
|
||||
" operation and cannot be modified", dest->name);
|
||||
if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_readonly(dest)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
||||
dest->name);
|
||||
if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -343,11 +343,17 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity;
|
||||
BdrvDirtyBitmap *bitmap = NULL;
|
||||
|
||||
if (bm->flags & BME_FLAG_IN_USE) {
|
||||
error_setg(errp, "Bitmap '%s' is in use", bm->name);
|
||||
granularity = 1U << bm->granularity_bits;
|
||||
bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp);
|
||||
if (bitmap == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bm->flags & BME_FLAG_IN_USE) {
|
||||
/* Data is unusable, skip loading it */
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
ret = bitmap_table_load(bs, &bm->table, &bitmap_table);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
@ -356,12 +362,6 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
granularity = 1U << bm->granularity_bits;
|
||||
bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp);
|
||||
if (bitmap == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image",
|
||||
@ -462,10 +462,25 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry)
|
||||
return len;
|
||||
}
|
||||
|
||||
fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) ||
|
||||
(len > ((phys_bitmap_bytes * 8) << entry->granularity_bits));
|
||||
if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return fail ? -EINVAL : 0;
|
||||
if (!(entry->flags & BME_FLAG_IN_USE) &&
|
||||
(len > ((phys_bitmap_bytes * 8) << entry->granularity_bits)))
|
||||
{
|
||||
/*
|
||||
* We've loaded a valid bitmap (IN_USE not set) or we are going to
|
||||
* store a valid bitmap, but the allocated bitmap table size is not
|
||||
* enough to store this bitmap.
|
||||
*
|
||||
* Note, that it's OK to have an invalid bitmap with invalid size due
|
||||
* to a bitmap that was not correctly saved after image resize.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void bitmap_directory_to_be(uint8_t *dir, size_t size)
|
||||
@ -950,6 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
Qcow2Bitmap *bm;
|
||||
GSList *created_dirty_bitmaps = NULL;
|
||||
bool header_updated = false;
|
||||
bool needs_update = false;
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
/* No bitmaps - nothing to do */
|
||||
@ -963,35 +979,39 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
if (!(bm->flags & BME_FLAG_IN_USE)) {
|
||||
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
|
||||
if (bitmap == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(bm->flags & BME_FLAG_AUTO)) {
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
bdrv_dirty_bitmap_set_persistance(bitmap, true);
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
created_dirty_bitmaps =
|
||||
g_slist_append(created_dirty_bitmaps, bitmap);
|
||||
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
|
||||
if (bitmap == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bdrv_dirty_bitmap_set_persistence(bitmap, true);
|
||||
if (bm->flags & BME_FLAG_IN_USE) {
|
||||
bdrv_dirty_bitmap_set_inconsistent(bitmap);
|
||||
} else {
|
||||
/* NB: updated flags only get written if can_write(bs) is true. */
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
needs_update = true;
|
||||
}
|
||||
if (!(bm->flags & BME_FLAG_AUTO)) {
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
created_dirty_bitmaps =
|
||||
g_slist_append(created_dirty_bitmaps, bitmap);
|
||||
}
|
||||
|
||||
if (created_dirty_bitmaps != NULL) {
|
||||
if (can_write(bs)) {
|
||||
/* in_use flags must be updated */
|
||||
int ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
goto fail;
|
||||
}
|
||||
header_updated = true;
|
||||
} else {
|
||||
g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
|
||||
(gpointer)true);
|
||||
if (needs_update && can_write(bs)) {
|
||||
/* in_use flags must be updated */
|
||||
int ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
goto fail;
|
||||
}
|
||||
header_updated = true;
|
||||
}
|
||||
|
||||
if (!can_write(bs)) {
|
||||
g_slist_foreach(created_dirty_bitmaps, set_readonly_helper,
|
||||
(gpointer)true);
|
||||
}
|
||||
|
||||
g_slist_free(created_dirty_bitmaps);
|
||||
@ -1113,23 +1133,21 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
if (!(bm->flags & BME_FLAG_IN_USE)) {
|
||||
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||
if (bitmap == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_setg(errp, "Bitmap %s is not readonly but not marked"
|
||||
"'IN_USE' in the image. Something went wrong,"
|
||||
"all the bitmaps may be corrupted", bm->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||
if (bitmap == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was "
|
||||
"not marked as readonly. This is a bug, something went "
|
||||
"wrong. All of the bitmaps may be corrupted", bm->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||
}
|
||||
|
||||
if (ro_dirty_bitmaps != NULL) {
|
||||
@ -1157,6 +1175,52 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||
return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
|
||||
}
|
||||
|
||||
/* Checks to see if it's safe to resize bitmaps */
|
||||
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2BitmapList *bm_list;
|
||||
Qcow2Bitmap *bm;
|
||||
int ret = 0;
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
if (bm_list == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||
if (bitmap == NULL) {
|
||||
/*
|
||||
* We rely on all bitmaps being in-memory to be able to resize them,
|
||||
* Otherwise, we'd need to resize them on disk explicitly
|
||||
*/
|
||||
error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that "
|
||||
"were not loaded into memory");
|
||||
ret = -ENOTSUP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The checks against readonly and busy are redundant, but certainly
|
||||
* do no harm. checks against inconsistent are crucial:
|
||||
*/
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
||||
ret = -ENOTSUP;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
bitmap_list_free(bm_list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* store_bitmap_data()
|
||||
* Store bitmap to image, filling bitmap table accordingly.
|
||||
*/
|
||||
@ -1424,9 +1488,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||
Qcow2Bitmap *bm;
|
||||
|
||||
if (!bdrv_dirty_bitmap_get_persistance(bitmap) ||
|
||||
bdrv_dirty_bitmap_readonly(bitmap))
|
||||
{
|
||||
if (!bdrv_dirty_bitmap_get_persistence(bitmap) ||
|
||||
bdrv_dirty_bitmap_readonly(bitmap) ||
|
||||
bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1542,7 +1606,7 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
||||
{
|
||||
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
bdrv_dirty_bitmap_set_readonly(bitmap, true);
|
||||
}
|
||||
}
|
||||
|
@ -3670,9 +3670,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
|
||||
/* cannot proceed if image has bitmaps */
|
||||
if (s->nb_bitmaps) {
|
||||
/* TODO: resize bitmaps in the image */
|
||||
error_setg(errp, "Can't resize an image which has bitmaps");
|
||||
if (qcow2_truncate_bitmaps_check(bs, errp)) {
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
}
|
||||
|
@ -723,6 +723,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
Error **errp);
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
|
||||
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp);
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
||||
bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
|
55
blockdev.c
55
blockdev.c
@ -1257,7 +1257,6 @@ out_aio_context:
|
||||
* @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.
|
||||
* @paio: Output pointer for aio_context acquisition, if desired. Can be NULL.
|
||||
* @errp: Output pointer for error information. Can be NULL.
|
||||
*
|
||||
* @return: A bitmap object on success, or NULL on failure.
|
||||
@ -2011,11 +2010,7 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a bitmap in use by another operation");
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
|
||||
error_setg(errp, "Cannot clear a readonly bitmap");
|
||||
if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2060,10 +2055,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be enabled", action->name);
|
||||
if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2101,10 +2093,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be disabled", action->name);
|
||||
if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2875,7 +2864,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
|
||||
bdrv_dirty_bitmap_set_persistence(bitmap, persistent);
|
||||
out:
|
||||
if (aio_context) {
|
||||
aio_context_release(aio_context);
|
||||
@ -2895,14 +2884,12 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation and"
|
||||
" cannot be removed", name);
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO,
|
||||
errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err);
|
||||
@ -2934,13 +2921,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be cleared", name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2958,10 +2939,7 @@ void qmp_block_dirty_bitmap_enable(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be enabled", name);
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2979,10 +2957,7 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be disabled", name);
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3561,10 +3536,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
bdrv_unref(target_bs);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_user_locked(bmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be used for backup", backup->bitmap);
|
||||
if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -3674,10 +3646,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_user_locked(bmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be used for backup", backup->bitmap);
|
||||
if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -633,7 +633,10 @@ Structure of a bitmap directory entry:
|
||||
Bit
|
||||
0: in_use
|
||||
The bitmap was not saved correctly and may be
|
||||
inconsistent.
|
||||
inconsistent. Although the bitmap metadata is still
|
||||
well-formed from a qcow2 perspective, the metadata
|
||||
(such as the auto flag or bitmap size) or data
|
||||
contents may be outdated.
|
||||
|
||||
1: auto
|
||||
The bitmap must reflect all changes of the virtual
|
||||
@ -761,8 +764,8 @@ corresponding range of the virtual disk (see above) was written to while the
|
||||
bitmap was 'enabled'. An unset bit means that this range was not written to.
|
||||
|
||||
The software doesn't have to sync the bitmap in the image file with its
|
||||
representation in RAM after each write. Flag 'in_use' should be set while the
|
||||
bitmap is not synced.
|
||||
representation in RAM after each write or metadata change. Flag 'in_use'
|
||||
should be set while the bitmap is not synced.
|
||||
|
||||
In the image file the 'enabled' state is reflected by the 'auto' flag. If this
|
||||
flag is set, the software must consider the bitmap as 'enabled' and start
|
||||
|
@ -5,6 +5,16 @@
|
||||
#include "qapi/qapi-types-block-core.h"
|
||||
#include "qemu/hbitmap.h"
|
||||
|
||||
typedef enum BitmapCheckFlags {
|
||||
BDRV_BITMAP_BUSY = 1,
|
||||
BDRV_BITMAP_RO = 2,
|
||||
BDRV_BITMAP_INCONSISTENT = 4,
|
||||
} BitmapCheckFlags;
|
||||
|
||||
#define BDRV_BITMAP_DEFAULT (BDRV_BITMAP_BUSY | BDRV_BITMAP_RO | \
|
||||
BDRV_BITMAP_INCONSISTENT)
|
||||
#define BDRV_BITMAP_ALLOW_RO (BDRV_BITMAP_BUSY | BDRV_BITMAP_INCONSISTENT)
|
||||
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
const char *name,
|
||||
@ -24,6 +34,8 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap);
|
||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name);
|
||||
int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
||||
Error **errp);
|
||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap);
|
||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
|
||||
void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
@ -36,7 +48,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs);
|
||||
uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs);
|
||||
uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap);
|
||||
const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap);
|
||||
int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap);
|
||||
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap);
|
||||
@ -66,9 +78,10 @@ void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap,
|
||||
void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap);
|
||||
|
||||
void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value);
|
||||
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap,
|
||||
void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap,
|
||||
bool persistent);
|
||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked);
|
||||
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap);
|
||||
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy);
|
||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||
HBitmap **backup, Error **errp);
|
||||
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration);
|
||||
@ -90,9 +103,8 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes);
|
||||
bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_has_readonly_bitmaps(BlockDriverState *bs);
|
||||
bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap);
|
||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap);
|
||||
|
@ -261,7 +261,7 @@ static void dirty_bitmap_mig_cleanup(void)
|
||||
|
||||
while ((dbms = QSIMPLEQ_FIRST(&dirty_bitmap_mig_state.dbms_list)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry);
|
||||
bdrv_dirty_bitmap_set_qmp_locked(dbms->bitmap, false);
|
||||
bdrv_dirty_bitmap_set_busy(dbms->bitmap, false);
|
||||
bdrv_unref(dbms->bs);
|
||||
g_free(dbms);
|
||||
}
|
||||
@ -274,6 +274,7 @@ static int init_dirty_bitmap_migration(void)
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
DirtyBitmapMigBitmapState *dbms;
|
||||
BdrvNextIterator it;
|
||||
Error *local_err = NULL;
|
||||
|
||||
dirty_bitmap_mig_state.bulk_completed = false;
|
||||
dirty_bitmap_mig_state.prev_bs = NULL;
|
||||
@ -301,20 +302,14 @@ static int init_dirty_bitmap_migration(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
error_report("Can't migrate a bitmap that is in use by another operation: '%s'",
|
||||
bdrv_dirty_bitmap_name(bitmap));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_report("Can't migrate read-only dirty bitmap: '%s",
|
||||
bdrv_dirty_bitmap_name(bitmap));
|
||||
if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT,
|
||||
&local_err)) {
|
||||
error_report_err(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bdrv_ref(bs);
|
||||
bdrv_dirty_bitmap_set_qmp_locked(bitmap, true);
|
||||
bdrv_dirty_bitmap_set_busy(bitmap, true);
|
||||
|
||||
dbms = g_new0(DirtyBitmapMigBitmapState, 1);
|
||||
dbms->bs = bs;
|
||||
@ -326,7 +321,7 @@ static int init_dirty_bitmap_migration(void)
|
||||
if (bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT;
|
||||
}
|
||||
|
||||
@ -478,7 +473,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s)
|
||||
}
|
||||
|
||||
if (flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT) {
|
||||
bdrv_dirty_bitmap_set_persistance(s->bitmap, true);
|
||||
bdrv_dirty_bitmap_set_persistence(s->bitmap, true);
|
||||
}
|
||||
|
||||
bdrv_disable_dirty_bitmap(s->bitmap);
|
||||
@ -542,7 +537,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s)
|
||||
}
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(s->bitmap)) {
|
||||
if (bdrv_dirty_bitmap_has_successor(s->bitmap)) {
|
||||
bdrv_dirty_bitmap_lock(s->bitmap);
|
||||
if (enabled_bitmaps == NULL) {
|
||||
/* in postcopy */
|
||||
|
13
nbd/server.c
13
nbd/server.c
@ -1510,6 +1510,10 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) &&
|
||||
bdrv_dirty_bitmap_enabled(bm)) {
|
||||
error_setg(errp,
|
||||
@ -1518,12 +1522,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bm)) {
|
||||
error_setg(errp, "Bitmap '%s' is in use", bitmap);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bdrv_dirty_bitmap_set_qmp_locked(bm, true);
|
||||
bdrv_dirty_bitmap_set_busy(bm, true);
|
||||
exp->export_bitmap = bm;
|
||||
exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s",
|
||||
bitmap);
|
||||
@ -1641,7 +1640,7 @@ void nbd_export_put(NBDExport *exp)
|
||||
}
|
||||
|
||||
if (exp->export_bitmap) {
|
||||
bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false);
|
||||
bdrv_dirty_bitmap_set_busy(exp->export_bitmap, false);
|
||||
g_free(exp->export_bitmap_context);
|
||||
}
|
||||
|
||||
|
@ -451,10 +451,15 @@
|
||||
# recording new writes. If the bitmap was @disabled, it is not
|
||||
# recording new writes. (Since 2.12)
|
||||
#
|
||||
# @inconsistent: This is a persistent dirty bitmap that was marked in-use on
|
||||
# disk, and is unusable by QEMU. It can only be deleted.
|
||||
# Please rely on the inconsistent field in @BlockDirtyInfo
|
||||
# instead, as the status field is deprecated. (Since 4.0)
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'enum': 'DirtyBitmapStatus',
|
||||
'data': ['active', 'disabled', 'frozen', 'locked'] }
|
||||
'data': ['active', 'disabled', 'frozen', 'locked', 'inconsistent'] }
|
||||
|
||||
##
|
||||
# @BlockDirtyInfo:
|
||||
@ -467,16 +472,29 @@
|
||||
#
|
||||
# @granularity: granularity of the dirty bitmap in bytes (since 1.4)
|
||||
#
|
||||
# @status: current status of the dirty bitmap (since 2.4)
|
||||
# @status: Deprecated in favor of @recording and @locked. (since 2.4)
|
||||
#
|
||||
# @persistent: true if the bitmap will eventually be flushed to persistent
|
||||
# storage (since 4.0)
|
||||
# @recording: true if the bitmap is recording new writes from the guest.
|
||||
# Replaces `active` and `disabled` statuses. (since 4.0)
|
||||
#
|
||||
# @busy: true if the bitmap is in-use by some operation (NBD or jobs)
|
||||
# and cannot be modified via QMP or used by another operation.
|
||||
# Replaces `locked` and `frozen` statuses. (since 4.0)
|
||||
#
|
||||
# @persistent: true if the bitmap was stored on disk, is scheduled to be stored
|
||||
# on disk, or both. (since 4.0)
|
||||
#
|
||||
# @inconsistent: true if this is a persistent bitmap that was improperly
|
||||
# stored. Implies @persistent to be true; @recording and
|
||||
# @busy to be false. This bitmap cannot be used. To remove
|
||||
# it, use @block-dirty-bitmap-remove. (Since 4.0)
|
||||
#
|
||||
# Since: 1.3
|
||||
##
|
||||
{ 'struct': 'BlockDirtyInfo',
|
||||
'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32',
|
||||
'status': 'DirtyBitmapStatus', 'persistent': 'bool' } }
|
||||
'recording': 'bool', 'busy': 'bool', 'status': 'DirtyBitmapStatus',
|
||||
'persistent': 'bool', '*inconsistent': 'bool' } }
|
||||
|
||||
##
|
||||
# @Qcow2BitmapInfoFlags:
|
||||
|
@ -79,6 +79,12 @@ the current values of the environment variables to ``-audiodev'' options.
|
||||
"autoload" parameter is now ignored. All bitmaps are automatically loaded
|
||||
from qcow2 images.
|
||||
|
||||
@subsection query-block result field dirty-bitmaps[i].status (since 4.0)
|
||||
|
||||
The ``status'' field of the ``BlockDirtyInfo'' structure, returned by
|
||||
the query-block command is deprecated. Two new boolean fields,
|
||||
``recording'' and ``busy'' effectively replace it.
|
||||
|
||||
@subsection query-cpus (since 2.12.0)
|
||||
|
||||
The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command.
|
||||
|
@ -634,6 +634,119 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
|
||||
self.vm.shutdown()
|
||||
self.check_backups()
|
||||
|
||||
def test_incremental_pause(self):
|
||||
"""
|
||||
Test an incremental backup that errors into a pause and is resumed.
|
||||
"""
|
||||
|
||||
drive0 = self.drives[0]
|
||||
# NB: The blkdebug script here looks for a "flush, read, read" pattern.
|
||||
# The flush occurs in hmp_io_writes, the first read in device_add, and
|
||||
# the last read during the block job.
|
||||
result = self.vm.qmp('blockdev-add',
|
||||
node_name=drive0['id'],
|
||||
driver=drive0['fmt'],
|
||||
file={
|
||||
'driver': 'blkdebug',
|
||||
'image': {
|
||||
'driver': 'file',
|
||||
'filename': drive0['file']
|
||||
},
|
||||
'set-state': [{
|
||||
'event': 'flush_to_disk',
|
||||
'state': 1,
|
||||
'new_state': 2
|
||||
},{
|
||||
'event': 'read_aio',
|
||||
'state': 2,
|
||||
'new_state': 3
|
||||
}],
|
||||
'inject-error': [{
|
||||
'event': 'read_aio',
|
||||
'errno': 5,
|
||||
'state': 3,
|
||||
'immediately': False,
|
||||
'once': True
|
||||
}],
|
||||
})
|
||||
self.assert_qmp(result, 'return', {})
|
||||
self.create_anchor_backup(drive0)
|
||||
bitmap = self.add_bitmap('bitmap0', drive0)
|
||||
|
||||
# Emulate guest activity
|
||||
self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
|
||||
('0xfe', '16M', '256k'),
|
||||
('0x64', '32736k', '64k')))
|
||||
|
||||
# For the purposes of query-block visibility of bitmaps, add a drive
|
||||
# frontend after we've written data; otherwise we can't use hmp-io
|
||||
result = self.vm.qmp("device_add",
|
||||
id="device0",
|
||||
drive=drive0['id'],
|
||||
driver="virtio-blk")
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
# Bitmap Status Check
|
||||
query = self.vm.qmp('query-block')
|
||||
ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
|
||||
if bmap.get('name') == bitmap.name][0]
|
||||
self.assert_qmp(ret, 'count', 458752)
|
||||
self.assert_qmp(ret, 'granularity', 65536)
|
||||
self.assert_qmp(ret, 'status', 'active')
|
||||
self.assert_qmp(ret, 'busy', False)
|
||||
self.assert_qmp(ret, 'recording', True)
|
||||
|
||||
# Start backup
|
||||
parent, _ = bitmap.last_target()
|
||||
target = self.prepare_backup(bitmap, parent)
|
||||
res = self.vm.qmp('drive-backup',
|
||||
job_id=bitmap.drive['id'],
|
||||
device=bitmap.drive['id'],
|
||||
sync='incremental',
|
||||
bitmap=bitmap.name,
|
||||
format=bitmap.drive['fmt'],
|
||||
target=target,
|
||||
mode='existing',
|
||||
on_source_error='stop')
|
||||
self.assert_qmp(res, 'return', {})
|
||||
|
||||
# Wait for the error
|
||||
event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
|
||||
match={"data":{"device":bitmap.drive['id']}})
|
||||
self.assert_qmp(event, 'data', {'device': bitmap.drive['id'],
|
||||
'action': 'stop',
|
||||
'operation': 'read'})
|
||||
|
||||
# Bitmap Status Check
|
||||
query = self.vm.qmp('query-block')
|
||||
ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
|
||||
if bmap.get('name') == bitmap.name][0]
|
||||
self.assert_qmp(ret, 'count', 458752)
|
||||
self.assert_qmp(ret, 'granularity', 65536)
|
||||
self.assert_qmp(ret, 'status', 'frozen')
|
||||
self.assert_qmp(ret, 'busy', True)
|
||||
self.assert_qmp(ret, 'recording', True)
|
||||
|
||||
# Resume and check incremental backup for consistency
|
||||
res = self.vm.qmp('block-job-resume', device=bitmap.drive['id'])
|
||||
self.assert_qmp(res, 'return', {})
|
||||
self.wait_qmp_backup(bitmap.drive['id'])
|
||||
|
||||
# Bitmap Status Check
|
||||
query = self.vm.qmp('query-block')
|
||||
ret = [bmap for bmap in query['return'][0]['dirty-bitmaps']
|
||||
if bmap.get('name') == bitmap.name][0]
|
||||
self.assert_qmp(ret, 'count', 0)
|
||||
self.assert_qmp(ret, 'granularity', 65536)
|
||||
self.assert_qmp(ret, 'status', 'active')
|
||||
self.assert_qmp(ret, 'busy', False)
|
||||
self.assert_qmp(ret, 'recording', True)
|
||||
|
||||
# Finalize / Cleanup
|
||||
self.make_reference_backup(bitmap)
|
||||
self.vm.shutdown()
|
||||
self.check_backups()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2'])
|
||||
|
@ -1,5 +1,5 @@
|
||||
...........
|
||||
............
|
||||
----------------------------------------------------------------------
|
||||
Ran 11 tests
|
||||
Ran 12 tests
|
||||
|
||||
OK
|
||||
|
@ -22,17 +22,21 @@ write -P0xcd 0x3ff0000 64k
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapB",
|
||||
"persistent": false,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapA",
|
||||
"persistent": false,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
@ -84,17 +88,21 @@ write -P0xcd 0x3ff0000 64k
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapB",
|
||||
"persistent": false,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapA",
|
||||
"persistent": false,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
@ -184,24 +192,30 @@ write -P0xea 0x3fe0000 64k
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 393216,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapC",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapB",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapA",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
}
|
||||
]
|
||||
@ -251,24 +265,30 @@ write -P0xea 0x3fe0000 64k
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 393216,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapC",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapB",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapA",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
}
|
||||
]
|
||||
@ -311,31 +331,39 @@ write -P0xea 0x3fe0000 64k
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapD",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 393216,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapC",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 262144,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapB",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
"granularity": 65536,
|
||||
"name": "bitmapA",
|
||||
"persistent": false,
|
||||
"recording": false,
|
||||
"status": "disabled"
|
||||
}
|
||||
]
|
||||
|
114
tests/qemu-iotests/246
Executable file
114
tests/qemu-iotests/246
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Test persistent bitmap resizing.
|
||||
#
|
||||
# Copyright (c) 2019 John Snow for Red Hat, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# owner=jsnow@redhat.com
|
||||
|
||||
import iotests
|
||||
from iotests import log
|
||||
|
||||
iotests.verify_image_format(supported_fmts=['qcow2'])
|
||||
size = 64 * 1024 * 1024 * 1024
|
||||
gran_small = 32 * 1024
|
||||
gran_large = 128 * 1024
|
||||
|
||||
def query_bitmaps(vm):
|
||||
res = vm.qmp("query-block")
|
||||
return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for
|
||||
device in res['return'] } }
|
||||
|
||||
with iotests.FilePath('img') as img_path, \
|
||||
iotests.VM() as vm:
|
||||
|
||||
log('--- Preparing image & VM ---\n')
|
||||
iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size))
|
||||
vm.add_drive(img_path)
|
||||
|
||||
|
||||
log('--- 1st Boot (Establish Baseline Image) ---\n')
|
||||
vm.launch()
|
||||
|
||||
log('\n--- Adding bitmaps Small, Medium, Large, and Transient ---\n')
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="Small", granularity=gran_small, persistent=True)
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="Medium", persistent=True)
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="Large", granularity=gran_large, persistent=True)
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="Transient", persistent=False)
|
||||
|
||||
log('--- Forcing flush of bitmaps to disk ---\n')
|
||||
log(query_bitmaps(vm), indent=2)
|
||||
vm.shutdown()
|
||||
|
||||
|
||||
log('--- 2nd Boot (Grow Image) ---\n')
|
||||
vm.launch()
|
||||
log(query_bitmaps(vm), indent=2)
|
||||
|
||||
log('--- Adding new bitmap, growing image, and adding 2nd new bitmap ---')
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="New", persistent=True)
|
||||
vm.qmp_log("human-monitor-command",
|
||||
command_line="block_resize drive0 70G")
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="Newtwo", persistent=True)
|
||||
log(query_bitmaps(vm), indent=2)
|
||||
|
||||
log('--- Forcing flush of bitmaps to disk ---\n')
|
||||
vm.shutdown()
|
||||
|
||||
|
||||
log('--- 3rd Boot (Shrink Image) ---\n')
|
||||
vm.launch()
|
||||
log(query_bitmaps(vm), indent=2)
|
||||
|
||||
log('--- Adding "NewB" bitmap, removing "New" bitmap ---')
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="NewB", persistent=True)
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0",
|
||||
name="New")
|
||||
|
||||
log('--- Truncating image ---\n')
|
||||
vm.qmp_log("human-monitor-command",
|
||||
command_line="block_resize drive0 50G")
|
||||
|
||||
log('--- Adding "NewC" bitmap, removing "NewTwo" bitmap ---')
|
||||
vm.qmp_log("block-dirty-bitmap-add", node="drive0",
|
||||
name="NewC", persistent=True)
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Newtwo")
|
||||
|
||||
log('--- Forcing flush of bitmaps to disk ---\n')
|
||||
vm.shutdown()
|
||||
|
||||
|
||||
log('--- 4th Boot (Verification and Cleanup) ---\n')
|
||||
vm.launch()
|
||||
log(query_bitmaps(vm), indent=2)
|
||||
|
||||
log('--- Removing all Bitmaps ---\n')
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Small")
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Medium")
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Large")
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="NewB")
|
||||
vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="NewC")
|
||||
log(query_bitmaps(vm), indent=2)
|
||||
|
||||
log('\n--- Done ---')
|
||||
vm.shutdown()
|
295
tests/qemu-iotests/246.out
Normal file
295
tests/qemu-iotests/246.out
Normal file
@ -0,0 +1,295 @@
|
||||
--- Preparing image & VM ---
|
||||
|
||||
--- 1st Boot (Establish Baseline Image) ---
|
||||
|
||||
|
||||
--- Adding bitmaps Small, Medium, Large, and Transient ---
|
||||
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 32768, "name": "Small", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Medium", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 131072, "name": "Large", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Transient", "node": "drive0", "persistent": false}}
|
||||
{"return": {}}
|
||||
--- Forcing flush of bitmaps to disk ---
|
||||
|
||||
{
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Transient",
|
||||
"persistent": false,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 131072,
|
||||
"name": "Large",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Medium",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 32768,
|
||||
"name": "Small",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
--- 2nd Boot (Grow Image) ---
|
||||
|
||||
{
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 32768,
|
||||
"name": "Small",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Medium",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 131072,
|
||||
"name": "Large",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
--- Adding new bitmap, growing image, and adding 2nd new bitmap ---
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "New", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{"execute": "human-monitor-command", "arguments": {"command-line": "block_resize drive0 70G"}}
|
||||
{"return": ""}
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Newtwo", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Newtwo",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "New",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 32768,
|
||||
"name": "Small",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Medium",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 131072,
|
||||
"name": "Large",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
--- Forcing flush of bitmaps to disk ---
|
||||
|
||||
--- 3rd Boot (Shrink Image) ---
|
||||
|
||||
{
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "New",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Newtwo",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 32768,
|
||||
"name": "Small",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Medium",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 131072,
|
||||
"name": "Large",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
--- Adding "NewB" bitmap, removing "New" bitmap ---
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "NewB", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "New", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
--- Truncating image ---
|
||||
|
||||
{"execute": "human-monitor-command", "arguments": {"command-line": "block_resize drive0 50G"}}
|
||||
{"return": ""}
|
||||
--- Adding "NewC" bitmap, removing "NewTwo" bitmap ---
|
||||
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "NewC", "node": "drive0", "persistent": true}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Newtwo", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
--- Forcing flush of bitmaps to disk ---
|
||||
|
||||
--- 4th Boot (Verification and Cleanup) ---
|
||||
|
||||
{
|
||||
"bitmaps": {
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "NewB",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "NewC",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 32768,
|
||||
"name": "Small",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"name": "Medium",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 131072,
|
||||
"name": "Large",
|
||||
"persistent": true,
|
||||
"recording": true,
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
--- Removing all Bitmaps ---
|
||||
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Small", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Medium", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Large", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "NewB", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "NewC", "node": "drive0"}}
|
||||
{"return": {}}
|
||||
{
|
||||
"bitmaps": {
|
||||
"drive0": []
|
||||
}
|
||||
}
|
||||
|
||||
--- Done ---
|
@ -244,3 +244,4 @@
|
||||
243 rw auto quick
|
||||
244 rw auto quick
|
||||
245 rw auto
|
||||
246 rw auto quick
|
||||
|
Loading…
Reference in New Issue
Block a user