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:
Peter Maydell
2019-10-18 12:53:52 +01:00
25 changed files with 623 additions and 367 deletions
+1 -2
View File
@@ -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
+21 -58
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+7 -13
View File
@@ -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
+14 -20
View File
@@ -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
+5
View File
@@ -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);
+4 -7
View File
@@ -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
View File
@@ -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);
}
}
+1 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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'])
+2 -2
View 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