*/
#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "trace.h"
#include "block/block_int.h"
#include "block/blockjob.h"
and this bitmap must remain unchanged while
this flag is set. */
bool persistent; /* bitmap must be saved to owner disk image */
+ bool inconsistent; /* bitmap is persistent, but inconsistent.
+ It cannot be used at all in any way, except
+ a QMP user can remove it. */
bool migration; /* Bitmap is selected for migration, it should
not be stored on the next inactivation
(persistent flag doesn't matter until next
return bitmap->successor;
}
-bool bdrv_dirty_bitmap_busy(BdrvDirtyBitmap *bitmap)
+static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap)
{
return bitmap->busy;
}
* 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_has_successor(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_busy(bitmap)) {
return DIRTY_BITMAP_STATUS_LOCKED;
!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 marked busy and has no successor.
uint64_t granularity;
BdrvDirtyBitmap *child;
- if (bdrv_dirty_bitmap_busy(bitmap)) {
- error_setg(errp, "Cannot create a successor for a bitmap that is "
- "in-use by an operation");
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) {
return -1;
}
if (bdrv_dirty_bitmap_has_successor(bitmap)) {
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 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);
bitmap->persistent = persistent;
qemu_mutex_unlock(bitmap->mutex);
}
+/* Called with BQL taken. */
+void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap)
+{
+ qemu_mutex_lock(bitmap->mutex);
+ assert(bitmap->persistent == true);
+ bitmap->inconsistent = true;
+ bitmap->disabled = true;
+ qemu_mutex_unlock(bitmap->mutex);
+}
+
/* Called with BQL taken. */
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
{
qemu_mutex_unlock(bitmap->mutex);
}
-bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
+bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
{
return bitmap->persistent && !bitmap->migration;
}
+bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap)
+{
+ return bitmap->inconsistent;
+}
+
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
{
BdrvDirtyBitmap *bm;
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);
+ if (src->mutex != dest->mutex) {
+ qemu_mutex_lock(src->mutex);
+ }
- if (bdrv_dirty_bitmap_busy(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:
+ qemu_mutex_unlock(dest->mutex);
+ if (src->mutex != dest->mutex) {
+ qemu_mutex_unlock(src->mutex);
+ }
+}
+
+/**
+ * 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) {
+ qemu_mutex_lock(dest->mutex);
+ if (src->mutex != dest->mutex) {
+ qemu_mutex_lock(src->mutex);
+ }
+ }
+
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) {
+ qemu_mutex_unlock(dest->mutex);
+ if (src->mutex != dest->mutex) {
+ qemu_mutex_unlock(src->mutex);
+ }
+ }
+
+ return ret;
}