*/
#include "qemu/osdep.h"
+#include "block/block-io.h"
+#include "block/dirty-bitmap.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#define BME_MIN_GRANULARITY_BITS 9
#define BME_MAX_NAME_SIZE 1023
+/* Size of bitmap table entries */
+#define BME_TABLE_ENTRY_SIZE (sizeof(uint64_t))
+
QEMU_BUILD_BUG_ON(BME_MAX_NAME_SIZE != BDRV_BITMAP_MAX_NAME_SIZE);
#if BME_MAX_TABLE_SIZE * 8ULL > INT_MAX
}
assert(tb->size <= BME_MAX_TABLE_SIZE);
- ret = bdrv_pread(bs->file, tb->offset,
- table, tb->size * sizeof(uint64_t));
+ ret = bdrv_pread(bs->file, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE,
+ table, 0);
if (ret < 0) {
goto fail;
}
}
clear_bitmap_table(bs, bitmap_table, tb->size);
- qcow2_free_clusters(bs, tb->offset, tb->size * sizeof(uint64_t),
+ qcow2_free_clusters(bs, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE,
QCOW2_DISCARD_OTHER);
g_free(bitmap_table);
return 0;
}
-/* Return the disk size covered by a single qcow2 cluster of bitmap data. */
-static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s,
- const BdrvDirtyBitmap *bitmap)
-{
- uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
- uint64_t limit = granularity * (s->cluster_size << 3);
-
- assert(QEMU_IS_ALIGNED(limit,
- bdrv_dirty_bitmap_serialization_align(bitmap)));
- return limit;
-}
-
/* load_bitmap_data
* @bitmap_table entries must satisfy specification constraints.
* @bitmap must be cleared */
}
buf = g_malloc(s->cluster_size);
- limit = bytes_covered_by_bitmap_cluster(s, bitmap);
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
uint64_t count = MIN(bm_size - offset, limit);
uint64_t entry = bitmap_table[i];
* already cleared */
}
} else {
- ret = bdrv_pread(bs->file, data_offset, buf, s->cluster_size);
+ ret = bdrv_pread(bs->file, data_offset, s->cluster_size, buf, 0);
if (ret < 0) {
goto finish;
}
}
dir_end = dir + size;
- ret = bdrv_pread(bs->file, offset, dir, size);
+ ret = bdrv_pread(bs->file, offset, size, dir, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to read bitmap directory");
goto fail;
return bm_list;
broken_dir:
- ret = -EINVAL;
error_setg(errp, "Broken bitmap directory");
fail:
ret = qcow2_inc_refcounts_imrt(bs, res,
refcount_table, refcount_table_size,
bm->table.offset,
- bm->table.size * sizeof(uint64_t));
+ bm->table.size * BME_TABLE_ENTRY_SIZE);
if (ret < 0) {
goto out;
}
}
}
- /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not
- * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap
- * directory in-place (actually, turn-off the extension), which is checked
- * in qcow2_check_metadata_overlap() */
+ /* Actually, even in the in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY
+ * is not necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating
+ * bitmap directory in-place (actually, turn-off the extension), which is
+ * checked in qcow2_check_metadata_overlap() */
ret = qcow2_pre_write_overlap_check(
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size,
false);
goto fail;
}
- ret = bdrv_pwrite(bs->file, dir_offset, dir, dir_size);
+ ret = bdrv_pwrite(bs->file, dir_offset, dir_size, dir, 0);
if (ret < 0) {
goto fail;
}
bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value);
}
-/* qcow2_load_dirty_bitmaps()
- * Return value is a hint for caller: true means that the Qcow2 header was
- * updated. (false doesn't mean that the header should be updated by the
- * caller, it just means that updating was not needed or the image cannot be
- * written to).
- * On failure the function returns false.
+/*
+ * Return true on success, false on failure.
+ * If header_updated is not NULL then it is set appropriately regardless of
+ * the return value.
*/
-bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
+bool coroutine_fn qcow2_load_dirty_bitmaps(BlockDriverState *bs,
+ bool *header_updated, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
Qcow2BitmapList *bm_list;
Qcow2Bitmap *bm;
GSList *created_dirty_bitmaps = NULL;
- bool header_updated = false;
bool needs_update = false;
+ if (header_updated) {
+ *header_updated = false;
+ }
+
if (s->nb_bitmaps == 0) {
/* No bitmaps - nothing to do */
- return false;
+ return true;
}
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
error_setg_errno(errp, -ret, "Can't update bitmap directory");
goto fail;
}
- header_updated = true;
+ if (header_updated) {
+ *header_updated = true;
+ }
}
if (!can_write(bs)) {
g_slist_free(created_dirty_bitmaps);
bitmap_list_free(bm_list);
- return header_updated;
+ return true;
fail:
g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags)
{
Qcow2BitmapInfoFlagsList *list = NULL;
- Qcow2BitmapInfoFlagsList **plist = &list;
+ Qcow2BitmapInfoFlagsList **tail = &list;
int i;
static const struct {
for (i = 0; i < map_size; ++i) {
if (flags & map[i].bme) {
- Qcow2BitmapInfoFlagsList *entry =
- g_new0(Qcow2BitmapInfoFlagsList, 1);
- entry->value = map[i].info;
- *plist = entry;
- plist = &entry->next;
+ QAPI_LIST_APPEND(tail, map[i].info);
flags &= ~map[i].bme;
}
}
/*
* qcow2_get_bitmap_info_list()
* Returns a list of QCOW2 bitmap details.
- * In case of no bitmaps, the function returns NULL and
- * the @errp parameter is not set.
- * When bitmap information can not be obtained, the function returns
- * NULL and the @errp parameter is set.
+ * On success return true with info_list set (note, that if there are no
+ * bitmaps, info_list is set to NULL).
+ * On failure return false with errp set.
*/
-Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
- Error **errp)
+bool qcow2_get_bitmap_info_list(BlockDriverState *bs,
+ Qcow2BitmapInfoList **info_list, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
Qcow2BitmapList *bm_list;
Qcow2Bitmap *bm;
- Qcow2BitmapInfoList *list = NULL;
- Qcow2BitmapInfoList **plist = &list;
+ Qcow2BitmapInfoList **tail;
if (s->nb_bitmaps == 0) {
- return NULL;
+ *info_list = NULL;
+ return true;
}
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
s->bitmap_directory_size, errp);
- if (bm_list == NULL) {
- return NULL;
+ if (!bm_list) {
+ return false;
}
+ *info_list = NULL;
+ tail = info_list;
+
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1);
- Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1);
info->granularity = 1U << bm->granularity_bits;
info->name = g_strdup(bm->name);
info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS);
- obj->value = info;
- *plist = obj;
- plist = &obj->next;
+ QAPI_LIST_APPEND(tail, info);
}
bitmap_list_free(bm_list);
- return list;
+ return true;
}
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
}
}
- g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
+ g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, (gpointer)false);
ret = 0;
out:
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
const char *bm_name = bdrv_dirty_bitmap_name(bitmap);
uint8_t *buf = NULL;
- BdrvDirtyBitmapIter *dbi;
uint64_t *tb;
uint64_t tb_size =
size_to_clusters(s,
return NULL;
}
- dbi = bdrv_dirty_iter_new(bitmap);
buf = g_malloc(s->cluster_size);
- limit = bytes_covered_by_bitmap_cluster(s, bitmap);
+ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
- while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
+ offset = 0;
+ while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX))
+ >= 0)
+ {
uint64_t cluster = offset / limit;
uint64_t end, write_size;
int64_t off;
goto fail;
}
- ret = bdrv_pwrite(bs->file, off, buf, s->cluster_size);
+ ret = bdrv_pwrite(bs->file, off, s->cluster_size, buf, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file",
bm_name);
goto fail;
}
- if (end >= bm_size) {
- break;
- }
-
- bdrv_set_dirty_iter(dbi, end);
+ offset = end;
}
*bitmap_table_size = tb_size;
g_free(buf);
- bdrv_dirty_iter_free(dbi);
return tb;
fail:
clear_bitmap_table(bs, tb, tb_size);
g_free(buf);
- bdrv_dirty_iter_free(dbi);
g_free(tb);
return NULL;
}
bitmap_table_to_be(tb, tb_size);
- ret = bdrv_pwrite(bs->file, tb_offset, tb, tb_size * sizeof(tb[0]));
+ ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file",
bm_name);
Qcow2BitmapList *bm_list;
if (s->nb_bitmaps == 0) {
- /* Absence of the bitmap is not an error: see explanation above
- * bdrv_remove_persistent_dirty_bitmap() definition. */
+ /*
+ * Absence of the bitmap is not an error: see explanation above
+ * bdrv_co_remove_persistent_dirty_bitmap() definition.
+ */
return 0;
}
bm = find_bitmap_by_name(bm_list, name);
if (bm == NULL) {
- ret = -EINVAL;
+ /* Absence of the bitmap is not an error, see above. */
+ ret = 0;
goto out;
}
* 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 qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
bool release_stored, Error **errp)
{
+ ERRP_GUARD();
BdrvDirtyBitmap *bitmap;
BDRVQcow2State *s = bs->opaque;
uint32_t new_nb_bitmaps = s->nb_bitmaps;
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
s->bitmap_directory_size, errp);
if (bm_list == NULL) {
- return;
+ return false;
}
}
Qcow2Bitmap *bm;
if (!bdrv_dirty_bitmap_get_persistence(bitmap) ||
- bdrv_dirty_bitmap_readonly(bitmap) ||
bdrv_dirty_bitmap_inconsistent(bitmap)) {
continue;
}
+ if (bdrv_dirty_bitmap_readonly(bitmap)) {
+ /*
+ * Store the bitmap in the associated Qcow2Bitmap so it
+ * can be released later
+ */
+ bm = find_bitmap_by_name(bm_list, name);
+ if (bm) {
+ bm->dirty_bitmap = bitmap;
+ }
+ continue;
+ }
+
need_write = true;
if (check_constraints_on_bitmap(bs, name, granularity, errp) < 0) {
/* allocate clusters and store bitmaps */
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
- if (bm->dirty_bitmap == NULL) {
+ BdrvDirtyBitmap *bitmap = bm->dirty_bitmap;
+
+ if (bitmap == NULL || bdrv_dirty_bitmap_readonly(bitmap)) {
continue;
}
g_free(tb);
}
+success:
if (release_stored) {
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
if (bm->dirty_bitmap == NULL) {
}
}
-success:
bitmap_list_free(bm_list);
- return;
+ return true;
fail:
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
- if (bm->dirty_bitmap == NULL || bm->table.offset == 0) {
+ if (bm->dirty_bitmap == NULL || bm->table.offset == 0 ||
+ bdrv_dirty_bitmap_readonly(bm->dirty_bitmap))
+ {
continue;
}
}
bitmap_list_free(bm_list);
+ return false;
}
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
{
BdrvDirtyBitmap *bitmap;
- Error *local_err = NULL;
- qcow2_store_persistent_dirty_bitmaps(bs, false, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
+ if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) {
return -EINVAL;
}
Error **errp)
{
BDRVQcow2State *s = bs->opaque;
- bool found;
- Qcow2BitmapList *bm_list;
+ BdrvDirtyBitmap *bitmap;
+ uint64_t bitmap_directory_size = 0;
+ uint32_t nb_bitmaps = 0;
+
+ if (bdrv_find_dirty_bitmap(bs, name)) {
+ error_setg(errp, "Bitmap already exists: %s", name);
+ return false;
+ }
if (s->qcow_version < 3) {
/* Without autoclear_features, we would always have to assume
goto fail;
}
- if (s->nb_bitmaps == 0) {
- return true;
+ FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
+ if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
+ nb_bitmaps++;
+ bitmap_directory_size +=
+ calc_dir_entry_size(strlen(bdrv_dirty_bitmap_name(bitmap)), 0);
+ }
}
+ nb_bitmaps++;
+ bitmap_directory_size += calc_dir_entry_size(strlen(name), 0);
- if (s->nb_bitmaps >= QCOW2_MAX_BITMAPS) {
+ if (nb_bitmaps > QCOW2_MAX_BITMAPS) {
error_setg(errp,
"Maximum number of persistent bitmaps is already reached");
goto fail;
}
- if (s->bitmap_directory_size + calc_dir_entry_size(strlen(name), 0) >
- QCOW2_MAX_BITMAP_DIRECTORY_SIZE)
- {
+ if (bitmap_directory_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) {
error_setg(errp, "Not enough space in the bitmap directory");
goto fail;
}
- qemu_co_mutex_lock(&s->lock);
- bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
- s->bitmap_directory_size, errp);
- qemu_co_mutex_unlock(&s->lock);
- if (bm_list == NULL) {
- goto fail;
- }
-
- found = find_bitmap_by_name(bm_list, name);
- bitmap_list_free(bm_list);
- if (found) {
- error_setg(errp, "Bitmap with the same name is already stored");
- goto fail;
- }
-
return true;
fail:
name, bdrv_get_device_or_node_name(bs));
return false;
}
+
+bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs)
+{
+ BDRVQcow2State *s = bs->opaque;
+
+ return s->qcow_version >= 3;
+}
+
+/*
+ * Compute the space required to copy bitmaps from @in_bs.
+ *
+ * The computation is based as if copying to a new image with the
+ * given @cluster_size, which may differ from the cluster size in
+ * @in_bs; in fact, @in_bs might be something other than qcow2.
+ */
+uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *in_bs,
+ uint32_t cluster_size)
+{
+ uint64_t bitmaps_size = 0;
+ BdrvDirtyBitmap *bm;
+ size_t bitmap_dir_size = 0;
+
+ FOR_EACH_DIRTY_BITMAP(in_bs, bm) {
+ if (bdrv_dirty_bitmap_get_persistence(bm)) {
+ const char *name = bdrv_dirty_bitmap_name(bm);
+ uint32_t granularity = bdrv_dirty_bitmap_granularity(bm);
+ uint64_t bmbytes =
+ get_bitmap_bytes_needed(bdrv_dirty_bitmap_size(bm),
+ granularity);
+ uint64_t bmclusters = DIV_ROUND_UP(bmbytes, cluster_size);
+
+ /* Assume the entire bitmap is allocated */
+ bitmaps_size += bmclusters * cluster_size;
+ /* Also reserve space for the bitmap table entries */
+ bitmaps_size += ROUND_UP(bmclusters * BME_TABLE_ENTRY_SIZE,
+ cluster_size);
+ /* And space for contribution to bitmap directory size */
+ bitmap_dir_size += calc_dir_entry_size(strlen(name), 0);
+ }
+ }
+ bitmaps_size += ROUND_UP(bitmap_dir_size, cluster_size);
+
+ return bitmaps_size;
+}