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:
Peter Maydell 2019-03-13 17:30:34 +00:00
commit 523a2a42c3
17 changed files with 872 additions and 196 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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);
}

View File

@ -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:

View File

@ -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.

View File

@ -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'])

View File

@ -1,5 +1,5 @@
...........
............
----------------------------------------------------------------------
Ran 11 tests
Ran 12 tests
OK

View File

@ -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
View 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
View 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 ---

View File

@ -244,3 +244,4 @@
243 rw auto quick
244 rw auto quick
245 rw auto
246 rw auto quick