提交 3f328549 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/jnsnow/tags/bitmaps-pull-request' into staging

Pull request

# gpg: Signature made Mon 29 Oct 2018 21:24:08 GMT
# gpg:                using RSA key 7DEF8106AAFC390E
# gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>"
# 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:
  iotests: 169: add cases for source vm resuming
  iotests: improve 169
  dirty-bitmaps: clean-up bitmaps loading and migration logic
  bitmap: Update count after a merge
  nbd: forbid use of frozen bitmaps
  block/backup: prohibit backup from using in use bitmaps
  block/dirty-bitmaps: prohibit enable/disable on locked/frozen bitmaps
  block/dirty-bitmaps: allow clear on disabled bitmaps
  block/dirty-bitmaps: fix merge permissions
  block/dirty-bitmaps: add user_locked status checker
  bloc/qcow2: drop dirty_bitmaps_loaded state variable
  block/qcow2: improve error message in qcow2_inactivate
  iotests: 169: drop deprecated 'autoload' parameter
  qapi: add transaction support for x-block-dirty-bitmap-merge
  blockdev: rename block-dirty-bitmap-clear transaction handlers
  dirty-bitmap: make it possible to restore bitmap after merge
  dirty-bitmap: rename bdrv_undo_clear_dirty_bitmap
  dirty-bitmap: switch assert-fails to errors in bdrv_merge_dirty_bitmap
  blockdev-backup: add bitmap argument
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
...@@ -4403,6 +4403,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, ...@@ -4403,6 +4403,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
uint64_t perm, shared_perm; uint64_t perm, shared_perm;
Error *local_err = NULL; Error *local_err = NULL;
int ret; int ret;
BdrvDirtyBitmap *bm;
if (!bs->drv) { if (!bs->drv) {
return; return;
...@@ -4452,6 +4453,12 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, ...@@ -4452,6 +4453,12 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
} }
} }
for (bm = bdrv_dirty_bitmap_next(bs, NULL); bm;
bm = bdrv_dirty_bitmap_next(bs, bm))
{
bdrv_dirty_bitmap_set_migration(bm, false);
}
ret = refresh_total_sectors(bs, bs->total_sectors); ret = refresh_total_sectors(bs, bs->total_sectors);
if (ret < 0) { if (ret < 0) {
bs->open_flags |= BDRV_O_INACTIVE; bs->open_flags |= BDRV_O_INACTIVE;
...@@ -4566,10 +4573,6 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs, ...@@ -4566,10 +4573,6 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
} }
} }
/* At this point persistent bitmaps should be already stored by the format
* driver */
bdrv_release_persistent_dirty_bitmaps(bs);
return 0; return 0;
} }
......
...@@ -55,6 +55,10 @@ struct BdrvDirtyBitmap { ...@@ -55,6 +55,10 @@ struct BdrvDirtyBitmap {
and this bitmap must remain unchanged while and this bitmap must remain unchanged while
this flag is set. */ this flag is set. */
bool persistent; /* bitmap must be saved to owner disk image */ bool persistent; /* bitmap must be saved to owner disk image */
bool migration; /* Bitmap is selected for migration, it should
not be stored on the next inactivation
(persistent flag doesn't matter until next
invalidation).*/
QLIST_ENTRY(BdrvDirtyBitmap) list; QLIST_ENTRY(BdrvDirtyBitmap) list;
}; };
...@@ -176,6 +180,12 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) ...@@ -176,6 +180,12 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
return bitmap->successor; 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);
}
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
{ {
qemu_mutex_lock(bitmap->mutex); qemu_mutex_lock(bitmap->mutex);
...@@ -314,7 +324,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, ...@@ -314,7 +324,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
return NULL; return NULL;
} }
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
error_setg(errp, "Merging of parent and successor bitmap failed"); error_setg(errp, "Merging of parent and successor bitmap failed");
return NULL; return NULL;
} }
...@@ -383,26 +393,6 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) ...@@ -383,26 +393,6 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
bdrv_dirty_bitmaps_unlock(bs); bdrv_dirty_bitmaps_unlock(bs);
} }
/**
* Release all persistent dirty bitmaps attached to a BDS (for use in
* bdrv_inactivate_recurse()).
* There must not be any frozen bitmaps attached.
* This function does not remove persistent bitmaps from the storage.
* Called with BQL taken.
*/
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
{
BdrvDirtyBitmap *bm, *next;
bdrv_dirty_bitmaps_lock(bs);
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
if (bdrv_dirty_bitmap_get_persistance(bm)) {
bdrv_release_dirty_bitmap_locked(bm);
}
}
bdrv_dirty_bitmaps_unlock(bs);
}
/** /**
* Remove persistent dirty bitmap from the storage if it exists. * Remove persistent dirty bitmap from the storage if it exists.
* Absence of bitmap is not an error, because we have the following scenario: * Absence of bitmap is not an error, because we have the following scenario:
...@@ -619,7 +609,6 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, ...@@ -619,7 +609,6 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
{ {
assert(bdrv_dirty_bitmap_enabled(bitmap));
assert(!bdrv_dirty_bitmap_readonly(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap));
bdrv_dirty_bitmap_lock(bitmap); bdrv_dirty_bitmap_lock(bitmap);
if (!out) { if (!out) {
...@@ -633,12 +622,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) ...@@ -633,12 +622,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
bdrv_dirty_bitmap_unlock(bitmap); bdrv_dirty_bitmap_unlock(bitmap);
} }
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
{ {
HBitmap *tmp = bitmap->bitmap; HBitmap *tmp = bitmap->bitmap;
assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(bdrv_dirty_bitmap_enabled(bitmap));
assert(!bdrv_dirty_bitmap_readonly(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap));
bitmap->bitmap = in; bitmap->bitmap = backup;
hbitmap_free(tmp); hbitmap_free(tmp);
} }
...@@ -756,16 +745,24 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) ...@@ -756,16 +745,24 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
qemu_mutex_unlock(bitmap->mutex); qemu_mutex_unlock(bitmap->mutex);
} }
/* Called with BQL taken. */
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
{
qemu_mutex_lock(bitmap->mutex);
bitmap->migration = migration;
qemu_mutex_unlock(bitmap->mutex);
}
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
{ {
return bitmap->persistent; return bitmap->persistent && !bitmap->migration;
} }
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
{ {
BdrvDirtyBitmap *bm; BdrvDirtyBitmap *bm;
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
if (bm->persistent && !bm->readonly) { if (bm->persistent && !bm->readonly && !bm->migration) {
return true; return true;
} }
} }
...@@ -791,19 +788,41 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) ...@@ -791,19 +788,41 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
} }
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
Error **errp) HBitmap **backup, Error **errp)
{ {
bool ret;
/* only bitmaps from one bds are supported */ /* only bitmaps from one bds are supported */
assert(dest->mutex == src->mutex); assert(dest->mutex == src->mutex);
qemu_mutex_lock(dest->mutex); qemu_mutex_lock(dest->mutex);
assert(bdrv_dirty_bitmap_enabled(dest)); if (bdrv_dirty_bitmap_user_locked(dest)) {
assert(!bdrv_dirty_bitmap_readonly(dest)); error_setg(errp, "Bitmap '%s' is currently in use by another"
" operation and cannot be modified", dest->name);
goto out;
}
if (bdrv_dirty_bitmap_readonly(dest)) {
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
dest->name);
goto out;
}
if (!hbitmap_merge(dest->bitmap, src->bitmap)) { if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
error_setg(errp, "Bitmaps are incompatible and can't be merged"); error_setg(errp, "Bitmaps are incompatible and can't be merged");
goto out;
}
if (backup) {
*backup = dest->bitmap;
dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
} else {
ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
} }
assert(ret);
out:
qemu_mutex_unlock(dest->mutex); qemu_mutex_unlock(dest->mutex);
} }
...@@ -1418,6 +1418,22 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) ...@@ -1418,6 +1418,22 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
g_free(tb); g_free(tb);
} }
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
/* For safety, we remove bitmap after storing.
* We may be here in two cases:
* 1. bdrv_close. It's ok to drop bitmap.
* 2. inactivation. It means migration without 'dirty-bitmaps'
* capability, so bitmaps are not marked with
* BdrvDirtyBitmap.migration flags. It's not bad to drop them too,
* and reload on invalidation.
*/
if (bm->dirty_bitmap == NULL) {
continue;
}
bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
}
bitmap_list_free(bm_list); bitmap_list_free(bm_list);
return; return;
......
...@@ -1153,7 +1153,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ...@@ -1153,7 +1153,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
uint64_t ext_end; uint64_t ext_end;
uint64_t l1_vm_state_index; uint64_t l1_vm_state_index;
bool update_header = false; bool update_header = false;
bool header_updated = false;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
...@@ -1492,23 +1491,70 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ...@@ -1492,23 +1491,70 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
} }
if (s->dirty_bitmaps_loaded) { /* == Handle persistent dirty bitmaps ==
/* It's some kind of reopen. There are no known cases where we need to *
* reload bitmaps in such a situation, so it's safer to skip them. * We want load dirty bitmaps in three cases:
* *
* Moreover, if we have some readonly bitmaps and we are reopening for * 1. Normal open of the disk in active mode, not related to invalidation
* rw we should reopen bitmaps correspondingly. * after migration.
*/ *
if (bdrv_has_readonly_bitmaps(bs) && * 2. Invalidation of the target vm after pre-copy phase of migration, if
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) * bitmaps are _not_ migrating through migration channel, i.e.
{ * 'dirty-bitmaps' capability is disabled.
qcow2_reopen_bitmaps_rw_hint(bs, &header_updated, &local_err); *
} * 3. Invalidation of source vm after failed or canceled migration.
} else { * This is a very interesting case. There are two possible types of
header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); * bitmaps:
s->dirty_bitmaps_loaded = true; *
* A. Stored on inactivation and removed. They should be loaded from the
* image.
*
* B. Not stored: not-persistent bitmaps and bitmaps, migrated through
* the migration channel (with dirty-bitmaps capability).
*
* On the other hand, there are two possible sub-cases:
*
* 3.1 disk was changed by somebody else while were inactive. In this
* case all in-RAM dirty bitmaps (both persistent and not) are
* definitely invalid. And we don't have any method to determine
* this.
*
* Simple and safe thing is to just drop all the bitmaps of type B on
* inactivation. But in this case we lose bitmaps in valid 4.2 case.
*
* On the other hand, resuming source vm, if disk was already changed
* is a bad thing anyway: not only bitmaps, the whole vm state is
* out of sync with disk.
*
* This means, that user or management tool, who for some reason
* decided to resume source vm, after disk was already changed by
* target vm, should at least drop all dirty bitmaps by hand.
*
* So, we can ignore this case for now, but TODO: "generation"
* extension for qcow2, to determine, that image was changed after
* last inactivation. And if it is changed, we will drop (or at least
* mark as 'invalid' all the bitmaps of type B, both persistent
* and not).
*
* 3.2 disk was _not_ changed while were inactive. Bitmaps may be saved
* to disk ('dirty-bitmaps' capability disabled), or not saved
* ('dirty-bitmaps' capability enabled), but we don't need to care
* of: let's load bitmaps as always: stored bitmaps will be loaded,
* and not stored has flag IN_USE=1 in the image and will be skipped
* on loading.
*
* One remaining possible case when we don't want load bitmaps:
*
* 4. Open disk in inactive mode in target vm (bitmaps are migrating or
* will be loaded on invalidation, no needs try loading them before)
*/
if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) {
/* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */
bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
update_header = update_header && !header_updated;
} }
update_header = update_header && !header_updated;
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
ret = -EINVAL; ret = -EINVAL;
...@@ -2123,9 +2169,9 @@ static int qcow2_inactivate(BlockDriverState *bs) ...@@ -2123,9 +2169,9 @@ static int qcow2_inactivate(BlockDriverState *bs)
qcow2_store_persistent_dirty_bitmaps(bs, &local_err); qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
result = -EINVAL; result = -EINVAL;
error_report_err(local_err); error_reportf_err(local_err, "Lost persistent bitmaps during "
error_report("Persistent bitmaps are lost for node '%s'", "inactivation of node '%s': ",
bdrv_get_device_or_node_name(bs)); bdrv_get_device_or_node_name(bs));
} }
ret = qcow2_cache_flush(bs, s->l2_table_cache); ret = qcow2_cache_flush(bs, s->l2_table_cache);
......
...@@ -300,7 +300,6 @@ typedef struct BDRVQcow2State { ...@@ -300,7 +300,6 @@ typedef struct BDRVQcow2State {
uint32_t nb_bitmaps; uint32_t nb_bitmaps;
uint64_t bitmap_directory_size; uint64_t bitmap_directory_size;
uint64_t bitmap_directory_offset; uint64_t bitmap_directory_offset;
bool dirty_bitmaps_loaded;
int flags; int flags;
int qcow_version; int qcow_version;
......
...@@ -2010,14 +2010,8 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, ...@@ -2010,14 +2010,8 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
return; return;
} }
if (bdrv_dirty_bitmap_frozen(state->bitmap)) { if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
error_setg(errp, "Cannot modify a frozen bitmap"); error_setg(errp, "Cannot modify a bitmap in use by another operation");
return;
} else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) {
error_setg(errp, "Cannot modify a locked bitmap");
return;
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
error_setg(errp, "Cannot clear a disabled bitmap");
return; return;
} else if (bdrv_dirty_bitmap_readonly(state->bitmap)) { } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
error_setg(errp, "Cannot clear a readonly bitmap"); error_setg(errp, "Cannot clear a readonly bitmap");
...@@ -2027,17 +2021,17 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, ...@@ -2027,17 +2021,17 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
} }
static void block_dirty_bitmap_clear_abort(BlkActionState *common) static void block_dirty_bitmap_restore(BlkActionState *common)
{ {
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common); common, common);
if (state->backup) { if (state->backup) {
bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup); bdrv_restore_dirty_bitmap(state->bitmap, state->backup);
} }
} }
static void block_dirty_bitmap_clear_commit(BlkActionState *common) static void block_dirty_bitmap_free_backup(BlkActionState *common)
{ {
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common); common, common);
...@@ -2065,6 +2059,13 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, ...@@ -2065,6 +2059,13 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
return; 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);
return;
}
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
bdrv_enable_dirty_bitmap(state->bitmap); bdrv_enable_dirty_bitmap(state->bitmap);
} }
...@@ -2099,6 +2100,13 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, ...@@ -2099,6 +2100,13 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
return; 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);
return;
}
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
bdrv_disable_dirty_bitmap(state->bitmap); bdrv_disable_dirty_bitmap(state->bitmap);
} }
...@@ -2113,6 +2121,35 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common) ...@@ -2113,6 +2121,35 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common)
} }
} }
static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
Error **errp)
{
BlockDirtyBitmapMerge *action;
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
BdrvDirtyBitmap *merge_source;
if (action_check_completion_mode(common, errp) < 0) {
return;
}
action = common->action->u.x_block_dirty_bitmap_merge.data;
state->bitmap = block_dirty_bitmap_lookup(action->node,
action->dst_name,
&state->bs,
errp);
if (!state->bitmap) {
return;
}
merge_source = bdrv_find_dirty_bitmap(state->bs, action->src_name);
if (!merge_source) {
return;
}
bdrv_merge_dirty_bitmap(state->bitmap, merge_source, &state->backup, errp);
}
static void abort_prepare(BlkActionState *common, Error **errp) static void abort_prepare(BlkActionState *common, Error **errp)
{ {
error_setg(errp, "Transaction aborted using Abort action"); error_setg(errp, "Transaction aborted using Abort action");
...@@ -2171,8 +2208,8 @@ static const BlkActionOps actions[] = { ...@@ -2171,8 +2208,8 @@ static const BlkActionOps actions[] = {
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = { [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
.instance_size = sizeof(BlockDirtyBitmapState), .instance_size = sizeof(BlockDirtyBitmapState),
.prepare = block_dirty_bitmap_clear_prepare, .prepare = block_dirty_bitmap_clear_prepare,
.commit = block_dirty_bitmap_clear_commit, .commit = block_dirty_bitmap_free_backup,
.abort = block_dirty_bitmap_clear_abort, .abort = block_dirty_bitmap_restore,
}, },
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = {
.instance_size = sizeof(BlockDirtyBitmapState), .instance_size = sizeof(BlockDirtyBitmapState),
...@@ -2184,6 +2221,12 @@ static const BlkActionOps actions[] = { ...@@ -2184,6 +2221,12 @@ static const BlkActionOps actions[] = {
.prepare = block_dirty_bitmap_disable_prepare, .prepare = block_dirty_bitmap_disable_prepare,
.abort = block_dirty_bitmap_disable_abort, .abort = block_dirty_bitmap_disable_abort,
}, },
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_MERGE] = {
.instance_size = sizeof(BlockDirtyBitmapState),
.prepare = block_dirty_bitmap_merge_prepare,
.commit = block_dirty_bitmap_free_backup,
.abort = block_dirty_bitmap_restore,
},
/* Where are transactions for MIRROR, COMMIT and STREAM? /* Where are transactions for MIRROR, COMMIT and STREAM?
* Although these blockjobs use transaction callbacks like the backup job, * Although these blockjobs use transaction callbacks like the backup job,
* these jobs do not necessarily adhere to transaction semantics. * these jobs do not necessarily adhere to transaction semantics.
...@@ -2848,15 +2891,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, ...@@ -2848,15 +2891,10 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
return; return;
} }
if (bdrv_dirty_bitmap_frozen(bitmap)) { if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp, error_setg(errp,
"Bitmap '%s' is currently frozen and cannot be removed", "Bitmap '%s' is currently in use by another operation and"
name); " cannot be removed", name);
return;
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
error_setg(errp,
"Bitmap '%s' is currently locked and cannot be removed",
name);
return; return;
} }
...@@ -2886,20 +2924,10 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, ...@@ -2886,20 +2924,10 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
return; return;
} }
if (bdrv_dirty_bitmap_frozen(bitmap)) { if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp,
"Bitmap '%s' is currently frozen and cannot be modified",
name);
return;
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
error_setg(errp,
"Bitmap '%s' is currently locked and cannot be modified",
name);
return;
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
error_setg(errp, error_setg(errp,
"Bitmap '%s' is currently disabled and cannot be cleared", "Bitmap '%s' is currently in use by another operation"
name); " and cannot be cleared", name);
return; return;
} else if (bdrv_dirty_bitmap_readonly(bitmap)) { } else if (bdrv_dirty_bitmap_readonly(bitmap)) {
error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name); error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
...@@ -2920,10 +2948,10 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, ...@@ -2920,10 +2948,10 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name,
return; return;
} }
if (bdrv_dirty_bitmap_frozen(bitmap)) { if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp, error_setg(errp,
"Bitmap '%s' is currently frozen and cannot be enabled", "Bitmap '%s' is currently in use by another operation"
name); " and cannot be enabled", name);
return; return;
} }
...@@ -2941,10 +2969,10 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, ...@@ -2941,10 +2969,10 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name,
return; return;
} }
if (bdrv_dirty_bitmap_frozen(bitmap)) { if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp, error_setg(errp,
"Bitmap '%s' is currently frozen and cannot be disabled", "Bitmap '%s' is currently in use by another operation"
name); " and cannot be disabled", name);
return; return;
} }
...@@ -2962,23 +2990,13 @@ void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, ...@@ -2962,23 +2990,13 @@ void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name,
return; return;
} }
if (bdrv_dirty_bitmap_frozen(dst)) {
error_setg(errp, "Bitmap '%s' is frozen and cannot be modified",
dst_name);
return;
} else if (bdrv_dirty_bitmap_readonly(dst)) {
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
dst_name);
return;
}
src = bdrv_find_dirty_bitmap(bs, src_name); src = bdrv_find_dirty_bitmap(bs, src_name);
if (!src) { if (!src) {
error_setg(errp, "Dirty bitmap '%s' not found", src_name); error_setg(errp, "Dirty bitmap '%s' not found", src_name);
return; return;
} }
bdrv_merge_dirty_bitmap(dst, src, errp); bdrv_merge_dirty_bitmap(dst, src, NULL, errp);
} }
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
...@@ -3495,10 +3513,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, ...@@ -3495,10 +3513,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
bdrv_unref(target_bs); bdrv_unref(target_bs);
goto out; goto out;
} }
if (bdrv_dirty_bitmap_qmp_locked(bmap)) { if (bdrv_dirty_bitmap_user_locked(bmap)) {
error_setg(errp, error_setg(errp,
"Bitmap '%s' is currently locked and cannot be used for " "Bitmap '%s' is currently in use by another operation"
"backup", backup->bitmap); " and cannot be used for backup", backup->bitmap);
goto out; goto out;
} }
} }
...@@ -3545,6 +3563,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, ...@@ -3545,6 +3563,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
BlockDriverState *bs; BlockDriverState *bs;
BlockDriverState *target_bs; BlockDriverState *target_bs;
Error *local_err = NULL; Error *local_err = NULL;
BdrvDirtyBitmap *bmap = NULL;
AioContext *aio_context; AioContext *aio_context;
BlockJob *job = NULL; BlockJob *job = NULL;
int job_flags = JOB_DEFAULT; int job_flags = JOB_DEFAULT;
...@@ -3595,6 +3614,21 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, ...@@ -3595,6 +3614,21 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
goto out; goto out;
} }
} }
if (backup->has_bitmap) {
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
if (!bmap) {
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);
goto out;
}
}
if (!backup->auto_finalize) { if (!backup->auto_finalize) {
job_flags |= JOB_MANUAL_FINALIZE; job_flags |= JOB_MANUAL_FINALIZE;
} }
...@@ -3602,7 +3636,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, ...@@ -3602,7 +3636,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
job_flags |= JOB_MANUAL_DISMISS; job_flags |= JOB_MANUAL_DISMISS;
} }
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
backup->sync, NULL, backup->compress, backup->sync, bmap, backup->compress,
backup->on_source_error, backup->on_target_error, backup->on_source_error, backup->on_target_error,
job_flags, NULL, NULL, txn, &local_err); job_flags, NULL, NULL, txn, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
......
...@@ -1155,7 +1155,7 @@ bool blk_dev_is_medium_locked(BlockBackend *blk); ...@@ -1155,7 +1155,7 @@ bool blk_dev_is_medium_locked(BlockBackend *blk);
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup);
void bdrv_inc_in_flight(BlockDriverState *bs); void bdrv_inc_in_flight(BlockDriverState *bs);
void bdrv_dec_in_flight(BlockDriverState *bs); void bdrv_dec_in_flight(BlockDriverState *bs);
......
...@@ -26,7 +26,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, ...@@ -26,7 +26,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs,
const char *name); const char *name);
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap);
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs);
void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
const char *name, const char *name,
Error **errp); Error **errp);
...@@ -71,7 +70,8 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, ...@@ -71,7 +70,8 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap,
bool persistent); bool persistent);
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked);
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
Error **errp); HBitmap **backup, Error **errp);
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration);
/* Functions that require manual locking. */ /* Functions that require manual locking. */
void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap);
...@@ -94,6 +94,7 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); ...@@ -94,6 +94,7 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs);
bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap);
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap);
bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap);
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap);
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
BdrvDirtyBitmap *bitmap); BdrvDirtyBitmap *bitmap);
......
...@@ -73,16 +73,23 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size); ...@@ -73,16 +73,23 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size);
/** /**
* hbitmap_merge: * hbitmap_merge:
* @a: The bitmap to store the result in. *
* @b: The bitmap to merge into @a. * Store result of merging @a and @b into @result.
* @return true if the merge was successful, * @result is allowed to be equal to @a or @b.
* false if it was not attempted. *
* * Return true if the merge was successful,
* Merge two bitmaps together. * false if it was not attempted.
* A := A (BITOR) B. */
* B is left unmodified. bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result);
/**
* hbitmap_can_merge:
*
* hbitmap_can_merge(a, b) && hbitmap_can_merge(a, result) is sufficient and
* necessary for hbitmap_merge will not fail.
*
*/ */
bool hbitmap_merge(HBitmap *a, const HBitmap *b); bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b);
/** /**
* hbitmap_empty: * hbitmap_empty:
......
...@@ -301,14 +301,14 @@ static int init_dirty_bitmap_migration(void) ...@@ -301,14 +301,14 @@ static int init_dirty_bitmap_migration(void)
goto fail; goto fail;
} }
if (bdrv_dirty_bitmap_frozen(bitmap)) { if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_report("Can't migrate frozen dirty bitmap: '%s", error_report("Can't migrate a bitmap that is in use by another operation: '%s'",
bdrv_dirty_bitmap_name(bitmap)); bdrv_dirty_bitmap_name(bitmap));
goto fail; goto fail;
} }
if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { if (bdrv_dirty_bitmap_readonly(bitmap)) {
error_report("Can't migrate locked dirty bitmap: '%s", error_report("Can't migrate read-only dirty bitmap: '%s",
bdrv_dirty_bitmap_name(bitmap)); bdrv_dirty_bitmap_name(bitmap));
goto fail; goto fail;
} }
...@@ -335,9 +335,9 @@ static int init_dirty_bitmap_migration(void) ...@@ -335,9 +335,9 @@ static int init_dirty_bitmap_migration(void)
} }
} }
/* unset persistance here, to not roll back it */ /* unset migration flags here, to not roll back it */
QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
bdrv_dirty_bitmap_set_persistance(dbms->bitmap, false); bdrv_dirty_bitmap_set_migration(dbms->bitmap, true);
} }
if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) { if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) {
......
...@@ -2456,8 +2456,8 @@ void nbd_export_bitmap(NBDExport *exp, const char *bitmap, ...@@ -2456,8 +2456,8 @@ void nbd_export_bitmap(NBDExport *exp, const char *bitmap,
return; return;
} }
if (bdrv_dirty_bitmap_qmp_locked(bm)) { if (bdrv_dirty_bitmap_user_locked(bm)) {
error_setg(errp, "Bitmap '%s' is locked", bitmap); error_setg(errp, "Bitmap '%s' is in use", bitmap);
return; return;
} }
......
...@@ -1316,6 +1316,10 @@ ...@@ -1316,6 +1316,10 @@
# @speed: the maximum speed, in bytes per second. The default is 0, # @speed: the maximum speed, in bytes per second. The default is 0,
# for unlimited. # for unlimited.
# #
# @bitmap: the name of dirty bitmap if sync is "incremental".
# Must be present if sync is "incremental", must NOT be present
# otherwise. (Since 3.1)
#
# @compress: true to compress data, if the target format supports it. # @compress: true to compress data, if the target format supports it.
# (default: false) (since 2.8) # (default: false) (since 2.8)
# #
...@@ -1348,7 +1352,8 @@ ...@@ -1348,7 +1352,8 @@
## ##
{ 'struct': 'BlockdevBackup', { 'struct': 'BlockdevBackup',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'sync': 'MirrorSyncMode', '*speed': 'int', '*compress': 'bool', 'sync': 'MirrorSyncMode', '*speed': 'int',
'*bitmap': 'str', '*compress': 'bool',
'*on-source-error': 'BlockdevOnError', '*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
# - @block-dirty-bitmap-clear: since 2.5 # - @block-dirty-bitmap-clear: since 2.5
# - @x-block-dirty-bitmap-enable: since 3.0 # - @x-block-dirty-bitmap-enable: since 3.0
# - @x-block-dirty-bitmap-disable: since 3.0 # - @x-block-dirty-bitmap-disable: since 3.0
# - @x-block-dirty-bitmap-merge: since 3.1
# - @blockdev-backup: since 2.3 # - @blockdev-backup: since 2.3
# - @blockdev-snapshot: since 2.5 # - @blockdev-snapshot: since 2.5
# - @blockdev-snapshot-internal-sync: since 1.7 # - @blockdev-snapshot-internal-sync: since 1.7
...@@ -63,6 +64,7 @@ ...@@ -63,6 +64,7 @@
'block-dirty-bitmap-clear': 'BlockDirtyBitmap', 'block-dirty-bitmap-clear': 'BlockDirtyBitmap',
'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap',
'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap',
'x-block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge',
'blockdev-backup': 'BlockdevBackup', 'blockdev-backup': 'BlockdevBackup',
'blockdev-snapshot': 'BlockdevSnapshot', 'blockdev-snapshot': 'BlockdevSnapshot',
'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal',
......
...@@ -24,6 +24,7 @@ import time ...@@ -24,6 +24,7 @@ import time
import itertools import itertools
import operator import operator
import new import new
import re
from iotests import qemu_img from iotests import qemu_img
...@@ -58,7 +59,6 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): ...@@ -58,7 +59,6 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
'granularity': granularity} 'granularity': granularity}
if persistent: if persistent:
params['persistent'] = True params['persistent'] = True
params['autoload'] = True
result = vm.qmp('block-dirty-bitmap-add', **params) result = vm.qmp('block-dirty-bitmap-add', **params)
self.assert_qmp(result, 'return', {}); self.assert_qmp(result, 'return', {});
...@@ -77,6 +77,58 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): ...@@ -77,6 +77,58 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
self.assert_qmp(result, 'error/desc', self.assert_qmp(result, 'error/desc',
"Dirty bitmap 'bitmap0' not found"); "Dirty bitmap 'bitmap0' not found");
def do_test_migration_resume_source(self, persistent, migrate_bitmaps):
granularity = 512
# regions = ((start, count), ...)
regions = ((0, 0x10000),
(0xf0000, 0x10000),
(0xa0201, 0x1000))
mig_caps = [{'capability': 'events', 'state': True}]
if migrate_bitmaps:
mig_caps.append({'capability': 'dirty-bitmaps', 'state': True})
result = self.vm_a.qmp('migrate-set-capabilities',
capabilities=mig_caps)
self.assert_qmp(result, 'return', {})
self.add_bitmap(self.vm_a, granularity, persistent)
for r in regions:
self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r)
sha256 = self.get_bitmap_hash(self.vm_a)
result = self.vm_a.qmp('migrate', uri=mig_cmd)
while True:
event = self.vm_a.event_wait('MIGRATION')
if event['data']['status'] == 'completed':
break
# test that bitmap is still here
removed = (not migrate_bitmaps) and persistent
self.check_bitmap(self.vm_a, False if removed else sha256)
self.vm_a.qmp('cont')
# test that bitmap is still here after invalidation
self.check_bitmap(self.vm_a, sha256)
# shutdown and check that invalidation didn't fail
self.vm_a.shutdown()
# catch 'Could not reopen qcow2 layer: Bitmap already exists'
# possible error
log = self.vm_a.get_log()
log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}',
'', log)
log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
self.assertEqual(log, '')
# test that bitmap is still persistent
self.vm_a.launch()
self.check_bitmap(self.vm_a, sha256 if persistent else False)
def do_test_migration(self, persistent, migrate_bitmaps, online, def do_test_migration(self, persistent, migrate_bitmaps, online,
shared_storage): shared_storage):
granularity = 512 granularity = 512
...@@ -134,6 +186,14 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): ...@@ -134,6 +186,14 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
if should_migrate: if should_migrate:
self.vm_b.shutdown() self.vm_b.shutdown()
# catch 'Could not reopen qcow2 layer: Bitmap already exists'
# possible error
log = self.vm_b.get_log()
log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
self.assertEqual(log, '')
# recreate vm_b, as we don't want -incoming option (this will lead # recreate vm_b, as we don't want -incoming option (this will lead
# to "cat" process left alive after test finish) # to "cat" process left alive after test finish)
self.vm_b = iotests.VM(path_suffix='b') self.vm_b = iotests.VM(path_suffix='b')
...@@ -144,7 +204,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): ...@@ -144,7 +204,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase):
def inject_test_case(klass, name, method, *args, **kwargs): def inject_test_case(klass, name, method, *args, **kwargs):
mc = operator.methodcaller(method, *args, **kwargs) mc = operator.methodcaller(method, *args, **kwargs)
setattr(klass, 'test_' + name, new.instancemethod(mc, None, klass)) setattr(klass, 'test_' + method + name, new.instancemethod(mc, None, klass))
for cmb in list(itertools.product((True, False), repeat=4)): for cmb in list(itertools.product((True, False), repeat=4)):
name = ('_' if cmb[0] else '_not_') + 'persistent_' name = ('_' if cmb[0] else '_not_') + 'persistent_'
...@@ -155,6 +215,12 @@ for cmb in list(itertools.product((True, False), repeat=4)): ...@@ -155,6 +215,12 @@ for cmb in list(itertools.product((True, False), repeat=4)):
inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration',
*list(cmb)) *list(cmb))
for cmb in list(itertools.product((True, False), repeat=2)):
name = ('_' if cmb[0] else '_not_') + 'persistent_'
name += ('_' if cmb[1] else '_not_') + 'migbitmap'
inject_test_case(TestDirtyBitmapMigration, name,
'do_test_migration_resume_source', *list(cmb))
if __name__ == '__main__': if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2']) iotests.main(supported_fmts=['qcow2'])
................ ....................
---------------------------------------------------------------------- ----------------------------------------------------------------------
Ran 16 tests Ran 20 tests
OK OK
...@@ -723,6 +723,10 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) ...@@ -723,6 +723,10 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
} }
} }
bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b)
{
return (a->size == b->size) && (a->granularity == b->granularity);
}
/** /**
* Given HBitmaps A and B, let A := A (BITOR) B. * Given HBitmaps A and B, let A := A (BITOR) B.
...@@ -731,14 +735,15 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) ...@@ -731,14 +735,15 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
* @return true if the merge was successful, * @return true if the merge was successful,
* false if it was not attempted. * false if it was not attempted.
*/ */
bool hbitmap_merge(HBitmap *a, const HBitmap *b) bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
{ {
int i; int i;
uint64_t j; uint64_t j;
if ((a->size != b->size) || (a->granularity != b->granularity)) { if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) {
return false; return false;
} }
assert(hbitmap_can_merge(b, result));
if (hbitmap_count(b) == 0) { if (hbitmap_count(b) == 0) {
return true; return true;
...@@ -750,10 +755,13 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b) ...@@ -750,10 +755,13 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b)
*/ */
for (i = HBITMAP_LEVELS - 1; i >= 0; i--) { for (i = HBITMAP_LEVELS - 1; i >= 0; i--) {
for (j = 0; j < a->sizes[i]; j++) { for (j = 0; j < a->sizes[i]; j++) {
a->levels[i][j] |= b->levels[i][j]; result->levels[i][j] = a->levels[i][j] | b->levels[i][j];
} }
} }
/* Recompute the dirty count */
result->count = hb_count_between(result, 0, result->size - 1);
return true; return true;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册