*/
#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "trace.h"
#include "block/block_int.h"
#include "block/blockjob.h"
+#include "qemu/main-loop.h"
-/**
- * A BdrvDirtyBitmap can be in three possible states:
- * (1) successor is NULL and disabled is false: full r/w mode
- * (2) successor is NULL and disabled is true: read only mode ("disabled")
- * (3) successor is set: frozen mode.
- * A frozen bitmap cannot be renamed, deleted, anonymized, cleared, set,
- * or enabled. A frozen bitmap can only abdicate() or reclaim().
- */
struct BdrvDirtyBitmap {
- QemuMutex *mutex;
+ BlockDriverState *bs;
HBitmap *bitmap; /* Dirty bitmap implementation */
- HBitmap *meta; /* Meta dirty bitmap */
- bool qmp_locked; /* Bitmap is locked, it can't be modified
- through QMP */
- BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
+ bool busy; /* Bitmap is busy, it can't be used via QMP */
+ BdrvDirtyBitmap *successor; /* Anonymous child, if any. */
char *name; /* Optional non-empty unique ID */
int64_t size; /* Size of the bitmap, in bytes */
bool disabled; /* Bitmap is disabled. It ignores all writes to
and this bitmap must remain unchanged while
this flag is set. */
bool persistent; /* bitmap must be saved to owner disk image */
- bool migration; /* Bitmap is selected for migration, it should
- not be stored on the next inactivation
- (persistent flag doesn't matter until next
- invalidation).*/
+ bool inconsistent; /* bitmap is persistent, but inconsistent.
+ It cannot be used at all in any way, except
+ a QMP user can remove it. */
+ bool skip_store; /* We are either migrating or deleting this
+ * bitmap; it should not be stored on the next
+ * inactivation. */
QLIST_ENTRY(BdrvDirtyBitmap) list;
};
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)
{
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
/* Called with BQL or dirty_bitmap lock taken. */
assert(is_power_of_2(granularity) && granularity >= BDRV_SECTOR_SIZE);
- if (name && bdrv_find_dirty_bitmap(bs, name)) {
- error_setg(errp, "Bitmap already exists: %s", name);
- return NULL;
+ if (name) {
+ if (bdrv_find_dirty_bitmap(bs, name)) {
+ error_setg(errp, "Bitmap already exists: %s", name);
+ return NULL;
+ }
+ if (strlen(name) > BDRV_BITMAP_MAX_NAME_SIZE) {
+ error_setg(errp, "Bitmap name too long: %s", name);
+ return NULL;
+ }
}
bitmap_size = bdrv_getlength(bs);
if (bitmap_size < 0) {
return NULL;
}
bitmap = g_new0(BdrvDirtyBitmap, 1);
- bitmap->mutex = &bs->dirty_bitmap_mutex;
+ bitmap->bs = bs;
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity));
bitmap->size = bitmap_size;
bitmap->name = g_strdup(name);
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)
{
return bitmap->size;
}
/* Called with BQL taken. */
-bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
+bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap)
{
return bitmap->successor;
}
-/* Both conditions disallow user-modification via QMP. */
-bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) {
- return bdrv_dirty_bitmap_frozen(bitmap) ||
- bdrv_dirty_bitmap_qmp_locked(bitmap);
-}
-
-void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
+static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
{
- qemu_mutex_lock(bitmap->mutex);
- bitmap->qmp_locked = qmp_locked;
- qemu_mutex_unlock(bitmap->mutex);
+ return bitmap->busy;
}
-bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
+void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
{
- return bitmap->qmp_locked;
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
+ bitmap->busy = busy;
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
/* Called with BQL taken. */
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
{
- return !(bitmap->disabled || bitmap->successor);
+ return !bitmap->disabled;
}
-/* Called with BQL taken. */
+/**
+ * bdrv_dirty_bitmap_status: This API is now deprecated.
+ * Called with BQL taken.
+ *
+ * A BdrvDirtyBitmap can be in four possible user-visible states:
+ * (1) Active: successor is NULL, and disabled is false: full r/w mode
+ * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode,
+ * guest writes are dropped, but monitor writes are possible,
+ * through commands like merge and clear.
+ * (3) Frozen: successor is not NULL.
+ * A frozen bitmap cannot be renamed, deleted, cleared, set,
+ * enabled, merged to, etc. A frozen bitmap can only abdicate()
+ * or reclaim().
+ * In this state, the anonymous successor bitmap may be either
+ * Active and recording writes from the guest (e.g. backup jobs),
+ * or it can be Disabled and not recording writes.
+ * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap
+ * in any way from the monitor.
+ * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and
+ * is unusable by QEMU. It can be deleted to remove it from
+ * the qcow2.
+ */
DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
{
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
+ if (bdrv_dirty_bitmap_inconsistent(bitmap)) {
+ return DIRTY_BITMAP_STATUS_INCONSISTENT;
+ } else if (bdrv_dirty_bitmap_has_successor(bitmap)) {
return DIRTY_BITMAP_STATUS_FROZEN;
- } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
+ } else if (bdrv_dirty_bitmap_busy(bitmap)) {
return DIRTY_BITMAP_STATUS_LOCKED;
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
return DIRTY_BITMAP_STATUS_DISABLED;
}
}
+/* Called with BQL taken. */
+static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap)
+{
+ return !bitmap->disabled || (bitmap->successor &&
+ !bitmap->successor->disabled);
+}
+
+int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
+ Error **errp)
+{
+ if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) {
+ error_setg(errp, "Bitmap '%s' is currently in use by another"
+ " operation and cannot be used", bitmap->name);
+ return -1;
+ }
+
+ if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) {
+ error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
+ bitmap->name);
+ return -1;
+ }
+
+ if ((flags & BDRV_BITMAP_INCONSISTENT) &&
+ bdrv_dirty_bitmap_inconsistent(bitmap)) {
+ error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used",
+ bitmap->name);
+ error_append_hint(errp, "Try block-dirty-bitmap-remove to delete"
+ " this bitmap from disk");
+ return -1;
+ }
+
+ return 0;
+}
+
/**
* Create a successor bitmap destined to replace this bitmap after an operation.
- * Requires that the bitmap is not frozen and has no successor.
+ * Requires that the bitmap is not marked busy and has no successor.
+ * The successor will be enabled if the parent bitmap was.
* Called with BQL taken.
*/
-int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
- BdrvDirtyBitmap *bitmap, Error **errp)
+int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap, Error **errp)
{
uint64_t granularity;
BdrvDirtyBitmap *child;
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
- error_setg(errp, "Cannot create a successor for a bitmap that is "
- "currently frozen");
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
+ return -1;
+ }
+ if (bdrv_dirty_bitmap_has_successor(bitmap)) {
+ error_setg(errp, "Cannot create a successor for a bitmap that already "
+ "has one");
return -1;
}
- assert(!bitmap->successor);
/* Create an anonymous successor */
granularity = bdrv_dirty_bitmap_granularity(bitmap);
- child = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
+ child = bdrv_create_dirty_bitmap(bitmap->bs, granularity, NULL, errp);
if (!child) {
return -1;
}
/* Successor will be on or off based on our current state. */
child->disabled = bitmap->disabled;
+ bitmap->disabled = true;
- /* Install the successor and freeze the parent */
+ /* Install the successor and mark the parent as busy */
bitmap->successor = child;
+ bitmap->busy = true;
return 0;
}
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
{
- assert(!bdrv_dirty_bitmap_frozen(bitmap));
bitmap->disabled = false;
}
/* Called with BQL taken. */
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
{
- assert(bitmap->mutex == bitmap->successor->mutex);
- qemu_mutex_lock(bitmap->mutex);
+ assert(bitmap->bs == bitmap->successor->bs);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
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. */
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
{
assert(!bitmap->active_iterators);
- assert(!bdrv_dirty_bitmap_frozen(bitmap));
- assert(!bitmap->meta);
+ assert(!bdrv_dirty_bitmap_busy(bitmap));
+ assert(!bdrv_dirty_bitmap_has_successor(bitmap));
QLIST_REMOVE(bitmap, list);
hbitmap_free(bitmap->bitmap);
g_free(bitmap->name);
* delete the old bitmap, and return a handle to the new bitmap.
* Called with BQL taken.
*/
-BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
- BdrvDirtyBitmap *bitmap,
+BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap,
Error **errp)
{
char *name;
bitmap->successor = NULL;
successor->persistent = bitmap->persistent;
bitmap->persistent = false;
- bdrv_release_dirty_bitmap(bs, bitmap);
+ bitmap->busy = false;
+ bdrv_release_dirty_bitmap(bitmap);
return successor;
}
/**
* In cases of failure where we can no longer safely delete the parent,
* we may wish to re-join the parent and child/successor.
- * The merged parent will be un-frozen, but not explicitly re-enabled.
+ * The merged parent will be marked as not busy.
+ * The marged parent will be enabled if and only if the successor was enabled.
* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
*/
-BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
- BdrvDirtyBitmap *parent,
+BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent,
Error **errp)
{
BdrvDirtyBitmap *successor = parent->successor;
error_setg(errp, "Merging of parent and successor bitmap failed");
return NULL;
}
+
+ parent->disabled = successor->disabled;
+ parent->busy = false;
bdrv_release_dirty_bitmap_locked(successor);
parent->successor = NULL;
}
/* Called with BQL taken. */
-BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
- BdrvDirtyBitmap *parent,
+BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *parent,
Error **errp)
{
BdrvDirtyBitmap *ret;
- qemu_mutex_lock(parent->mutex);
- ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp);
- qemu_mutex_unlock(parent->mutex);
+ bdrv_dirty_bitmaps_lock(parent->bs);
+ ret = bdrv_reclaim_dirty_bitmap_locked(parent, errp);
+ bdrv_dirty_bitmaps_unlock(parent->bs);
return ret;
}
bdrv_dirty_bitmaps_lock(bs);
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
- assert(!bdrv_dirty_bitmap_frozen(bitmap));
+ assert(!bdrv_dirty_bitmap_busy(bitmap));
+ assert(!bdrv_dirty_bitmap_has_successor(bitmap));
assert(!bitmap->active_iterators);
hbitmap_truncate(bitmap->bitmap, bytes);
bitmap->size = bytes;
}
/* 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_release_dirty_bitmap_locked(bitmap);
bdrv_dirty_bitmaps_unlock(bs);
/**
* Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()).
- * There must not be any frozen bitmaps attached.
+ * There must not be any busy bitmaps attached.
* This function does not remove persistent bitmaps from the storage.
* Called with BQL taken.
*/
* not fail.
* This function doesn't release corresponding BdrvDirtyBitmap.
*/
-void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
- const char *name,
- Error **errp)
+static int coroutine_fn
+bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name,
+ Error **errp)
{
- if (bs->drv && bs->drv->bdrv_remove_persistent_dirty_bitmap) {
- bs->drv->bdrv_remove_persistent_dirty_bitmap(bs, name, errp);
+ if (bs->drv && bs->drv->bdrv_co_remove_persistent_dirty_bitmap) {
+ 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)
{
- bdrv_dirty_bitmap_lock(bitmap);
- assert(!bdrv_dirty_bitmap_frozen(bitmap));
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
bitmap->disabled = true;
- bdrv_dirty_bitmap_unlock(bitmap);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
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_dirty_bitmap_unlock(bitmap);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
info->has_name = !!bm->name;
info->name = g_strdup(bm->name);
info->status = bdrv_dirty_bitmap_status(bm);
+ info->recording = bdrv_dirty_bitmap_recording(bm);
+ info->busy = bdrv_dirty_bitmap_busy(bm);
+ info->persistent = bm->persistent;
+ info->has_inconsistent = bm->inconsistent;
+ info->inconsistent = bm->inconsistent;
entry->value = info;
*plist = entry;
plist = &entry->next;
}
/* Called within bdrv_dirty_bitmap_lock..unlock */
-bool bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
- int64_t offset)
+bool bdrv_dirty_bitmap_get_locked(BdrvDirtyBitmap *bitmap, int64_t offset)
{
- if (bitmap) {
- return hbitmap_get(bitmap->bitmap, offset);
- } else {
- return false;
- }
+ return hbitmap_get(bitmap->bitmap, offset);
+}
+
+bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset)
+{
+ bool ret;
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
+ ret = bdrv_dirty_bitmap_get_locked(bitmap, offset);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
+
+ return ret;
}
/**
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)
{
if (!iter) {
void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
int64_t offset, int64_t bytes)
{
- assert(bdrv_dirty_bitmap_enabled(bitmap));
assert(!bdrv_dirty_bitmap_readonly(bitmap));
hbitmap_set(bitmap->bitmap, offset, bytes);
}
void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
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_dirty_bitmap_unlock(bitmap);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
/* Called within bdrv_dirty_bitmap_lock..unlock */
void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
int64_t offset, int64_t bytes)
{
- assert(bdrv_dirty_bitmap_enabled(bitmap));
assert(!bdrv_dirty_bitmap_readonly(bitmap));
hbitmap_reset(bitmap->bitmap, offset, bytes);
}
void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
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_dirty_bitmap_unlock(bitmap);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
{
assert(!bdrv_dirty_bitmap_readonly(bitmap));
- bdrv_dirty_bitmap_lock(bitmap);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
if (!out) {
hbitmap_reset_all(bitmap->bitmap);
} else {
hbitmap_granularity(backup));
*out = backup;
}
- bdrv_dirty_bitmap_unlock(bitmap);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
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)
{
return bitmap->readonly;
/* Called with BQL taken. */
void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value)
{
- qemu_mutex_lock(bitmap->mutex);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
bitmap->readonly = value;
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
}
/* Called with BQL taken. */
-void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
+void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent)
{
- qemu_mutex_lock(bitmap->mutex);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
bitmap->persistent = persistent;
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
/* Called with BQL taken. */
-void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
+void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
{
- qemu_mutex_lock(bitmap->mutex);
- bitmap->migration = migration;
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
+ assert(bitmap->persistent == true);
+ bitmap->inconsistent = true;
+ bitmap->disabled = true;
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
-bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
+/* Called with BQL taken. */
+void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip)
{
- return bitmap->persistent && !bitmap->migration;
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
+ bitmap->skip_store = skip;
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
-bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
+bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
{
- BdrvDirtyBitmap *bm;
- QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
- if (bm->persistent && !bm->readonly && !bm->migration) {
- return true;
- }
- }
+ return bitmap->persistent && !bitmap->skip_store;
+}
- return false;
+bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap)
+{
+ return bitmap->inconsistent;
}
-BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
- BdrvDirtyBitmap *bitmap)
+BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs)
{
- return bitmap == NULL ? QLIST_FIRST(&bs->dirty_bitmaps) :
- QLIST_NEXT(bitmap, list);
+ return QLIST_FIRST(&bs->dirty_bitmaps);
+}
+
+BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap)
+{
+ return QLIST_NEXT(bitmap, list);
}
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes);
}
+/**
+ * bdrv_merge_dirty_bitmap: merge src into dest.
+ * Ensures permissions on bitmaps are reasonable; use for public API.
+ *
+ * @backup: If provided, make a copy of dest here prior to merge.
+ */
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
HBitmap **backup, Error **errp)
{
bool ret;
- /* only bitmaps from one bds are supported */
- assert(dest->mutex == src->mutex);
-
- qemu_mutex_lock(dest->mutex);
+ bdrv_dirty_bitmaps_lock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_lock(src->bs);
+ }
- if (bdrv_dirty_bitmap_user_locked(dest)) {
- error_setg(errp, "Bitmap '%s' is currently in use by another"
- " operation and cannot be modified", dest->name);
+ if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
goto out;
}
- if (bdrv_dirty_bitmap_readonly(dest)) {
- error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
- dest->name);
+ if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) {
goto out;
}
goto out;
}
+ ret = bdrv_dirty_bitmap_merge_internal(dest, src, backup, false);
+ assert(ret);
+
+out:
+ bdrv_dirty_bitmaps_unlock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_unlock(src->bs);
+ }
+}
+
+/**
+ * bdrv_dirty_bitmap_merge_internal: merge src into dest.
+ * Does NOT check bitmap permissions; not suitable for use as public API.
+ *
+ * @backup: If provided, make a copy of dest here prior to merge.
+ * @lock: If true, lock and unlock bitmaps on the way in/out.
+ * returns true if the merge succeeded; false if unattempted.
+ */
+bool bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
+ const BdrvDirtyBitmap *src,
+ HBitmap **backup,
+ bool lock)
+{
+ bool ret;
+
+ assert(!bdrv_dirty_bitmap_readonly(dest));
+ assert(!bdrv_dirty_bitmap_inconsistent(dest));
+ assert(!bdrv_dirty_bitmap_inconsistent(src));
+
+ if (lock) {
+ bdrv_dirty_bitmaps_lock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_lock(src->bs);
+ }
+ }
+
if (backup) {
*backup = dest->bitmap;
dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
} else {
ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
}
- assert(ret);
-out:
- qemu_mutex_unlock(dest->mutex);
+ if (lock) {
+ bdrv_dirty_bitmaps_unlock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_unlock(src->bs);
+ }
+ }
+
+ return ret;
}