mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/jnsnow/tags/bitmaps-pull-request' into staging
pull request # gpg: Signature made Thu 17 Oct 2019 22:54:14 BST # 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: dirty-bitmaps: remove deprecated autoload parameter MAINTAINERS: Add Vladimir as a reviewer for bitmaps qcow2-bitmap: move bitmap reopen-rw code to qcow2_reopen_commit block/qcow2-bitmap: fix and improve qcow2_reopen_bitmaps_rw iotests: add test 260 to check bitmap life after snapshot + commit block/qcow2-bitmap: do not remove bitmaps on reopen-ro block/qcow2-bitmap: drop qcow2_reopen_bitmaps_rw_hint() block/qcow2-bitmap: get rid of bdrv_has_changed_persistent_bitmaps iotests: add test-case to 165 to test reopening qcow2 bitmaps to RW block: reverse order for reopen commits block: switch reopen queue from QSIMPLEQ to QTAILQ block/dirty-bitmap: refactor bdrv_dirty_bitmap_next block/dirty-bitmap: drop BdrvDirtyBitmap.mutex block/dirty-bitmap: add bs link block/dirty-bitmap: drop meta block/qcow2: proper locking on bitmap add/remove paths block/dirty-bitmap: return int from bdrv_remove_persistent_dirty_bitmap block: move bdrv_can_store_new_dirty_bitmap to block/dirty-bitmap.c util/hbitmap: strict hbitmap_reset Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+1
-2
@@ -1816,8 +1816,8 @@ F: qapi/transaction.json
|
||||
T: git https://repo.or.cz/qemu/armbru.git block-next
|
||||
|
||||
Dirty Bitmaps
|
||||
M: Fam Zheng <fam@euphon.net>
|
||||
M: John Snow <jsnow@redhat.com>
|
||||
R: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: util/hbitmap.c
|
||||
@@ -1826,7 +1826,6 @@ F: include/qemu/hbitmap.h
|
||||
F: include/block/dirty-bitmap.h
|
||||
F: tests/test-hbitmap.c
|
||||
F: docs/interop/bitmaps.rst
|
||||
T: git https://github.com/famz/qemu.git bitmaps
|
||||
T: git https://github.com/jnsnow/qemu.git bitmaps
|
||||
|
||||
Character device backends
|
||||
|
||||
@@ -1719,7 +1719,7 @@ typedef struct BlockReopenQueueEntry {
|
||||
bool prepared;
|
||||
bool perms_checked;
|
||||
BDRVReopenState state;
|
||||
QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
|
||||
QTAILQ_ENTRY(BlockReopenQueueEntry) entry;
|
||||
} BlockReopenQueueEntry;
|
||||
|
||||
/*
|
||||
@@ -1732,7 +1732,7 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
|
||||
BlockReopenQueueEntry *entry;
|
||||
|
||||
if (q != NULL) {
|
||||
QSIMPLEQ_FOREACH(entry, q, entry) {
|
||||
QTAILQ_FOREACH(entry, q, entry) {
|
||||
if (entry->state.bs == bs) {
|
||||
return entry->state.flags;
|
||||
}
|
||||
@@ -3249,7 +3249,7 @@ static bool bdrv_recurse_has_child(BlockDriverState *bs,
|
||||
* Adds a BlockDriverState to a simple queue for an atomic, transactional
|
||||
* reopen of multiple devices.
|
||||
*
|
||||
* bs_queue can either be an existing BlockReopenQueue that has had QSIMPLE_INIT
|
||||
* bs_queue can either be an existing BlockReopenQueue that has had QTAILQ_INIT
|
||||
* already performed, or alternatively may be NULL a new BlockReopenQueue will
|
||||
* be created and initialized. This newly created BlockReopenQueue should be
|
||||
* passed back in for subsequent calls that are intended to be of the same
|
||||
@@ -3290,7 +3290,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
|
||||
if (bs_queue == NULL) {
|
||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||
QSIMPLEQ_INIT(bs_queue);
|
||||
QTAILQ_INIT(bs_queue);
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
@@ -3298,7 +3298,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
}
|
||||
|
||||
/* Check if this BlockDriverState is already in the queue */
|
||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
if (bs == bs_entry->state.bs) {
|
||||
break;
|
||||
}
|
||||
@@ -3354,7 +3354,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
|
||||
if (!bs_entry) {
|
||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
||||
QTAILQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
||||
} else {
|
||||
qobject_unref(bs_entry->state.options);
|
||||
qobject_unref(bs_entry->state.explicit_options);
|
||||
@@ -3455,7 +3455,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||
|
||||
assert(bs_queue != NULL);
|
||||
|
||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
assert(bs_entry->state.bs->quiesce_counter > 0);
|
||||
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
|
||||
goto cleanup;
|
||||
@@ -3463,7 +3463,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||
bs_entry->prepared = true;
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
BDRVReopenState *state = &bs_entry->state;
|
||||
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
|
||||
state->shared_perm, NULL, NULL, errp);
|
||||
@@ -3486,16 +3486,22 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||
bs_entry->perms_checked = true;
|
||||
}
|
||||
|
||||
/* If we reach this point, we have success and just need to apply the
|
||||
* changes
|
||||
/*
|
||||
* If we reach this point, we have success and just need to apply the
|
||||
* changes.
|
||||
*
|
||||
* Reverse order is used to comfort qcow2 driver: on commit it need to write
|
||||
* IN_USE flag to the image, to mark bitmaps in the image as invalid. But
|
||||
* children are usually goes after parents in reopen-queue, so go from last
|
||||
* to first element.
|
||||
*/
|
||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
|
||||
bdrv_reopen_commit(&bs_entry->state);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup_perm:
|
||||
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
BDRVReopenState *state = &bs_entry->state;
|
||||
|
||||
if (!bs_entry->perms_checked) {
|
||||
@@ -3512,7 +3518,7 @@ cleanup_perm:
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
if (ret) {
|
||||
if (bs_entry->prepared) {
|
||||
bdrv_reopen_abort(&bs_entry->state);
|
||||
@@ -3552,7 +3558,7 @@ static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q,
|
||||
{
|
||||
BlockReopenQueueEntry *entry;
|
||||
|
||||
QSIMPLEQ_FOREACH(entry, q, entry) {
|
||||
QTAILQ_FOREACH(entry, q, entry) {
|
||||
BlockDriverState *bs = entry->state.bs;
|
||||
BdrvChild *child;
|
||||
|
||||
@@ -3929,16 +3935,12 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
BlockDriver *drv;
|
||||
BlockDriverState *bs;
|
||||
BdrvChild *child;
|
||||
bool old_can_write, new_can_write;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
bs = reopen_state->bs;
|
||||
drv = bs->drv;
|
||||
assert(drv != NULL);
|
||||
|
||||
old_can_write =
|
||||
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
|
||||
|
||||
/* If there are any driver level actions to take */
|
||||
if (drv->bdrv_reopen_commit) {
|
||||
drv->bdrv_reopen_commit(reopen_state);
|
||||
@@ -3982,21 +3984,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
}
|
||||
|
||||
bdrv_refresh_limits(bs, NULL);
|
||||
|
||||
new_can_write =
|
||||
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
|
||||
if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) {
|
||||
Error *local_err = NULL;
|
||||
if (drv->bdrv_reopen_bitmaps_rw(bs, &local_err) < 0) {
|
||||
/* This is not fatal, bitmaps just left read-only, so all following
|
||||
* writes will fail. User can remove read-only bitmaps to unblock
|
||||
* writes.
|
||||
*/
|
||||
error_reportf_err(local_err,
|
||||
"%s: Failed to make dirty bitmaps writable: ",
|
||||
bdrv_get_node_name(bs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5390,9 +5377,7 @@ 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))
|
||||
{
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bm) {
|
||||
bdrv_dirty_bitmap_skip_store(bm, false);
|
||||
}
|
||||
|
||||
@@ -6582,25 +6567,3 @@ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp)
|
||||
|
||||
parent_bs->drv->bdrv_del_child(parent_bs, child, errp);
|
||||
}
|
||||
|
||||
bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
uint32_t granularity, Error **errp)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
if (!drv) {
|
||||
error_setg_errno(errp, ENOMEDIUM,
|
||||
"Can't store persistent bitmaps to %s",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!drv->bdrv_can_store_new_dirty_bitmap) {
|
||||
error_setg_errno(errp, ENOTSUP,
|
||||
"Can't store persistent bitmaps to %s",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
return false;
|
||||
}
|
||||
|
||||
return drv->bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
||||
}
|
||||
|
||||
+4
-4
@@ -98,13 +98,13 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
* We succeeded, or we always intended to sync the bitmap.
|
||||
* Delete this bitmap and install the child.
|
||||
*/
|
||||
bm = bdrv_dirty_bitmap_abdicate(job->source_bs, job->sync_bitmap, NULL);
|
||||
bm = bdrv_dirty_bitmap_abdicate(job->sync_bitmap, NULL);
|
||||
} else {
|
||||
/*
|
||||
* We failed, or we never intended to sync the bitmap anyway.
|
||||
* Merge the successor back into the parent, keeping all data.
|
||||
*/
|
||||
bm = bdrv_reclaim_dirty_bitmap(job->source_bs, job->sync_bitmap, NULL);
|
||||
bm = bdrv_reclaim_dirty_bitmap(job->sync_bitmap, NULL);
|
||||
}
|
||||
|
||||
assert(bm);
|
||||
@@ -402,7 +402,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Create a new bitmap, and freeze/disable this one. */
|
||||
if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) {
|
||||
if (bdrv_dirty_bitmap_create_successor(sync_bitmap, errp) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -472,7 +472,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
|
||||
error:
|
||||
if (sync_bitmap) {
|
||||
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
|
||||
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
|
||||
}
|
||||
if (job) {
|
||||
backup_clean(&job->common.job);
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ void block_copy_state_free(BlockCopyState *s)
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_release_dirty_bitmap(s->source->bs, s->copy_bitmap);
|
||||
bdrv_release_dirty_bitmap(s->copy_bitmap);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
|
||||
+171
-119
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -638,7 +638,7 @@ static int mirror_exit_common(Job *job)
|
||||
bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
|
||||
}
|
||||
|
||||
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
||||
bdrv_release_dirty_bitmap(s->dirty_bitmap);
|
||||
|
||||
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
|
||||
* before we can call bdrv_drained_end */
|
||||
@@ -1709,7 +1709,7 @@ fail:
|
||||
blk_unref(s->target);
|
||||
bs_opaque->job = NULL;
|
||||
if (s->dirty_bitmap) {
|
||||
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
|
||||
bdrv_release_dirty_bitmap(s->dirty_bitmap);
|
||||
}
|
||||
job_early_fail(&s->common.job);
|
||||
}
|
||||
|
||||
+133
-79
@@ -374,7 +374,7 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
|
||||
fail:
|
||||
g_free(bitmap_table);
|
||||
if (bitmap != NULL) {
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
bdrv_release_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -941,7 +941,7 @@ fail:
|
||||
static void release_dirty_bitmap_helper(gpointer bitmap,
|
||||
gpointer bs)
|
||||
{
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
bdrv_release_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
|
||||
@@ -1102,29 +1102,20 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
return list;
|
||||
}
|
||||
|
||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
Error **errp)
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2BitmapList *bm_list;
|
||||
Qcow2Bitmap *bm;
|
||||
GSList *ro_dirty_bitmaps = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (header_updated != NULL) {
|
||||
*header_updated = false;
|
||||
}
|
||||
int ret = -EINVAL;
|
||||
bool need_header_update = false;
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
/* No bitmaps - nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!can_write(bs)) {
|
||||
error_setg(errp, "Can't write to the image on reopening bitmaps rw");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
if (bm_list == NULL) {
|
||||
@@ -1133,35 +1124,75 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
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;
|
||||
if (!bitmap) {
|
||||
error_setg(errp, "Unexpected bitmap '%s' in image '%s'",
|
||||
bm->name, bs->filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||
if (!(bm->flags & BME_FLAG_IN_USE)) {
|
||||
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_setg(errp, "Corruption: bitmap '%s' is not marked IN_USE "
|
||||
"in the image '%s' and not marked readonly in RAM",
|
||||
bm->name, bs->filename);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_inconsistent(bitmap)) {
|
||||
error_setg(errp, "Corruption: bitmap '%s' is inconsistent but "
|
||||
"is not marked IN_USE in the image '%s'", bm->name,
|
||||
bs->filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
bm->flags |= BME_FLAG_IN_USE;
|
||||
need_header_update = true;
|
||||
} else {
|
||||
/*
|
||||
* What if flags already has BME_FLAG_IN_USE ?
|
||||
*
|
||||
* 1. if we are reopening RW -> RW it's OK, of course.
|
||||
* 2. if we are reopening RO -> RW:
|
||||
* 2.1 if @bitmap is inconsistent, it's OK. It means that it was
|
||||
* inconsistent (IN_USE) when we loaded it
|
||||
* 2.2 if @bitmap is not inconsistent. This seems to be impossible
|
||||
* and implies third party interaction. Let's error-out for
|
||||
* safety.
|
||||
*/
|
||||
if (bdrv_dirty_bitmap_readonly(bitmap) &&
|
||||
!bdrv_dirty_bitmap_inconsistent(bitmap))
|
||||
{
|
||||
error_setg(errp, "Corruption: bitmap '%s' is marked IN_USE "
|
||||
"in the image '%s' but it is readonly and "
|
||||
"consistent in RAM",
|
||||
bm->name, bs->filename);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
if (ro_dirty_bitmaps != NULL) {
|
||||
if (need_header_update) {
|
||||
if (!can_write(bs->file->bs) || !(bs->file->perm & BLK_PERM_WRITE)) {
|
||||
error_setg(errp, "Failed to reopen bitmaps rw: no write access "
|
||||
"the protocol file");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* in_use flags must be updated */
|
||||
ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
error_setg_errno(errp, -ret, "Cannot update bitmap directory");
|
||||
goto out;
|
||||
}
|
||||
if (header_updated != NULL) {
|
||||
*header_updated = true;
|
||||
}
|
||||
g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
|
||||
}
|
||||
|
||||
g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
g_slist_free(ro_dirty_bitmaps);
|
||||
bitmap_list_free(bm_list);
|
||||
@@ -1169,11 +1200,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -1404,30 +1430,34 @@ static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2Bitmap *bm;
|
||||
Qcow2Bitmap *bm = NULL;
|
||||
Qcow2BitmapList *bm_list;
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
/* Absence of the bitmap is not an error: see explanation above
|
||||
* bdrv_remove_persistent_dirty_bitmap() definition. */
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
if (bm_list == NULL) {
|
||||
return;
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bm = find_bitmap_by_name(bm_list, name);
|
||||
if (bm == NULL) {
|
||||
goto fail;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry);
|
||||
@@ -1435,17 +1465,46 @@ void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
ret = update_ext_header_and_dir(bs, bm_list);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to update bitmap extension");
|
||||
goto fail;
|
||||
goto out;
|
||||
}
|
||||
|
||||
free_bitmap_clusters(bs, &bm->table);
|
||||
|
||||
fail:
|
||||
out:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
bitmap_free(bm);
|
||||
bitmap_list_free(bm_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
/*
|
||||
* qcow2_store_persistent_dirty_bitmaps
|
||||
*
|
||||
* Stores persistent BdrvDirtyBitmap objects.
|
||||
*
|
||||
* @release_stored: if true, release BdrvDirtyBitmap's after storing to the
|
||||
* image. This is used in two cases, both via qcow2_inactivate:
|
||||
* 1. bdrv_close: It's correct to remove bitmaps on close.
|
||||
* 2. migration: If bitmaps are migrated through migration channel via
|
||||
* 'dirty-bitmaps' migration capability they are not handled by this code.
|
||||
* Otherwise, it's OK to drop BdrvDirtyBitmap's and reload them on
|
||||
* invalidation.
|
||||
*
|
||||
* Anyway, it's correct to remove BdrvDirtyBitmap's on inactivation, as
|
||||
* inactivation means that we lose control on disk, and therefore on bitmaps,
|
||||
* we should sync them and do not touch more.
|
||||
*
|
||||
* Contrariwise, we don't want to release any bitmaps on just reopen-to-ro,
|
||||
* when we need to store them, as image is still under our control, and it's
|
||||
* good to keep all the bitmaps in read-only mode. Moreover, keeping them
|
||||
* read-only is correct because this is what would happen if we opened the node
|
||||
* readonly to begin with, and whether we opened directly or reopened to that
|
||||
* state shouldn't matter for the state we get afterward.
|
||||
*/
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bool release_stored, Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
@@ -1456,16 +1515,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
Qcow2Bitmap *bm;
|
||||
QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables;
|
||||
Qcow2BitmapTable *tb, *tb_next;
|
||||
|
||||
if (!bdrv_has_changed_persistent_bitmaps(bs)) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!can_write(bs)) {
|
||||
error_setg(errp, "No write access");
|
||||
return;
|
||||
}
|
||||
bool need_write = false;
|
||||
|
||||
QSIMPLEQ_INIT(&drop_tables);
|
||||
|
||||
@@ -1480,9 +1530,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
|
||||
/* check constraints and names */
|
||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
||||
{
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||
const char *name = bdrv_dirty_bitmap_name(bitmap);
|
||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||
Qcow2Bitmap *bm;
|
||||
@@ -1493,6 +1541,8 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
continue;
|
||||
}
|
||||
|
||||
need_write = true;
|
||||
|
||||
if (check_constraints_on_bitmap(bs, name, granularity, errp) < 0) {
|
||||
error_prepend(errp, "Bitmap '%s' doesn't satisfy the constraints: ",
|
||||
name);
|
||||
@@ -1531,6 +1581,15 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
bm->dirty_bitmap = bitmap;
|
||||
}
|
||||
|
||||
if (!need_write) {
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (!can_write(bs)) {
|
||||
error_setg(errp, "No write access");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* allocate clusters and store bitmaps */
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
if (bm->dirty_bitmap == NULL) {
|
||||
@@ -1556,22 +1615,17 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
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;
|
||||
}
|
||||
if (release_stored) {
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
if (bm->dirty_bitmap == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
|
||||
bdrv_release_dirty_bitmap(bm->dirty_bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
bitmap_list_free(bm_list);
|
||||
return;
|
||||
|
||||
@@ -1596,15 +1650,13 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
Error *local_err = NULL;
|
||||
|
||||
qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
|
||||
qcow2_store_persistent_dirty_bitmaps(bs, false, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
||||
{
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
bdrv_dirty_bitmap_set_readonly(bitmap, true);
|
||||
}
|
||||
@@ -1613,10 +1665,10 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
uint32_t granularity,
|
||||
Error **errp)
|
||||
bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
uint32_t granularity,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
bool found;
|
||||
@@ -1653,8 +1705,10 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (bm_list == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+18
-4
@@ -1835,6 +1835,20 @@ fail:
|
||||
static void qcow2_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
qcow2_update_options_commit(state->bs, state->opaque);
|
||||
if (state->flags & BDRV_O_RDWR) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (qcow2_reopen_bitmaps_rw(state->bs, &local_err) < 0) {
|
||||
/*
|
||||
* This is not fatal, bitmaps just left read-only, so all following
|
||||
* writes will fail. User can remove read-only bitmaps to unblock
|
||||
* writes or retry reopen.
|
||||
*/
|
||||
error_reportf_err(local_err,
|
||||
"%s: Failed to make dirty bitmaps writable: ",
|
||||
bdrv_get_node_name(state->bs));
|
||||
}
|
||||
}
|
||||
g_free(state->opaque);
|
||||
}
|
||||
|
||||
@@ -2503,7 +2517,7 @@ static int qcow2_inactivate(BlockDriverState *bs)
|
||||
int ret, result = 0;
|
||||
Error *local_err = NULL;
|
||||
|
||||
qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
|
||||
qcow2_store_persistent_dirty_bitmaps(bs, true, &local_err);
|
||||
if (local_err != NULL) {
|
||||
result = -EINVAL;
|
||||
error_reportf_err(local_err, "Lost persistent bitmaps during "
|
||||
@@ -5406,9 +5420,9 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_detach_aio_context = qcow2_detach_aio_context,
|
||||
.bdrv_attach_aio_context = qcow2_attach_aio_context,
|
||||
|
||||
.bdrv_reopen_bitmaps_rw = qcow2_reopen_bitmaps_rw,
|
||||
.bdrv_can_store_new_dirty_bitmap = qcow2_can_store_new_dirty_bitmap,
|
||||
.bdrv_remove_persistent_dirty_bitmap = qcow2_remove_persistent_dirty_bitmap,
|
||||
.bdrv_co_can_store_new_dirty_bitmap = qcow2_co_can_store_new_dirty_bitmap,
|
||||
.bdrv_co_remove_persistent_dirty_bitmap =
|
||||
qcow2_co_remove_persistent_dirty_bitmap,
|
||||
};
|
||||
|
||||
static void bdrv_qcow2_init(void)
|
||||
|
||||
+9
-10
@@ -740,19 +740,18 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||
Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
Error **errp);
|
||||
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);
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bool release_stored, Error **errp);
|
||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
||||
bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
uint32_t granularity,
|
||||
Error **errp);
|
||||
void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
uint32_t granularity,
|
||||
Error **errp);
|
||||
int qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
|
||||
ssize_t coroutine_fn
|
||||
qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size,
|
||||
|
||||
+10
-30
@@ -1966,7 +1966,6 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
|
||||
qmp_block_dirty_bitmap_add(action->node, action->name,
|
||||
action->has_granularity, action->granularity,
|
||||
action->has_persistent, action->persistent,
|
||||
action->has_autoload, action->autoload,
|
||||
action->has_disabled, action->disabled,
|
||||
&local_err);
|
||||
|
||||
@@ -2178,7 +2177,7 @@ static void block_dirty_bitmap_remove_commit(BlkActionState *common)
|
||||
common, common);
|
||||
|
||||
bdrv_dirty_bitmap_set_busy(state->bitmap, false);
|
||||
bdrv_release_dirty_bitmap(state->bs, state->bitmap);
|
||||
bdrv_release_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
|
||||
static void abort_prepare(BlkActionState *common, Error **errp)
|
||||
@@ -2858,7 +2857,6 @@ out:
|
||||
void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
bool has_granularity, uint32_t granularity,
|
||||
bool has_persistent, bool persistent,
|
||||
bool has_autoload, bool autoload,
|
||||
bool has_disabled, bool disabled,
|
||||
Error **errp)
|
||||
{
|
||||
@@ -2890,24 +2888,14 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||
persistent = false;
|
||||
}
|
||||
|
||||
if (has_autoload) {
|
||||
warn_report("Autoload option is deprecated and its value is ignored");
|
||||
}
|
||||
|
||||
if (!has_disabled) {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
if (persistent) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
bool ok;
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
ok = bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
||||
aio_context_release(aio_context);
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
if (persistent &&
|
||||
!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
||||
@@ -2939,22 +2927,14 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_remove(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
Error *local_err = NULL;
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err);
|
||||
aio_context_release(aio_context);
|
||||
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap) &&
|
||||
bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (release) {
|
||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
||||
bdrv_release_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
if (bitmap_bs) {
|
||||
@@ -3086,7 +3066,7 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(
|
||||
bdrv_merge_dirty_bitmap(dst, anon, backup, errp);
|
||||
|
||||
out:
|
||||
bdrv_release_dirty_bitmap(bs, anon);
|
||||
bdrv_release_dirty_bitmap(anon);
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ typedef struct HDGeometry {
|
||||
#define BDRV_BLOCK_EOF 0x20
|
||||
#define BDRV_BLOCK_RECURSE 0x40
|
||||
|
||||
typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
||||
typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
||||
|
||||
typedef struct BDRVReopenState {
|
||||
BlockDriverState *bs;
|
||||
|
||||
@@ -547,19 +547,13 @@ struct BlockDriver {
|
||||
uint64_t parent_perm, uint64_t parent_shared,
|
||||
uint64_t *nperm, uint64_t *nshared);
|
||||
|
||||
/**
|
||||
* Bitmaps should be marked as 'IN_USE' in the image on reopening image
|
||||
* as rw. This handler should realize it. It also should unset readonly
|
||||
* field of BlockDirtyBitmap's in case of success.
|
||||
*/
|
||||
int (*bdrv_reopen_bitmaps_rw)(BlockDriverState *bs, Error **errp);
|
||||
bool (*bdrv_can_store_new_dirty_bitmap)(BlockDriverState *bs,
|
||||
const char *name,
|
||||
uint32_t granularity,
|
||||
Error **errp);
|
||||
void (*bdrv_remove_persistent_dirty_bitmap)(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs,
|
||||
const char *name,
|
||||
uint32_t granularity,
|
||||
Error **errp);
|
||||
int (*bdrv_co_remove_persistent_dirty_bitmap)(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* Register/unregister a buffer for I/O. For example, when the driver is
|
||||
|
||||
@@ -18,28 +18,21 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int chunk_size);
|
||||
void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap,
|
||||
Error **errp);
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap,
|
||||
Error **errp);
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
Error **errp);
|
||||
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_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
||||
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs);
|
||||
void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||
const char *name,
|
||||
Error **errp);
|
||||
int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||
Error **errp);
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap);
|
||||
@@ -55,7 +48,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t offset, int64_t bytes);
|
||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
int64_t offset, int64_t bytes);
|
||||
BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap);
|
||||
BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap);
|
||||
void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter);
|
||||
|
||||
@@ -97,23 +89,25 @@ void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);
|
||||
void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset);
|
||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
|
||||
int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap);
|
||||
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_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);
|
||||
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs);
|
||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap);
|
||||
#define FOR_EACH_DIRTY_BITMAP(bs, bitmap) \
|
||||
for (bitmap = bdrv_dirty_bitmap_first(bs); bitmap; \
|
||||
bitmap = bdrv_dirty_bitmap_next(bitmap))
|
||||
|
||||
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
|
||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
||||
uint64_t bytes);
|
||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
||||
uint64_t *offset, uint64_t *bytes);
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
BdrvDirtyBitmap *bitmap,
|
||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||
Error **errp);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -132,6 +132,11 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count);
|
||||
* @count: Number of bits to reset.
|
||||
*
|
||||
* Reset a consecutive range of bits in an HBitmap.
|
||||
* @start and @count must be aligned to bitmap granularity. The only exception
|
||||
* is resetting the tail of the bitmap: @count may be equal to hb->orig_size -
|
||||
* @start, in this case @count may be not aligned. The sum of @start + @count is
|
||||
* allowed to be greater than hb->orig_size, but only if @start < hb->orig_size
|
||||
* and @start + @count = ALIGN_UP(hb->orig_size, granularity).
|
||||
*/
|
||||
void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count);
|
||||
|
||||
|
||||
@@ -283,9 +283,7 @@ static int init_dirty_bitmap_migration(void)
|
||||
for (bs = bdrv_next_all_states(NULL); bs; bs = bdrv_next_all_states(bs)) {
|
||||
const char *name = bdrv_get_device_or_node_name(bs);
|
||||
|
||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
|
||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
||||
{
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||
if (!bdrv_dirty_bitmap_name(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
@@ -474,7 +472,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s)
|
||||
if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) {
|
||||
DirtyBitmapLoadBitmapState *b;
|
||||
|
||||
bdrv_dirty_bitmap_create_successor(s->bs, s->bitmap, &local_err);
|
||||
bdrv_dirty_bitmap_create_successor(s->bitmap, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
return -EINVAL;
|
||||
@@ -535,13 +533,12 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s)
|
||||
bdrv_dirty_bitmap_lock(s->bitmap);
|
||||
if (enabled_bitmaps == NULL) {
|
||||
/* in postcopy */
|
||||
bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort);
|
||||
bdrv_reclaim_dirty_bitmap_locked(s->bitmap, &error_abort);
|
||||
bdrv_enable_dirty_bitmap_locked(s->bitmap);
|
||||
} else {
|
||||
/* target not started, successor must be empty */
|
||||
int64_t count = bdrv_get_dirty_count(s->bitmap);
|
||||
BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bs,
|
||||
s->bitmap,
|
||||
BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bitmap,
|
||||
NULL);
|
||||
/* bdrv_reclaim_dirty_bitmap can fail only on no successor (it
|
||||
* must be) or on merge fail, but merge can't fail when second
|
||||
|
||||
+2
-2
@@ -361,7 +361,7 @@ static int set_dirty_tracking(void)
|
||||
fail:
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
if (bmds->dirty_bitmap) {
|
||||
bdrv_release_dirty_bitmap(blk_bs(bmds->blk), bmds->dirty_bitmap);
|
||||
bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -374,7 +374,7 @@ static void unset_dirty_tracking(void)
|
||||
BlkMigDevState *bmds;
|
||||
|
||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||
bdrv_release_dirty_bitmap(blk_bs(bmds->blk), bmds->dirty_bitmap);
|
||||
bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2052,10 +2052,6 @@
|
||||
# Qcow2 disks support persistent bitmaps. Default is false for
|
||||
# block-dirty-bitmap-add. (Since: 2.10)
|
||||
#
|
||||
# @autoload: ignored and deprecated since 2.12.
|
||||
# Currently, all dirty tracking bitmaps are loaded from Qcow2 on
|
||||
# open.
|
||||
#
|
||||
# @disabled: the bitmap is created in the disabled state, which means that
|
||||
# it will not track drive changes. The bitmap may be enabled with
|
||||
# block-dirty-bitmap-enable. Default is false. (Since: 4.0)
|
||||
@@ -2064,7 +2060,7 @@
|
||||
##
|
||||
{ 'struct': 'BlockDirtyBitmapAdd',
|
||||
'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32',
|
||||
'*persistent': 'bool', '*autoload': 'bool', '*disabled': 'bool' } }
|
||||
'*persistent': 'bool', '*disabled': 'bool' } }
|
||||
|
||||
##
|
||||
# @BlockDirtyBitmapMergeSource:
|
||||
|
||||
+15
-5
@@ -149,11 +149,6 @@ QEMU 4.1 has three options, please migrate to one of these three:
|
||||
|
||||
@section QEMU Machine Protocol (QMP) commands
|
||||
|
||||
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
|
||||
|
||||
"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
|
||||
@@ -356,3 +351,18 @@ existing CPU models. Management software that needs runnability
|
||||
guarantees must resolve the CPU model aliases using te
|
||||
``alias-of'' field returned by the ``query-cpu-definitions'' QMP
|
||||
command.
|
||||
|
||||
|
||||
@node Recently removed features
|
||||
@appendix Recently removed features
|
||||
|
||||
What follows is a record of recently removed, formerly deprecated
|
||||
features that serves as a record for users who have encountered
|
||||
trouble after a recent upgrade.
|
||||
|
||||
@section QEMU Machine Protocol (QMP) commands
|
||||
|
||||
@subsection block-dirty-bitmap-add "autoload" parameter (since 4.2.0)
|
||||
|
||||
The "autoload" parameter has been ignored since 2.12.0. All bitmaps
|
||||
are automatically loaded from qcow2 images.
|
||||
|
||||
+55
-2
@@ -43,10 +43,10 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
|
||||
os.remove(disk)
|
||||
|
||||
def mkVm(self):
|
||||
return iotests.VM().add_drive(disk)
|
||||
return iotests.VM().add_drive(disk, opts='node-name=node0')
|
||||
|
||||
def mkVmRo(self):
|
||||
return iotests.VM().add_drive(disk, opts='readonly=on')
|
||||
return iotests.VM().add_drive(disk, opts='readonly=on,node-name=node0')
|
||||
|
||||
def getSha256(self):
|
||||
result = self.vm.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||
@@ -102,6 +102,59 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
|
||||
|
||||
self.vm.shutdown()
|
||||
|
||||
def test_reopen_rw(self):
|
||||
self.vm = self.mkVm()
|
||||
self.vm.launch()
|
||||
self.qmpAddBitmap()
|
||||
|
||||
# Calculate hashes
|
||||
|
||||
self.writeRegions(regions1)
|
||||
sha256_1 = self.getSha256()
|
||||
|
||||
self.writeRegions(regions2)
|
||||
sha256_2 = self.getSha256()
|
||||
assert sha256_1 != sha256_2 # Otherwise, it's not very interesting.
|
||||
|
||||
result = self.vm.qmp('block-dirty-bitmap-clear', node='drive0',
|
||||
name='bitmap0')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
# Start with regions1
|
||||
|
||||
self.writeRegions(regions1)
|
||||
assert sha256_1 == self.getSha256()
|
||||
|
||||
self.vm.shutdown()
|
||||
|
||||
self.vm = self.mkVmRo()
|
||||
self.vm.launch()
|
||||
|
||||
assert sha256_1 == self.getSha256()
|
||||
|
||||
# Check that we are in RO mode and can't modify bitmap.
|
||||
self.writeRegions(regions2)
|
||||
assert sha256_1 == self.getSha256()
|
||||
|
||||
# Reopen to RW
|
||||
result = self.vm.qmp('x-blockdev-reopen', **{
|
||||
'node-name': 'node0',
|
||||
'driver': iotests.imgfmt,
|
||||
'file': {
|
||||
'driver': 'file',
|
||||
'filename': disk
|
||||
},
|
||||
'read-only': False
|
||||
})
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
# Check that bitmap is reopened to RW and we can write to it.
|
||||
self.writeRegions(regions2)
|
||||
assert sha256_2 == self.getSha256()
|
||||
|
||||
self.vm.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2'],
|
||||
supported_protocols=['file'])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.
|
||||
..
|
||||
----------------------------------------------------------------------
|
||||
Ran 1 tests
|
||||
Ran 2 tests
|
||||
|
||||
OK
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user