pull request
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAl2o4wYACgkQfe+BBqr8 OQ5MBA/+Ou8GKAh7WJ4SqsPlP8/B7nxpRQ4cYYsL90+CJguhy/akYL++LmdBcWle Ek0KyQSJI8YcC0DKNHAOwhs4JJAecIdAqIvJWalPnzXRmf9Sv2ceNlBw8jJlclg2 FQ8YmJJ+hk3TyqcdAxLi7wmQ3aLeos3zEN/MRiI+rznATHvYGoF38UXTKAOJzQ1i 5YFaBdVuC3GATFgdKSJEGJ6+h1xG4UFIRZzUkiKUP/VsfrY3xyMzGyXlxG6mNQ7t U0ko8rYZmhMDLEkr6AdsthlVfQaEX0BMA1iSKD3ApyN4vKOI8Bjc72nS8eaE1jG/ luQUZf2afdWSi3AaAiOBAOfObdh/taFtv4IsMgSCXRfJVlS6uzdJFbL256cuZVWZ 9N++eNP3CuJUzcaEesZUbM6AHIoVpcxT5rbbNB0oSTcxO3AnjCRxWlMbyaH7gEbs x7zN/dTdNvZvmh+VLd0etFL9Jj2329u414bAJ9xmC1pcNjOWrKtuIMoQcH+ijf03 DoLnyxWxz+NNc9K1M0uxe0mnYXhfi16gdfKYy9MdEORLIts9juXU4fWeevpbvRmD ucvnlgdlGME+wAs4YKEYoVhCJ2/GqahgFCkfc5739zO9DEDhJ+z/UySzdNB+PvlT Nu5paIkji7WmbUmEGvRH1www8xKku60L3GnkU0noELSbZuGH5J4= =sb/Q -----END PGP SIGNATURE----- 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:
commit
ca32646d41
|
@ -1816,8 +1816,8 @@ F: qapi/transaction.json
|
||||||
T: git https://repo.or.cz/qemu/armbru.git block-next
|
T: git https://repo.or.cz/qemu/armbru.git block-next
|
||||||
|
|
||||||
Dirty Bitmaps
|
Dirty Bitmaps
|
||||||
M: Fam Zheng <fam@euphon.net>
|
|
||||||
M: John Snow <jsnow@redhat.com>
|
M: John Snow <jsnow@redhat.com>
|
||||||
|
R: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||||
L: qemu-block@nongnu.org
|
L: qemu-block@nongnu.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: util/hbitmap.c
|
F: util/hbitmap.c
|
||||||
|
@ -1826,7 +1826,6 @@ F: include/qemu/hbitmap.h
|
||||||
F: include/block/dirty-bitmap.h
|
F: include/block/dirty-bitmap.h
|
||||||
F: tests/test-hbitmap.c
|
F: tests/test-hbitmap.c
|
||||||
F: docs/interop/bitmaps.rst
|
F: docs/interop/bitmaps.rst
|
||||||
T: git https://github.com/famz/qemu.git bitmaps
|
|
||||||
T: git https://github.com/jnsnow/qemu.git bitmaps
|
T: git https://github.com/jnsnow/qemu.git bitmaps
|
||||||
|
|
||||||
Character device backends
|
Character device backends
|
||||||
|
|
79
block.c
79
block.c
|
@ -1719,7 +1719,7 @@ typedef struct BlockReopenQueueEntry {
|
||||||
bool prepared;
|
bool prepared;
|
||||||
bool perms_checked;
|
bool perms_checked;
|
||||||
BDRVReopenState state;
|
BDRVReopenState state;
|
||||||
QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry;
|
QTAILQ_ENTRY(BlockReopenQueueEntry) entry;
|
||||||
} BlockReopenQueueEntry;
|
} BlockReopenQueueEntry;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1732,7 +1732,7 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
|
||||||
BlockReopenQueueEntry *entry;
|
BlockReopenQueueEntry *entry;
|
||||||
|
|
||||||
if (q != NULL) {
|
if (q != NULL) {
|
||||||
QSIMPLEQ_FOREACH(entry, q, entry) {
|
QTAILQ_FOREACH(entry, q, entry) {
|
||||||
if (entry->state.bs == bs) {
|
if (entry->state.bs == bs) {
|
||||||
return entry->state.flags;
|
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
|
* Adds a BlockDriverState to a simple queue for an atomic, transactional
|
||||||
* reopen of multiple devices.
|
* 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
|
* already performed, or alternatively may be NULL a new BlockReopenQueue will
|
||||||
* be created and initialized. This newly created BlockReopenQueue should be
|
* be created and initialized. This newly created BlockReopenQueue should be
|
||||||
* passed back in for subsequent calls that are intended to be of the same
|
* 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) {
|
if (bs_queue == NULL) {
|
||||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||||
QSIMPLEQ_INIT(bs_queue);
|
QTAILQ_INIT(bs_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options) {
|
if (!options) {
|
||||||
|
@ -3298,7 +3298,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if this BlockDriverState is already in the 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) {
|
if (bs == bs_entry->state.bs) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3354,7 +3354,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||||
|
|
||||||
if (!bs_entry) {
|
if (!bs_entry) {
|
||||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||||
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
QTAILQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
||||||
} else {
|
} else {
|
||||||
qobject_unref(bs_entry->state.options);
|
qobject_unref(bs_entry->state.options);
|
||||||
qobject_unref(bs_entry->state.explicit_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);
|
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);
|
assert(bs_entry->state.bs->quiesce_counter > 0);
|
||||||
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
|
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
@ -3463,7 +3463,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||||
bs_entry->prepared = true;
|
bs_entry->prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||||
BDRVReopenState *state = &bs_entry->state;
|
BDRVReopenState *state = &bs_entry->state;
|
||||||
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
|
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
|
||||||
state->shared_perm, NULL, NULL, errp);
|
state->shared_perm, NULL, NULL, errp);
|
||||||
|
@ -3486,16 +3486,22 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
||||||
bs_entry->perms_checked = true;
|
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);
|
bdrv_reopen_commit(&bs_entry->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
cleanup_perm:
|
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;
|
BDRVReopenState *state = &bs_entry->state;
|
||||||
|
|
||||||
if (!bs_entry->perms_checked) {
|
if (!bs_entry->perms_checked) {
|
||||||
|
@ -3512,7 +3518,7 @@ cleanup_perm:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanup:
|
cleanup:
|
||||||
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (bs_entry->prepared) {
|
if (bs_entry->prepared) {
|
||||||
bdrv_reopen_abort(&bs_entry->state);
|
bdrv_reopen_abort(&bs_entry->state);
|
||||||
|
@ -3552,7 +3558,7 @@ static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q,
|
||||||
{
|
{
|
||||||
BlockReopenQueueEntry *entry;
|
BlockReopenQueueEntry *entry;
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(entry, q, entry) {
|
QTAILQ_FOREACH(entry, q, entry) {
|
||||||
BlockDriverState *bs = entry->state.bs;
|
BlockDriverState *bs = entry->state.bs;
|
||||||
BdrvChild *child;
|
BdrvChild *child;
|
||||||
|
|
||||||
|
@ -3929,16 +3935,12 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BdrvChild *child;
|
BdrvChild *child;
|
||||||
bool old_can_write, new_can_write;
|
|
||||||
|
|
||||||
assert(reopen_state != NULL);
|
assert(reopen_state != NULL);
|
||||||
bs = reopen_state->bs;
|
bs = reopen_state->bs;
|
||||||
drv = bs->drv;
|
drv = bs->drv;
|
||||||
assert(drv != NULL);
|
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 there are any driver level actions to take */
|
||||||
if (drv->bdrv_reopen_commit) {
|
if (drv->bdrv_reopen_commit) {
|
||||||
drv->bdrv_reopen_commit(reopen_state);
|
drv->bdrv_reopen_commit(reopen_state);
|
||||||
|
@ -3982,21 +3984,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_refresh_limits(bs, NULL);
|
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;
|
FOR_EACH_DIRTY_BITMAP(bs, bm) {
|
||||||
bm = bdrv_dirty_bitmap_next(bs, bm))
|
|
||||||
{
|
|
||||||
bdrv_dirty_bitmap_skip_store(bm, false);
|
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);
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -98,13 +98,13 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||||
* We succeeded, or we always intended to sync the bitmap.
|
* We succeeded, or we always intended to sync the bitmap.
|
||||||
* Delete this bitmap and install the child.
|
* 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 {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We failed, or we never intended to sync the bitmap anyway.
|
* We failed, or we never intended to sync the bitmap anyway.
|
||||||
* Merge the successor back into the parent, keeping all data.
|
* 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);
|
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. */
|
/* 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;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (sync_bitmap) {
|
if (sync_bitmap) {
|
||||||
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
|
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
|
||||||
}
|
}
|
||||||
if (job) {
|
if (job) {
|
||||||
backup_clean(&job->common.job);
|
backup_clean(&job->common.job);
|
||||||
|
|
|
@ -60,7 +60,7 @@ void block_copy_state_free(BlockCopyState *s)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_release_dirty_bitmap(s->source->bs, s->copy_bitmap);
|
bdrv_release_dirty_bitmap(s->copy_bitmap);
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,11 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/blockjob.h"
|
#include "block/blockjob.h"
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
|
|
||||||
struct BdrvDirtyBitmap {
|
struct BdrvDirtyBitmap {
|
||||||
QemuMutex *mutex;
|
BlockDriverState *bs;
|
||||||
HBitmap *bitmap; /* Dirty bitmap implementation */
|
HBitmap *bitmap; /* Dirty bitmap implementation */
|
||||||
HBitmap *meta; /* Meta dirty bitmap */
|
|
||||||
bool busy; /* Bitmap is busy, it can't be used via QMP */
|
bool busy; /* Bitmap is busy, it can't be used via QMP */
|
||||||
BdrvDirtyBitmap *successor; /* Anonymous child, if any. */
|
BdrvDirtyBitmap *successor; /* Anonymous child, if any. */
|
||||||
char *name; /* Optional non-empty unique ID */
|
char *name; /* Optional non-empty unique ID */
|
||||||
|
@ -71,12 +71,12 @@ static inline void bdrv_dirty_bitmaps_unlock(BlockDriverState *bs)
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap)
|
void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap)
|
void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL or dirty_bitmap lock taken. */
|
/* Called with BQL or dirty_bitmap lock taken. */
|
||||||
|
@ -115,7 +115,7 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
bitmap = g_new0(BdrvDirtyBitmap, 1);
|
||||||
bitmap->mutex = &bs->dirty_bitmap_mutex;
|
bitmap->bs = bs;
|
||||||
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity));
|
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity));
|
||||||
bitmap->size = bitmap_size;
|
bitmap->size = bitmap_size;
|
||||||
bitmap->name = g_strdup(name);
|
bitmap->name = g_strdup(name);
|
||||||
|
@ -126,36 +126,6 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bdrv_create_meta_dirty_bitmap
|
|
||||||
*
|
|
||||||
* Create a meta dirty bitmap that tracks the changes of bits in @bitmap. I.e.
|
|
||||||
* when a dirty status bit in @bitmap is changed (either from reset to set or
|
|
||||||
* the other way around), its respective meta dirty bitmap bit will be marked
|
|
||||||
* dirty as well.
|
|
||||||
*
|
|
||||||
* @bitmap: the block dirty bitmap for which to create a meta dirty bitmap.
|
|
||||||
* @chunk_size: how many bytes of bitmap data does each bit in the meta bitmap
|
|
||||||
* track.
|
|
||||||
*/
|
|
||||||
void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
|
||||||
int chunk_size)
|
|
||||||
{
|
|
||||||
assert(!bitmap->meta);
|
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
|
||||||
bitmap->meta = hbitmap_create_meta(bitmap->bitmap,
|
|
||||||
chunk_size * BITS_PER_BYTE);
|
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
assert(bitmap->meta);
|
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
|
||||||
hbitmap_free_meta(bitmap->bitmap);
|
|
||||||
bitmap->meta = NULL;
|
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap)
|
int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return bitmap->size;
|
return bitmap->size;
|
||||||
|
@ -179,9 +149,9 @@ static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
|
||||||
|
|
||||||
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
|
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bitmap->busy = busy;
|
bitmap->busy = busy;
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
|
@ -267,8 +237,7 @@ int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
||||||
* The successor will be enabled if the parent bitmap was.
|
* The successor will be enabled if the parent bitmap was.
|
||||||
* Called with BQL taken.
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap, Error **errp)
|
||||||
BdrvDirtyBitmap *bitmap, Error **errp)
|
|
||||||
{
|
{
|
||||||
uint64_t granularity;
|
uint64_t granularity;
|
||||||
BdrvDirtyBitmap *child;
|
BdrvDirtyBitmap *child;
|
||||||
|
@ -284,7 +253,7 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
||||||
|
|
||||||
/* Create an anonymous successor */
|
/* Create an anonymous successor */
|
||||||
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||||
child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
|
child = bdrv_create_dirty_bitmap(bitmap->bs, granularity, NULL, errp);
|
||||||
if (!child) {
|
if (!child) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -307,10 +276,10 @@ void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
assert(bitmap->mutex == bitmap->successor->mutex);
|
assert(bitmap->bs == bitmap->successor->bs);
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bdrv_enable_dirty_bitmap_locked(bitmap->successor);
|
bdrv_enable_dirty_bitmap_locked(bitmap->successor);
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */
|
/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */
|
||||||
|
@ -319,7 +288,6 @@ static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||||
assert(!bitmap->active_iterators);
|
assert(!bitmap->active_iterators);
|
||||||
assert(!bdrv_dirty_bitmap_busy(bitmap));
|
assert(!bdrv_dirty_bitmap_busy(bitmap));
|
||||||
assert(!bdrv_dirty_bitmap_has_successor(bitmap));
|
assert(!bdrv_dirty_bitmap_has_successor(bitmap));
|
||||||
assert(!bitmap->meta);
|
|
||||||
QLIST_REMOVE(bitmap, list);
|
QLIST_REMOVE(bitmap, list);
|
||||||
hbitmap_free(bitmap->bitmap);
|
hbitmap_free(bitmap->bitmap);
|
||||||
g_free(bitmap->name);
|
g_free(bitmap->name);
|
||||||
|
@ -331,8 +299,7 @@ static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||||
* delete the old bitmap, and return a handle to the new bitmap.
|
* delete the old bitmap, and return a handle to the new bitmap.
|
||||||
* Called with BQL taken.
|
* Called with BQL taken.
|
||||||
*/
|
*/
|
||||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap,
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -351,7 +318,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||||
successor->persistent = bitmap->persistent;
|
successor->persistent = bitmap->persistent;
|
||||||
bitmap->persistent = false;
|
bitmap->persistent = false;
|
||||||
bitmap->busy = false;
|
bitmap->busy = false;
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
bdrv_release_dirty_bitmap(bitmap);
|
||||||
|
|
||||||
return successor;
|
return successor;
|
||||||
}
|
}
|
||||||
|
@ -363,8 +330,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
||||||
* The marged parent will be enabled if and only if the successor was enabled.
|
* The marged parent will be enabled if and only if the successor was enabled.
|
||||||
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
|
||||||
*/
|
*/
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent,
|
||||||
BdrvDirtyBitmap *parent,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *successor = parent->successor;
|
BdrvDirtyBitmap *successor = parent->successor;
|
||||||
|
@ -388,15 +354,14 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *parent,
|
||||||
BdrvDirtyBitmap *parent,
|
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *ret;
|
BdrvDirtyBitmap *ret;
|
||||||
|
|
||||||
qemu_mutex_lock(parent->mutex);
|
bdrv_dirty_bitmaps_lock(parent->bs);
|
||||||
ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp);
|
ret = bdrv_reclaim_dirty_bitmap_locked(parent, errp);
|
||||||
qemu_mutex_unlock(parent->mutex);
|
bdrv_dirty_bitmaps_unlock(parent->bs);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -421,8 +386,10 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
|
void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
|
BlockDriverState *bs = bitmap->bs;
|
||||||
|
|
||||||
bdrv_dirty_bitmaps_lock(bs);
|
bdrv_dirty_bitmaps_lock(bs);
|
||||||
bdrv_release_dirty_bitmap_locked(bitmap);
|
bdrv_release_dirty_bitmap_locked(bitmap);
|
||||||
bdrv_dirty_bitmaps_unlock(bs);
|
bdrv_dirty_bitmaps_unlock(bs);
|
||||||
|
@ -455,27 +422,135 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||||
* not fail.
|
* not fail.
|
||||||
* This function doesn't release corresponding BdrvDirtyBitmap.
|
* This function doesn't release corresponding BdrvDirtyBitmap.
|
||||||
*/
|
*/
|
||||||
void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
static int coroutine_fn
|
||||||
const char *name,
|
bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
if (bs->drv && bs->drv->bdrv_remove_persistent_dirty_bitmap) {
|
if (bs->drv && bs->drv->bdrv_co_remove_persistent_dirty_bitmap) {
|
||||||
bs->drv->bdrv_remove_persistent_dirty_bitmap(bs, name, errp);
|
return bs->drv->bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct BdrvRemovePersistentDirtyBitmapCo {
|
||||||
|
BlockDriverState *bs;
|
||||||
|
const char *name;
|
||||||
|
Error **errp;
|
||||||
|
int ret;
|
||||||
|
} BdrvRemovePersistentDirtyBitmapCo;
|
||||||
|
|
||||||
|
static void coroutine_fn
|
||||||
|
bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque)
|
||||||
|
{
|
||||||
|
BdrvRemovePersistentDirtyBitmapCo *s = opaque;
|
||||||
|
|
||||||
|
s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp);
|
||||||
|
aio_wait_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
if (qemu_in_coroutine()) {
|
||||||
|
return bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp);
|
||||||
|
} else {
|
||||||
|
Coroutine *co;
|
||||||
|
BdrvRemovePersistentDirtyBitmapCo s = {
|
||||||
|
.bs = bs,
|
||||||
|
.name = name,
|
||||||
|
.errp = errp,
|
||||||
|
.ret = -EINPROGRESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
co = qemu_coroutine_create(bdrv_co_remove_persistent_dirty_bitmap_entry,
|
||||||
|
&s);
|
||||||
|
bdrv_coroutine_enter(bs, co);
|
||||||
|
BDRV_POLL_WHILE(bs, s.ret == -EINPROGRESS);
|
||||||
|
|
||||||
|
return s.ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool coroutine_fn
|
||||||
|
bdrv_co_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_co_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_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct BdrvCanStoreNewDirtyBitmapCo {
|
||||||
|
BlockDriverState *bs;
|
||||||
|
const char *name;
|
||||||
|
uint32_t granularity;
|
||||||
|
Error **errp;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
bool in_progress;
|
||||||
|
} BdrvCanStoreNewDirtyBitmapCo;
|
||||||
|
|
||||||
|
static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque)
|
||||||
|
{
|
||||||
|
BdrvCanStoreNewDirtyBitmapCo *s = opaque;
|
||||||
|
|
||||||
|
s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity,
|
||||||
|
s->errp);
|
||||||
|
s->in_progress = false;
|
||||||
|
aio_wait_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||||
|
uint32_t granularity, Error **errp)
|
||||||
|
{
|
||||||
|
if (qemu_in_coroutine()) {
|
||||||
|
return bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
||||||
|
} else {
|
||||||
|
Coroutine *co;
|
||||||
|
BdrvCanStoreNewDirtyBitmapCo s = {
|
||||||
|
.bs = bs,
|
||||||
|
.name = name,
|
||||||
|
.granularity = granularity,
|
||||||
|
.errp = errp,
|
||||||
|
.in_progress = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
co = qemu_coroutine_create(bdrv_co_can_store_new_dirty_bitmap_entry,
|
||||||
|
&s);
|
||||||
|
bdrv_coroutine_enter(bs, co);
|
||||||
|
BDRV_POLL_WHILE(bs, s.in_progress);
|
||||||
|
|
||||||
|
return s.ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bitmap->disabled = true;
|
bitmap->disabled = true;
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bdrv_enable_dirty_bitmap_locked(bitmap);
|
bdrv_enable_dirty_bitmap_locked(bitmap);
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
|
||||||
|
@ -516,9 +591,9 @@ bool bdrv_dirty_bitmap_get_locked(BdrvDirtyBitmap *bitmap, int64_t offset)
|
||||||
bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset)
|
bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
ret = bdrv_dirty_bitmap_get_locked(bitmap, offset);
|
ret = bdrv_dirty_bitmap_get_locked(bitmap, offset);
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -557,15 +632,6 @@ BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap)
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1);
|
|
||||||
hbitmap_iter_init(&iter->hbi, bitmap->meta, 0);
|
|
||||||
iter->bitmap = bitmap;
|
|
||||||
bitmap->active_iterators++;
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
|
void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
|
||||||
{
|
{
|
||||||
if (!iter) {
|
if (!iter) {
|
||||||
|
@ -592,9 +658,9 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t offset, int64_t bytes)
|
int64_t offset, int64_t bytes)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bdrv_set_dirty_bitmap_locked(bitmap, offset, bytes);
|
bdrv_set_dirty_bitmap_locked(bitmap, offset, bytes);
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||||
|
@ -608,15 +674,15 @@ void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t offset, int64_t bytes)
|
int64_t offset, int64_t bytes)
|
||||||
{
|
{
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bdrv_reset_dirty_bitmap_locked(bitmap, offset, bytes);
|
bdrv_reset_dirty_bitmap_locked(bitmap, offset, bytes);
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
{
|
{
|
||||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
hbitmap_reset_all(bitmap->bitmap);
|
hbitmap_reset_all(bitmap->bitmap);
|
||||||
} else {
|
} else {
|
||||||
|
@ -625,7 +691,7 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||||
hbitmap_granularity(backup));
|
hbitmap_granularity(backup));
|
||||||
*out = backup;
|
*out = backup;
|
||||||
}
|
}
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
|
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
|
||||||
|
@ -712,11 +778,6 @@ int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap)
|
||||||
return hbitmap_count(bitmap->bitmap);
|
return hbitmap_count(bitmap->bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
|
||||||
return hbitmap_count(bitmap->meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
return bitmap->readonly;
|
return bitmap->readonly;
|
||||||
|
@ -725,9 +786,9 @@ bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap)
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value)
|
void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bitmap->readonly = value;
|
bitmap->readonly = value;
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
|
bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
|
||||||
|
@ -745,27 +806,27 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent)
|
void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bitmap->persistent = persistent;
|
bitmap->persistent = persistent;
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
|
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
assert(bitmap->persistent == true);
|
assert(bitmap->persistent == true);
|
||||||
bitmap->inconsistent = true;
|
bitmap->inconsistent = true;
|
||||||
bitmap->disabled = true;
|
bitmap->disabled = true;
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with BQL taken. */
|
/* Called with BQL taken. */
|
||||||
void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip)
|
void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(bitmap->mutex);
|
bdrv_dirty_bitmaps_lock(bitmap->bs);
|
||||||
bitmap->skip_store = skip;
|
bitmap->skip_store = skip;
|
||||||
qemu_mutex_unlock(bitmap->mutex);
|
bdrv_dirty_bitmaps_unlock(bitmap->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
|
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
|
||||||
|
@ -778,23 +839,14 @@ bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap)
|
||||||
return bitmap->inconsistent;
|
return bitmap->inconsistent;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvDirtyBitmap *bm;
|
return QLIST_FIRST(&bs->dirty_bitmaps);
|
||||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
|
||||||
if (bm->persistent && !bm->readonly && !bm->skip_store) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap)
|
||||||
BdrvDirtyBitmap *bitmap)
|
|
||||||
{
|
{
|
||||||
return bitmap == NULL ? QLIST_FIRST(&bs->dirty_bitmaps) :
|
return QLIST_NEXT(bitmap, list);
|
||||||
QLIST_NEXT(bitmap, list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
||||||
|
@ -825,9 +877,9 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
qemu_mutex_lock(dest->mutex);
|
bdrv_dirty_bitmaps_lock(dest->bs);
|
||||||
if (src->mutex != dest->mutex) {
|
if (src->bs != dest->bs) {
|
||||||
qemu_mutex_lock(src->mutex);
|
bdrv_dirty_bitmaps_lock(src->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
|
if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
|
||||||
|
@ -847,9 +899,9 @@ void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_mutex_unlock(dest->mutex);
|
bdrv_dirty_bitmaps_unlock(dest->bs);
|
||||||
if (src->mutex != dest->mutex) {
|
if (src->bs != dest->bs) {
|
||||||
qemu_mutex_unlock(src->mutex);
|
bdrv_dirty_bitmaps_unlock(src->bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,9 +925,9 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
|
||||||
assert(!bdrv_dirty_bitmap_inconsistent(src));
|
assert(!bdrv_dirty_bitmap_inconsistent(src));
|
||||||
|
|
||||||
if (lock) {
|
if (lock) {
|
||||||
qemu_mutex_lock(dest->mutex);
|
bdrv_dirty_bitmaps_lock(dest->bs);
|
||||||
if (src->mutex != dest->mutex) {
|
if (src->bs != dest->bs) {
|
||||||
qemu_mutex_lock(src->mutex);
|
bdrv_dirty_bitmaps_lock(src->bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,9 +940,9 @@ bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lock) {
|
if (lock) {
|
||||||
qemu_mutex_unlock(dest->mutex);
|
bdrv_dirty_bitmaps_unlock(dest->bs);
|
||||||
if (src->mutex != dest->mutex) {
|
if (src->bs != dest->bs) {
|
||||||
qemu_mutex_unlock(src->mutex);
|
bdrv_dirty_bitmaps_unlock(src->bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -638,7 +638,7 @@ static int mirror_exit_common(Job *job)
|
||||||
bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
|
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,
|
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
|
||||||
* before we can call bdrv_drained_end */
|
* before we can call bdrv_drained_end */
|
||||||
|
@ -1709,7 +1709,7 @@ fail:
|
||||||
blk_unref(s->target);
|
blk_unref(s->target);
|
||||||
bs_opaque->job = NULL;
|
bs_opaque->job = NULL;
|
||||||
if (s->dirty_bitmap) {
|
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);
|
job_early_fail(&s->common.job);
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,7 +374,7 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
|
||||||
fail:
|
fail:
|
||||||
g_free(bitmap_table);
|
g_free(bitmap_table);
|
||||||
if (bitmap != NULL) {
|
if (bitmap != NULL) {
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
bdrv_release_dirty_bitmap(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -941,7 +941,7 @@ fail:
|
||||||
static void release_dirty_bitmap_helper(gpointer bitmap,
|
static void release_dirty_bitmap_helper(gpointer bitmap,
|
||||||
gpointer bs)
|
gpointer bs)
|
||||||
{
|
{
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
bdrv_release_dirty_bitmap(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
|
/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */
|
||||||
|
@ -1102,29 +1102,20 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
|
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2BitmapList *bm_list;
|
Qcow2BitmapList *bm_list;
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm;
|
||||||
GSList *ro_dirty_bitmaps = NULL;
|
GSList *ro_dirty_bitmaps = NULL;
|
||||||
int ret = 0;
|
int ret = -EINVAL;
|
||||||
|
bool need_header_update = false;
|
||||||
if (header_updated != NULL) {
|
|
||||||
*header_updated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->nb_bitmaps == 0) {
|
if (s->nb_bitmaps == 0) {
|
||||||
/* No bitmaps - nothing to do */
|
/* No bitmaps - nothing to do */
|
||||||
return 0;
|
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,
|
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||||
s->bitmap_directory_size, errp);
|
s->bitmap_directory_size, errp);
|
||||||
if (bm_list == NULL) {
|
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) {
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name);
|
||||||
if (bitmap == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bdrv_dirty_bitmap_readonly(bitmap)) {
|
if (!bitmap) {
|
||||||
error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was "
|
error_setg(errp, "Unexpected bitmap '%s' in image '%s'",
|
||||||
"not marked as readonly. This is a bug, something went "
|
bm->name, bs->filename);
|
||||||
"wrong. All of the bitmaps may be corrupted", bm->name);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bm->flags |= BME_FLAG_IN_USE;
|
if (!(bm->flags & BME_FLAG_IN_USE)) {
|
||||||
ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap);
|
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 */
|
/* in_use flags must be updated */
|
||||||
ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
ret = update_ext_header_and_dir_in_place(bs, bm_list);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
error_setg_errno(errp, -ret, "Cannot update bitmap directory");
|
||||||
goto out;
|
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:
|
out:
|
||||||
g_slist_free(ro_dirty_bitmaps);
|
g_slist_free(ro_dirty_bitmaps);
|
||||||
bitmap_list_free(bm_list);
|
bitmap_list_free(bm_list);
|
||||||
|
@ -1169,11 +1200,6 @@ out:
|
||||||
return ret;
|
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 */
|
/* Checks to see if it's safe to resize bitmaps */
|
||||||
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp)
|
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -1404,30 +1430,34 @@ static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||||
const char *name,
|
const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm = NULL;
|
||||||
Qcow2BitmapList *bm_list;
|
Qcow2BitmapList *bm_list;
|
||||||
|
|
||||||
if (s->nb_bitmaps == 0) {
|
if (s->nb_bitmaps == 0) {
|
||||||
/* Absence of the bitmap is not an error: see explanation above
|
/* Absence of the bitmap is not an error: see explanation above
|
||||||
* bdrv_remove_persistent_dirty_bitmap() definition. */
|
* 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,
|
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||||
s->bitmap_directory_size, errp);
|
s->bitmap_directory_size, errp);
|
||||||
if (bm_list == NULL) {
|
if (bm_list == NULL) {
|
||||||
return;
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bm = find_bitmap_by_name(bm_list, name);
|
bm = find_bitmap_by_name(bm_list, name);
|
||||||
if (bm == NULL) {
|
if (bm == NULL) {
|
||||||
goto fail;
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry);
|
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);
|
ret = update_ext_header_and_dir(bs, bm_list);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Failed to update bitmap extension");
|
error_setg_errno(errp, -ret, "Failed to update bitmap extension");
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_bitmap_clusters(bs, &bm->table);
|
free_bitmap_clusters(bs, &bm->table);
|
||||||
|
|
||||||
fail:
|
out:
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
|
||||||
bitmap_free(bm);
|
bitmap_free(bm);
|
||||||
bitmap_list_free(bm_list);
|
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;
|
BdrvDirtyBitmap *bitmap;
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
@ -1456,16 +1515,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm;
|
||||||
QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables;
|
QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables;
|
||||||
Qcow2BitmapTable *tb, *tb_next;
|
Qcow2BitmapTable *tb, *tb_next;
|
||||||
|
bool need_write = false;
|
||||||
if (!bdrv_has_changed_persistent_bitmaps(bs)) {
|
|
||||||
/* nothing to do */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!can_write(bs)) {
|
|
||||||
error_setg(errp, "No write access");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSIMPLEQ_INIT(&drop_tables);
|
QSIMPLEQ_INIT(&drop_tables);
|
||||||
|
|
||||||
|
@ -1480,9 +1530,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check constraints and names */
|
/* check constraints and names */
|
||||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
|
||||||
{
|
|
||||||
const char *name = bdrv_dirty_bitmap_name(bitmap);
|
const char *name = bdrv_dirty_bitmap_name(bitmap);
|
||||||
uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
|
||||||
Qcow2Bitmap *bm;
|
Qcow2Bitmap *bm;
|
||||||
|
@ -1493,6 +1541,8 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
need_write = true;
|
||||||
|
|
||||||
if (check_constraints_on_bitmap(bs, name, granularity, errp) < 0) {
|
if (check_constraints_on_bitmap(bs, name, granularity, errp) < 0) {
|
||||||
error_prepend(errp, "Bitmap '%s' doesn't satisfy the constraints: ",
|
error_prepend(errp, "Bitmap '%s' doesn't satisfy the constraints: ",
|
||||||
name);
|
name);
|
||||||
|
@ -1531,6 +1581,15 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
bm->dirty_bitmap = bitmap;
|
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 */
|
/* allocate clusters and store bitmaps */
|
||||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
if (bm->dirty_bitmap == NULL) {
|
if (bm->dirty_bitmap == NULL) {
|
||||||
|
@ -1556,22 +1615,17 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||||
g_free(tb);
|
g_free(tb);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
if (release_stored) {
|
||||||
/* For safety, we remove bitmap after storing.
|
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||||
* We may be here in two cases:
|
if (bm->dirty_bitmap == NULL) {
|
||||||
* 1. bdrv_close. It's ok to drop bitmap.
|
continue;
|
||||||
* 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);
|
bdrv_release_dirty_bitmap(bm->dirty_bitmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
success:
|
||||||
bitmap_list_free(bm_list);
|
bitmap_list_free(bm_list);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1596,15 +1650,13 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||||
BdrvDirtyBitmap *bitmap;
|
BdrvDirtyBitmap *bitmap;
|
||||||
Error *local_err = NULL;
|
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) {
|
if (local_err != NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL;
|
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
|
||||||
{
|
|
||||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||||
bdrv_dirty_bitmap_set_readonly(bitmap, true);
|
bdrv_dirty_bitmap_set_readonly(bitmap, true);
|
||||||
}
|
}
|
||||||
|
@ -1613,10 +1665,10 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||||
const char *name,
|
const char *name,
|
||||||
uint32_t granularity,
|
uint32_t granularity,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcow2State *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
bool found;
|
bool found;
|
||||||
|
@ -1653,8 +1705,10 @@ bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||||
s->bitmap_directory_size, errp);
|
s->bitmap_directory_size, errp);
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
if (bm_list == NULL) {
|
if (bm_list == NULL) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1835,6 +1835,20 @@ fail:
|
||||||
static void qcow2_reopen_commit(BDRVReopenState *state)
|
static void qcow2_reopen_commit(BDRVReopenState *state)
|
||||||
{
|
{
|
||||||
qcow2_update_options_commit(state->bs, state->opaque);
|
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);
|
g_free(state->opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2503,7 +2517,7 @@ static int qcow2_inactivate(BlockDriverState *bs)
|
||||||
int ret, result = 0;
|
int ret, result = 0;
|
||||||
Error *local_err = NULL;
|
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) {
|
if (local_err != NULL) {
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
error_reportf_err(local_err, "Lost persistent bitmaps during "
|
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_detach_aio_context = qcow2_detach_aio_context,
|
||||||
.bdrv_attach_aio_context = qcow2_attach_aio_context,
|
.bdrv_attach_aio_context = qcow2_attach_aio_context,
|
||||||
|
|
||||||
.bdrv_reopen_bitmaps_rw = qcow2_reopen_bitmaps_rw,
|
.bdrv_co_can_store_new_dirty_bitmap = qcow2_co_can_store_new_dirty_bitmap,
|
||||||
.bdrv_can_store_new_dirty_bitmap = qcow2_can_store_new_dirty_bitmap,
|
.bdrv_co_remove_persistent_dirty_bitmap =
|
||||||
.bdrv_remove_persistent_dirty_bitmap = qcow2_remove_persistent_dirty_bitmap,
|
qcow2_co_remove_persistent_dirty_bitmap,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_qcow2_init(void)
|
static void bdrv_qcow2_init(void)
|
||||||
|
|
|
@ -740,19 +740,18 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||||
Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||||
Error **errp);
|
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_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
|
||||||
int qcow2_truncate_bitmaps_check(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);
|
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
||||||
bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||||
const char *name,
|
const char *name,
|
||||||
uint32_t granularity,
|
uint32_t granularity,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
int qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
||||||
const char *name,
|
const char *name,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
ssize_t coroutine_fn
|
ssize_t coroutine_fn
|
||||||
qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size,
|
qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size,
|
||||||
|
|
40
blockdev.c
40
blockdev.c
|
@ -1966,7 +1966,6 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
|
||||||
qmp_block_dirty_bitmap_add(action->node, action->name,
|
qmp_block_dirty_bitmap_add(action->node, action->name,
|
||||||
action->has_granularity, action->granularity,
|
action->has_granularity, action->granularity,
|
||||||
action->has_persistent, action->persistent,
|
action->has_persistent, action->persistent,
|
||||||
action->has_autoload, action->autoload,
|
|
||||||
action->has_disabled, action->disabled,
|
action->has_disabled, action->disabled,
|
||||||
&local_err);
|
&local_err);
|
||||||
|
|
||||||
|
@ -2178,7 +2177,7 @@ static void block_dirty_bitmap_remove_commit(BlkActionState *common)
|
||||||
common, common);
|
common, common);
|
||||||
|
|
||||||
bdrv_dirty_bitmap_set_busy(state->bitmap, false);
|
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)
|
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,
|
void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||||
bool has_granularity, uint32_t granularity,
|
bool has_granularity, uint32_t granularity,
|
||||||
bool has_persistent, bool persistent,
|
bool has_persistent, bool persistent,
|
||||||
bool has_autoload, bool autoload,
|
|
||||||
bool has_disabled, bool disabled,
|
bool has_disabled, bool disabled,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -2890,24 +2888,14 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
||||||
persistent = false;
|
persistent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_autoload) {
|
|
||||||
warn_report("Autoload option is deprecated and its value is ignored");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_disabled) {
|
if (!has_disabled) {
|
||||||
disabled = false;
|
disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (persistent) {
|
if (persistent &&
|
||||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
|
||||||
bool ok;
|
{
|
||||||
|
return;
|
||||||
aio_context_acquire(aio_context);
|
|
||||||
ok = bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp);
|
|
||||||
aio_context_release(aio_context);
|
|
||||||
if (!ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
|
||||||
|
@ -2939,22 +2927,14 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_remove(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
if (bdrv_dirty_bitmap_get_persistence(bitmap) &&
|
||||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0)
|
||||||
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);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (release) {
|
if (release) {
|
||||||
bdrv_release_dirty_bitmap(bs, bitmap);
|
bdrv_release_dirty_bitmap(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap_bs) {
|
if (bitmap_bs) {
|
||||||
|
@ -3086,7 +3066,7 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(
|
||||||
bdrv_merge_dirty_bitmap(dst, anon, backup, errp);
|
bdrv_merge_dirty_bitmap(dst, anon, backup, errp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
bdrv_release_dirty_bitmap(bs, anon);
|
bdrv_release_dirty_bitmap(anon);
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,7 @@ typedef struct HDGeometry {
|
||||||
#define BDRV_BLOCK_EOF 0x20
|
#define BDRV_BLOCK_EOF 0x20
|
||||||
#define BDRV_BLOCK_RECURSE 0x40
|
#define BDRV_BLOCK_RECURSE 0x40
|
||||||
|
|
||||||
typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
||||||
|
|
||||||
typedef struct BDRVReopenState {
|
typedef struct BDRVReopenState {
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
|
|
|
@ -547,19 +547,13 @@ struct BlockDriver {
|
||||||
uint64_t parent_perm, uint64_t parent_shared,
|
uint64_t parent_perm, uint64_t parent_shared,
|
||||||
uint64_t *nperm, uint64_t *nshared);
|
uint64_t *nperm, uint64_t *nshared);
|
||||||
|
|
||||||
/**
|
bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs,
|
||||||
* Bitmaps should be marked as 'IN_USE' in the image on reopening image
|
const char *name,
|
||||||
* as rw. This handler should realize it. It also should unset readonly
|
uint32_t granularity,
|
||||||
* field of BlockDirtyBitmap's in case of success.
|
Error **errp);
|
||||||
*/
|
int (*bdrv_co_remove_persistent_dirty_bitmap)(BlockDriverState *bs,
|
||||||
int (*bdrv_reopen_bitmaps_rw)(BlockDriverState *bs, Error **errp);
|
const char *name,
|
||||||
bool (*bdrv_can_store_new_dirty_bitmap)(BlockDriverState *bs,
|
Error **errp);
|
||||||
const char *name,
|
|
||||||
uint32_t granularity,
|
|
||||||
Error **errp);
|
|
||||||
void (*bdrv_remove_persistent_dirty_bitmap)(BlockDriverState *bs,
|
|
||||||
const char *name,
|
|
||||||
Error **errp);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register/unregister a buffer for I/O. For example, when the driver is
|
* 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,
|
uint32_t granularity,
|
||||||
const char *name,
|
const char *name,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap,
|
||||||
int chunk_size);
|
|
||||||
void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
|
||||||
int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
Error **errp);
|
Error **errp);
|
||||||
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap,
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
Error **errp);
|
Error **errp);
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap);
|
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap);
|
||||||
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs,
|
||||||
const char *name);
|
const char *name);
|
||||||
int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
||||||
Error **errp);
|
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_release_named_dirty_bitmaps(BlockDriverState *bs);
|
||||||
void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||||
const char *name,
|
Error **errp);
|
||||||
Error **errp);
|
|
||||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
||||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);
|
||||||
void bdrv_enable_dirty_bitmap_locked(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);
|
int64_t offset, int64_t bytes);
|
||||||
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t offset, int64_t bytes);
|
int64_t offset, int64_t bytes);
|
||||||
BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap);
|
|
||||||
BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap);
|
BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap);
|
||||||
void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter);
|
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);
|
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);
|
||||||
void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset);
|
void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset);
|
||||||
int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap);
|
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);
|
void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes);
|
||||||
bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap);
|
bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap);
|
||||||
bool bdrv_has_readonly_bitmaps(BlockDriverState *bs);
|
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_persistence(BdrvDirtyBitmap *bitmap);
|
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap);
|
||||||
bool bdrv_dirty_bitmap_inconsistent(const 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 *bdrv_dirty_bitmap_first(BlockDriverState *bs);
|
||||||
BdrvDirtyBitmap *bitmap);
|
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);
|
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
|
||||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
||||||
uint64_t bytes);
|
uint64_t bytes);
|
||||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
||||||
uint64_t *offset, uint64_t *bytes);
|
uint64_t *offset, uint64_t *bytes);
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
BdrvDirtyBitmap *bitmap,
|
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -132,6 +132,11 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count);
|
||||||
* @count: Number of bits to reset.
|
* @count: Number of bits to reset.
|
||||||
*
|
*
|
||||||
* Reset a consecutive range of bits in an HBitmap.
|
* 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);
|
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)) {
|
for (bs = bdrv_next_all_states(NULL); bs; bs = bdrv_next_all_states(bs)) {
|
||||||
const char *name = bdrv_get_device_or_node_name(bs);
|
const char *name = bdrv_get_device_or_node_name(bs);
|
||||||
|
|
||||||
for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
|
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||||
bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
|
|
||||||
{
|
|
||||||
if (!bdrv_dirty_bitmap_name(bitmap)) {
|
if (!bdrv_dirty_bitmap_name(bitmap)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -474,7 +472,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s)
|
||||||
if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) {
|
if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) {
|
||||||
DirtyBitmapLoadBitmapState *b;
|
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) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -535,13 +533,12 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s)
|
||||||
bdrv_dirty_bitmap_lock(s->bitmap);
|
bdrv_dirty_bitmap_lock(s->bitmap);
|
||||||
if (enabled_bitmaps == NULL) {
|
if (enabled_bitmaps == NULL) {
|
||||||
/* in postcopy */
|
/* 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);
|
bdrv_enable_dirty_bitmap_locked(s->bitmap);
|
||||||
} else {
|
} else {
|
||||||
/* target not started, successor must be empty */
|
/* target not started, successor must be empty */
|
||||||
int64_t count = bdrv_get_dirty_count(s->bitmap);
|
int64_t count = bdrv_get_dirty_count(s->bitmap);
|
||||||
BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bs,
|
BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bitmap,
|
||||||
s->bitmap,
|
|
||||||
NULL);
|
NULL);
|
||||||
/* bdrv_reclaim_dirty_bitmap can fail only on no successor (it
|
/* bdrv_reclaim_dirty_bitmap can fail only on no successor (it
|
||||||
* must be) or on merge fail, but merge can't fail when second
|
* must be) or on merge fail, but merge can't fail when second
|
||||||
|
|
|
@ -361,7 +361,7 @@ static int set_dirty_tracking(void)
|
||||||
fail:
|
fail:
|
||||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
||||||
if (bmds->dirty_bitmap) {
|
if (bmds->dirty_bitmap) {
|
||||||
bdrv_release_dirty_bitmap(blk_bs(bmds->blk), bmds->dirty_bitmap);
|
bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -374,7 +374,7 @@ static void unset_dirty_tracking(void)
|
||||||
BlkMigDevState *bmds;
|
BlkMigDevState *bmds;
|
||||||
|
|
||||||
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
|
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
|
# Qcow2 disks support persistent bitmaps. Default is false for
|
||||||
# block-dirty-bitmap-add. (Since: 2.10)
|
# 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
|
# @disabled: the bitmap is created in the disabled state, which means that
|
||||||
# it will not track drive changes. The bitmap may be enabled with
|
# it will not track drive changes. The bitmap may be enabled with
|
||||||
# block-dirty-bitmap-enable. Default is false. (Since: 4.0)
|
# block-dirty-bitmap-enable. Default is false. (Since: 4.0)
|
||||||
|
@ -2064,7 +2060,7 @@
|
||||||
##
|
##
|
||||||
{ 'struct': 'BlockDirtyBitmapAdd',
|
{ 'struct': 'BlockDirtyBitmapAdd',
|
||||||
'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32',
|
'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32',
|
||||||
'*persistent': 'bool', '*autoload': 'bool', '*disabled': 'bool' } }
|
'*persistent': 'bool', '*disabled': 'bool' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockDirtyBitmapMergeSource:
|
# @BlockDirtyBitmapMergeSource:
|
||||||
|
|
|
@ -149,11 +149,6 @@ QEMU 4.1 has three options, please migrate to one of these three:
|
||||||
|
|
||||||
@section QEMU Machine Protocol (QMP) commands
|
@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)
|
@subsection query-block result field dirty-bitmaps[i].status (since 4.0)
|
||||||
|
|
||||||
The ``status'' field of the ``BlockDirtyInfo'' structure, returned by
|
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
|
guarantees must resolve the CPU model aliases using te
|
||||||
``alias-of'' field returned by the ``query-cpu-definitions'' QMP
|
``alias-of'' field returned by the ``query-cpu-definitions'' QMP
|
||||||
command.
|
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.
|
||||||
|
|
|
@ -43,10 +43,10 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
|
||||||
os.remove(disk)
|
os.remove(disk)
|
||||||
|
|
||||||
def mkVm(self):
|
def mkVm(self):
|
||||||
return iotests.VM().add_drive(disk)
|
return iotests.VM().add_drive(disk, opts='node-name=node0')
|
||||||
|
|
||||||
def mkVmRo(self):
|
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):
|
def getSha256(self):
|
||||||
result = self.vm.qmp('x-debug-block-dirty-bitmap-sha256',
|
result = self.vm.qmp('x-debug-block-dirty-bitmap-sha256',
|
||||||
|
@ -102,6 +102,59 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
|
||||||
|
|
||||||
self.vm.shutdown()
|
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__':
|
if __name__ == '__main__':
|
||||||
iotests.main(supported_fmts=['qcow2'],
|
iotests.main(supported_fmts=['qcow2'],
|
||||||
supported_protocols=['file'])
|
supported_protocols=['file'])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.
|
..
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
Ran 1 tests
|
Ran 2 tests
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
89
tests/qemu-iotests/260
Executable file
89
tests/qemu-iotests/260
Executable file
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Tests for temporary external snapshot when we have bitmaps.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
import iotests
|
||||||
|
from iotests import qemu_img_create, file_path, log, filter_qmp_event
|
||||||
|
|
||||||
|
iotests.verify_image_format(supported_fmts=['qcow2'])
|
||||||
|
|
||||||
|
base, top = file_path('base', 'top')
|
||||||
|
size = 64 * 1024 * 3
|
||||||
|
|
||||||
|
|
||||||
|
def print_bitmap(msg, vm):
|
||||||
|
result = vm.qmp('query-block')['return'][0]
|
||||||
|
if 'dirty-bitmaps' in result:
|
||||||
|
bitmap = result['dirty-bitmaps'][0]
|
||||||
|
log('{}: name={} dirty-clusters={}'.format(msg, bitmap['name'],
|
||||||
|
bitmap['count'] // 64 // 1024))
|
||||||
|
else:
|
||||||
|
log(msg + ': not found')
|
||||||
|
|
||||||
|
|
||||||
|
def test(persistent, restart):
|
||||||
|
assert persistent or not restart
|
||||||
|
log("\nTestcase {}persistent {} restart\n".format(
|
||||||
|
'' if persistent else 'non-', 'with' if restart else 'without'))
|
||||||
|
|
||||||
|
qemu_img_create('-f', iotests.imgfmt, base, str(size))
|
||||||
|
|
||||||
|
vm = iotests.VM().add_drive(base)
|
||||||
|
vm.launch()
|
||||||
|
|
||||||
|
vm.qmp_log('block-dirty-bitmap-add', node='drive0', name='bitmap0',
|
||||||
|
persistent=persistent)
|
||||||
|
vm.hmp_qemu_io('drive0', 'write 0 64K')
|
||||||
|
print_bitmap('initial bitmap', vm)
|
||||||
|
|
||||||
|
vm.qmp_log('blockdev-snapshot-sync', device='drive0', snapshot_file=top,
|
||||||
|
format=iotests.imgfmt, filters=[iotests.filter_qmp_testfiles])
|
||||||
|
vm.hmp_qemu_io('drive0', 'write 64K 512')
|
||||||
|
print_bitmap('check that no bitmaps are in snapshot', vm)
|
||||||
|
|
||||||
|
if restart:
|
||||||
|
log("... Restart ...")
|
||||||
|
vm.shutdown()
|
||||||
|
vm = iotests.VM().add_drive(top)
|
||||||
|
vm.launch()
|
||||||
|
|
||||||
|
vm.qmp_log('block-commit', device='drive0', top=top,
|
||||||
|
filters=[iotests.filter_qmp_testfiles])
|
||||||
|
ev = vm.events_wait((('BLOCK_JOB_READY', None),
|
||||||
|
('BLOCK_JOB_COMPLETED', None)))
|
||||||
|
log(filter_qmp_event(ev))
|
||||||
|
if (ev['event'] == 'BLOCK_JOB_COMPLETED'):
|
||||||
|
vm.shutdown()
|
||||||
|
log(vm.get_log())
|
||||||
|
exit()
|
||||||
|
|
||||||
|
vm.qmp_log('block-job-complete', device='drive0')
|
||||||
|
ev = vm.event_wait('BLOCK_JOB_COMPLETED')
|
||||||
|
log(filter_qmp_event(ev))
|
||||||
|
print_bitmap('check bitmap after commit', vm)
|
||||||
|
|
||||||
|
vm.hmp_qemu_io('drive0', 'write 128K 64K')
|
||||||
|
print_bitmap('check updated bitmap', vm)
|
||||||
|
|
||||||
|
vm.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
test(persistent=False, restart=False)
|
||||||
|
test(persistent=True, restart=False)
|
||||||
|
test(persistent=True, restart=True)
|
52
tests/qemu-iotests/260.out
Normal file
52
tests/qemu-iotests/260.out
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
Testcase non-persistent without restart
|
||||||
|
|
||||||
|
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap0", "node": "drive0", "persistent": false}}
|
||||||
|
{"return": {}}
|
||||||
|
initial bitmap: name=bitmap0 dirty-clusters=1
|
||||||
|
{"execute": "blockdev-snapshot-sync", "arguments": {"device": "drive0", "format": "qcow2", "snapshot-file": "TEST_DIR/PID-top"}}
|
||||||
|
{"return": {}}
|
||||||
|
check that no bitmaps are in snapshot: not found
|
||||||
|
{"execute": "block-commit", "arguments": {"device": "drive0", "top": "TEST_DIR/PID-top"}}
|
||||||
|
{"return": {}}
|
||||||
|
{"data": {"device": "drive0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
{"execute": "block-job-complete", "arguments": {"device": "drive0"}}
|
||||||
|
{"return": {}}
|
||||||
|
{"data": {"device": "drive0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
check bitmap after commit: name=bitmap0 dirty-clusters=2
|
||||||
|
check updated bitmap: name=bitmap0 dirty-clusters=3
|
||||||
|
|
||||||
|
Testcase persistent without restart
|
||||||
|
|
||||||
|
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap0", "node": "drive0", "persistent": true}}
|
||||||
|
{"return": {}}
|
||||||
|
initial bitmap: name=bitmap0 dirty-clusters=1
|
||||||
|
{"execute": "blockdev-snapshot-sync", "arguments": {"device": "drive0", "format": "qcow2", "snapshot-file": "TEST_DIR/PID-top"}}
|
||||||
|
{"return": {}}
|
||||||
|
check that no bitmaps are in snapshot: not found
|
||||||
|
{"execute": "block-commit", "arguments": {"device": "drive0", "top": "TEST_DIR/PID-top"}}
|
||||||
|
{"return": {}}
|
||||||
|
{"data": {"device": "drive0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
{"execute": "block-job-complete", "arguments": {"device": "drive0"}}
|
||||||
|
{"return": {}}
|
||||||
|
{"data": {"device": "drive0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
check bitmap after commit: name=bitmap0 dirty-clusters=2
|
||||||
|
check updated bitmap: name=bitmap0 dirty-clusters=3
|
||||||
|
|
||||||
|
Testcase persistent with restart
|
||||||
|
|
||||||
|
{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap0", "node": "drive0", "persistent": true}}
|
||||||
|
{"return": {}}
|
||||||
|
initial bitmap: name=bitmap0 dirty-clusters=1
|
||||||
|
{"execute": "blockdev-snapshot-sync", "arguments": {"device": "drive0", "format": "qcow2", "snapshot-file": "TEST_DIR/PID-top"}}
|
||||||
|
{"return": {}}
|
||||||
|
check that no bitmaps are in snapshot: not found
|
||||||
|
... Restart ...
|
||||||
|
{"execute": "block-commit", "arguments": {"device": "drive0", "top": "TEST_DIR/PID-top"}}
|
||||||
|
{"return": {}}
|
||||||
|
{"data": {"device": "drive0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
{"execute": "block-job-complete", "arguments": {"device": "drive0"}}
|
||||||
|
{"return": {}}
|
||||||
|
{"data": {"device": "drive0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
check bitmap after commit: name=bitmap0 dirty-clusters=2
|
||||||
|
check updated bitmap: name=bitmap0 dirty-clusters=3
|
|
@ -273,6 +273,7 @@
|
||||||
256 rw quick
|
256 rw quick
|
||||||
257 rw
|
257 rw
|
||||||
258 rw quick
|
258 rw quick
|
||||||
|
260 rw quick
|
||||||
262 rw quick migration
|
262 rw quick migration
|
||||||
263 rw quick
|
263 rw quick
|
||||||
265 rw auto quick
|
265 rw auto quick
|
||||||
|
|
|
@ -423,7 +423,7 @@ static void test_hbitmap_granularity(TestHBitmapData *data,
|
||||||
hbitmap_test_check(data, 0);
|
hbitmap_test_check(data, 0);
|
||||||
hbitmap_test_set(data, 0, 3);
|
hbitmap_test_set(data, 0, 3);
|
||||||
g_assert_cmpint(hbitmap_count(data->hb), ==, 4);
|
g_assert_cmpint(hbitmap_count(data->hb), ==, 4);
|
||||||
hbitmap_test_reset(data, 0, 1);
|
hbitmap_test_reset(data, 0, 2);
|
||||||
g_assert_cmpint(hbitmap_count(data->hb), ==, 2);
|
g_assert_cmpint(hbitmap_count(data->hb), ==, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -476,6 +476,10 @@ void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count)
|
||||||
/* Compute range in the last layer. */
|
/* Compute range in the last layer. */
|
||||||
uint64_t first;
|
uint64_t first;
|
||||||
uint64_t last = start + count - 1;
|
uint64_t last = start + count - 1;
|
||||||
|
uint64_t gran = 1ULL << hb->granularity;
|
||||||
|
|
||||||
|
assert(QEMU_IS_ALIGNED(start, gran));
|
||||||
|
assert(QEMU_IS_ALIGNED(count, gran) || (start + count == hb->orig_size));
|
||||||
|
|
||||||
trace_hbitmap_reset(hb, start, count,
|
trace_hbitmap_reset(hb, start, count,
|
||||||
start >> hb->granularity, last >> hb->granularity);
|
start >> hb->granularity, last >> hb->granularity);
|
||||||
|
|
Loading…
Reference in a new issue