#include "qemu/main-loop.h"
struct BdrvDirtyBitmap {
- QemuMutex *mutex;
BlockDriverState *bs;
HBitmap *bitmap; /* Dirty bitmap implementation */
bool busy; /* Bitmap is busy, it can't be used via QMP */
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) {
}
bitmap = g_new0(BdrvDirtyBitmap, 1);
bitmap->bs = bs;
- bitmap->mutex = &bs->dirty_bitmap_mutex;
bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity));
bitmap->size = bitmap_size;
bitmap->name = g_strdup(name);
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy)
{
- qemu_mutex_lock(bitmap->mutex);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
bitmap->busy = busy;
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
/* Called with BQL taken. */
return !bitmap->disabled;
}
-/**
- * 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_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;
- } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
- return DIRTY_BITMAP_STATUS_DISABLED;
- } else {
- return DIRTY_BITMAP_STATUS_ACTIVE;
- }
-}
-
/* Called with BQL taken. */
static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *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");
+ " this bitmap from disk\n");
return -1;
}
/* 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. */
return NULL;
}
- if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
- error_setg(errp, "Merging of parent and successor bitmap failed");
- return NULL;
- }
+ hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap);
parent->disabled = successor->disabled;
parent->busy = false;
{
BdrvDirtyBitmap *ret;
- qemu_mutex_lock(parent->mutex);
+ bdrv_dirty_bitmaps_lock(parent->bs);
ret = bdrv_reclaim_dirty_bitmap_locked(parent, errp);
- qemu_mutex_unlock(parent->mutex);
+ bdrv_dirty_bitmaps_unlock(parent->bs);
return ret;
}
}
}
+bool
+bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs)
+{
+ if (bs->drv && bs->drv->bdrv_supports_persistent_dirty_bitmap) {
+ return bs->drv->bdrv_supports_persistent_dirty_bitmap(bs);
+ }
+ return false;
+}
+
static bool coroutine_fn
bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
uint32_t granularity, Error **errp)
bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
uint32_t granularity, Error **errp)
{
+ IO_CODE();
if (qemu_in_coroutine()) {
return bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp);
} else {
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
{
- bdrv_dirty_bitmap_lock(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)
{
BdrvDirtyBitmap *bm;
BlockDirtyInfoList *list = NULL;
- BlockDirtyInfoList **plist = &list;
+ BlockDirtyInfoList **tail = &list;
bdrv_dirty_bitmaps_lock(bs);
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1);
- BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1);
+
info->count = bdrv_get_dirty_count(bm);
info->granularity = bdrv_dirty_bitmap_granularity(bm);
- 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;
+ QAPI_LIST_APPEND(tail, info);
}
bdrv_dirty_bitmaps_unlock(bs);
bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset)
{
bool ret;
- bdrv_dirty_bitmap_lock(bitmap);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
ret = bdrv_dirty_bitmap_get_locked(bitmap, offset);
- bdrv_dirty_bitmap_unlock(bitmap);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
return ret;
}
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(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)
{
+ IO_CODE();
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)
{
HBitmap *tmp = bitmap->bitmap;
assert(!bdrv_dirty_bitmap_readonly(bitmap));
+ GLOBAL_STATE_CODE();
bitmap->bitmap = backup;
hbitmap_free(tmp);
}
return hbitmap_serialization_align(bitmap->bitmap);
}
+/* Return the disk size covered by a chunk of serialized bitmap data. */
+uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
+ const BdrvDirtyBitmap *bitmap)
+{
+ uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
+ uint64_t limit = granularity * (serialized_chunk_size << 3);
+
+ assert(QEMU_IS_ALIGNED(limit,
+ bdrv_dirty_bitmap_serialization_align(bitmap)));
+ return limit;
+}
+
+
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
uint8_t *buf, uint64_t offset,
uint64_t bytes)
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes)
{
BdrvDirtyBitmap *bitmap;
+ IO_CODE();
if (QLIST_EMPTY(&bs->dirty_bitmaps)) {
return;
/* 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)
return false;
}
+bool bdrv_has_named_bitmaps(BlockDriverState *bs)
+{
+ BdrvDirtyBitmap *bm;
+
+ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
+ if (bdrv_dirty_bitmap_name(bm)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Called with BQL taken. */
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_inconsistent(BdrvDirtyBitmap *bitmap)
{
- qemu_mutex_lock(bitmap->mutex);
+ bdrv_dirty_bitmaps_lock(bitmap->bs);
assert(bitmap->persistent == true);
bitmap->inconsistent = true;
bitmap->disabled = true;
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
/* Called with BQL taken. */
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;
- qemu_mutex_unlock(bitmap->mutex);
+ bdrv_dirty_bitmaps_unlock(bitmap->bs);
}
bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap)
return bitmap->inconsistent;
}
-bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
+BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs)
{
- BdrvDirtyBitmap *bm;
- QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
- if (bm->persistent && !bm->readonly && !bm->skip_store) {
- return true;
- }
- }
-
- return false;
+ return QLIST_FIRST(&bs->dirty_bitmaps);
}
-BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
- BdrvDirtyBitmap *bitmap)
+BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap)
{
- return bitmap == NULL ? QLIST_FIRST(&bs->dirty_bitmaps) :
- QLIST_NEXT(bitmap, list);
+ return QLIST_NEXT(bitmap, list);
}
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
return hbitmap_sha256(bitmap->bitmap, errp);
}
-int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
- uint64_t bytes)
+int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset,
+ int64_t bytes)
+{
+ return hbitmap_next_dirty(bitmap->bitmap, offset, bytes);
+}
+
+int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset,
+ int64_t bytes)
{
return hbitmap_next_zero(bitmap->bitmap, offset, bytes);
}
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
- uint64_t *offset, uint64_t *bytes)
+ int64_t start, int64_t end, int64_t max_dirty_count,
+ int64_t *dirty_start, int64_t *dirty_count)
+{
+ return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count,
+ dirty_start, dirty_count);
+}
+
+bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset,
+ int64_t bytes, int64_t *count)
{
- return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes);
+ return hbitmap_status(bitmap->bitmap, offset, bytes, count);
}
/**
* Ensures permissions on bitmaps are reasonable; use for public API.
*
* @backup: If provided, make a copy of dest here prior to merge.
+ *
+ * Returns true on success, false on failure. In case of failure bitmaps are
+ * untouched.
*/
-void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
+bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
HBitmap **backup, Error **errp)
{
- bool ret;
+ bool ret = false;
- qemu_mutex_lock(dest->mutex);
- if (src->mutex != dest->mutex) {
- qemu_mutex_lock(src->mutex);
+ bdrv_dirty_bitmaps_lock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_lock(src->bs);
}
if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) {
goto out;
}
- if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
- error_setg(errp, "Bitmaps are incompatible and can't be merged");
+ if (bdrv_dirty_bitmap_size(src) != bdrv_dirty_bitmap_size(dest)) {
+ error_setg(errp, "Bitmaps are of different sizes (destination size is %"
+ PRId64 ", source size is %" PRId64 ") and can't be merged",
+ bdrv_dirty_bitmap_size(dest), bdrv_dirty_bitmap_size(src));
goto out;
}
- ret = bdrv_dirty_bitmap_merge_internal(dest, src, backup, false);
- assert(ret);
+ bdrv_dirty_bitmap_merge_internal(dest, src, backup, false);
+ ret = true;
out:
- qemu_mutex_unlock(dest->mutex);
- if (src->mutex != dest->mutex) {
- qemu_mutex_unlock(src->mutex);
+ bdrv_dirty_bitmaps_unlock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_unlock(src->bs);
}
+
+ return ret;
}
/**
* bdrv_dirty_bitmap_merge_internal: merge src into dest.
* Does NOT check bitmap permissions; not suitable for use as public API.
+ * @dest, @src and @backup (if not NULL) must have same size.
*
* @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,
+void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest,
const BdrvDirtyBitmap *src,
HBitmap **backup,
bool lock)
{
- bool ret;
+ IO_CODE();
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);
+ 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));
- ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
+ hbitmap_merge(*backup, src->bitmap, dest->bitmap);
} else {
- ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
+ hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
}
if (lock) {
- qemu_mutex_unlock(dest->mutex);
- if (src->mutex != dest->mutex) {
- qemu_mutex_unlock(src->mutex);
+ bdrv_dirty_bitmaps_unlock(dest->bs);
+ if (src->bs != dest->bs) {
+ bdrv_dirty_bitmaps_unlock(src->bs);
}
}
-
- return ret;
}