[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v2 06/21] qcow2: Helper for refcount array reallocat
From: |
Max Reitz |
Subject: |
[Qemu-devel] [PATCH v2 06/21] qcow2: Helper for refcount array reallocation |
Date: |
Fri, 14 Nov 2014 14:05:59 +0100 |
Add a helper function for reallocating a refcount array, independently
of the refcount order. The newly allocated space is zeroed and the
function handles failed reallocations gracefully.
Signed-off-by: Max Reitz <address@hidden>
---
block/qcow2-refcount.c | 121 +++++++++++++++++++++++++++++--------------------
1 file changed, 72 insertions(+), 49 deletions(-)
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 66c78c0..18fcd0d 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1108,6 +1108,54 @@ fail:
/* refcount checking functions */
+static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries)
+{
+ if (s->refcount_order < 3) {
+ /* sub-byte width */
+ int shift = 3 - s->refcount_order;
+ return (entries + (1 << shift) - 1) >> shift;
+ } else if (s->refcount_order == 3) {
+ /* byte width */
+ return entries;
+ } else {
+ /* multiple bytes wide */
+
+ /* This assertion holds because there is no way we can address more
than
+ * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and
because
+ * offsets have to be representable in bytes); due to every cluster
+ * corresponding to one refcount entry and because refcount_order has
to
+ * be below 7, we are far below that limit */
+ assert(!(entries >> (64 - (s->refcount_order - 3))));
+
+ return entries << (s->refcount_order - 3);
+ }
+}
+
+static int realloc_refcount_array(BDRVQcowState *s, uint16_t **array,
+ int64_t *size, int64_t new_size)
+{
+ /* Round to clusters so the array can be directly written to disk */
+ size_t old_byte_size = ROUND_UP(refcount_array_byte_size(s, *size),
+ s->cluster_size);
+ size_t new_byte_size = ROUND_UP(refcount_array_byte_size(s, new_size),
+ s->cluster_size);
+ uint16_t *new_ptr;
+
+ new_ptr = g_try_realloc(*array, new_byte_size);
+ if (new_byte_size && !new_ptr) {
+ return -ENOMEM;
+ }
+
+ if (new_ptr) {
+ memset((void *)((uintptr_t)new_ptr + old_byte_size), 0,
+ new_byte_size - old_byte_size);
+ }
+
+ *array = new_ptr;
+ *size = new_size;
+
+ return 0;
+}
/*
* Increases the refcount for a range of clusters in a given refcount table.
@@ -1124,6 +1172,7 @@ static int inc_refcounts(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
uint64_t start, last, cluster_offset, k;
+ int ret;
if (size <= 0) {
return 0;
@@ -1135,23 +1184,12 @@ static int inc_refcounts(BlockDriverState *bs,
cluster_offset += s->cluster_size) {
k = cluster_offset >> s->cluster_bits;
if (k >= *refcount_table_size) {
- int64_t old_refcount_table_size = *refcount_table_size;
- uint16_t *new_refcount_table;
-
- *refcount_table_size = k + 1;
- new_refcount_table = g_try_realloc(*refcount_table,
- *refcount_table_size *
- sizeof(**refcount_table));
- if (!new_refcount_table) {
- *refcount_table_size = old_refcount_table_size;
+ ret = realloc_refcount_array(s, refcount_table,
+ refcount_table_size, k + 1);
+ if (ret < 0) {
res->check_errors++;
- return -ENOMEM;
+ return ret;
}
- *refcount_table = new_refcount_table;
-
- memset(*refcount_table + old_refcount_table_size, 0,
- (*refcount_table_size - old_refcount_table_size) *
- sizeof(**refcount_table));
}
if (++(*refcount_table)[k] == 0) {
@@ -1518,8 +1556,7 @@ static int check_refblocks(BlockDriverState *bs,
BdrvCheckResult *res,
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
if (fix & BDRV_FIX_ERRORS) {
- int64_t old_nb_clusters = *nb_clusters;
- uint16_t *new_refcount_table;
+ int64_t new_nb_clusters;
if (offset > INT64_MAX - s->cluster_size) {
ret = -EINVAL;
@@ -1536,22 +1573,15 @@ static int check_refblocks(BlockDriverState *bs,
BdrvCheckResult *res,
goto resize_fail;
}
- *nb_clusters = size_to_clusters(s, size);
- assert(*nb_clusters >= old_nb_clusters);
+ new_nb_clusters = size_to_clusters(s, size);
+ assert(new_nb_clusters >= *nb_clusters);
- new_refcount_table = g_try_realloc(*refcount_table,
- *nb_clusters *
- sizeof(**refcount_table));
- if (!new_refcount_table) {
- *nb_clusters = old_nb_clusters;
+ ret = realloc_refcount_array(s, refcount_table,
+ nb_clusters, new_nb_clusters);
+ if (ret < 0) {
res->check_errors++;
- return -ENOMEM;
+ return ret;
}
- *refcount_table = new_refcount_table;
-
- memset(*refcount_table + old_nb_clusters, 0,
- (*nb_clusters - old_nb_clusters) *
- sizeof(**refcount_table));
if (cluster >= *nb_clusters) {
ret = -EINVAL;
@@ -1611,10 +1641,12 @@ static int calculate_refcounts(BlockDriverState *bs,
BdrvCheckResult *res,
int ret;
if (!*refcount_table) {
- *refcount_table = g_try_new0(uint16_t, *nb_clusters);
- if (*nb_clusters && *refcount_table == NULL) {
+ int64_t old_size = 0;
+ ret = realloc_refcount_array(s, refcount_table,
+ &old_size, *nb_clusters);
+ if (ret < 0) {
res->check_errors++;
- return -ENOMEM;
+ return ret;
}
}
@@ -1746,6 +1778,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs,
int64_t cluster = *first_free_cluster, i;
bool first_gap = true;
int contiguous_free_clusters;
+ int ret;
/* Starting at *first_free_cluster, find a range of at least cluster_count
* continuously free clusters */
@@ -1775,28 +1808,18 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs,
/* If no such range could be found, grow the in-memory refcount table
* accordingly to append free clusters at the end of the image */
if (contiguous_free_clusters < cluster_count) {
- int64_t old_imrt_nb_clusters = *imrt_nb_clusters;
- uint16_t *new_refcount_table;
-
/* contiguous_free_clusters clusters are already empty at the image
end;
* we need cluster_count clusters; therefore, we have to allocate
* cluster_count - contiguous_free_clusters new clusters at the end of
* the image (which is the current value of cluster; note that cluster
* may exceed old_imrt_nb_clusters if *first_free_cluster pointed
beyond
* the image end) */
- *imrt_nb_clusters = cluster + cluster_count - contiguous_free_clusters;
- new_refcount_table = g_try_realloc(*refcount_table,
- *imrt_nb_clusters *
- sizeof(**refcount_table));
- if (!new_refcount_table) {
- *imrt_nb_clusters = old_imrt_nb_clusters;
- return -ENOMEM;
- }
- *refcount_table = new_refcount_table;
-
- memset(*refcount_table + old_imrt_nb_clusters, 0,
- (*imrt_nb_clusters - old_imrt_nb_clusters) *
- sizeof(**refcount_table));
+ ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters,
+ cluster + cluster_count
+ - contiguous_free_clusters);
+ if (ret < 0) {
+ return ret;
+ }
}
/* Go back to the first free cluster */
--
1.9.3
- [Qemu-devel] [PATCH v2 00/21] qcow2: Support refcount orders != 4, Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 01/21] qcow2: Add two new fields to BDRVQcowState, Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 02/21] qcow2: Add refcount_width to format-specific info, Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 03/21] qcow2: Use 64 bits for refcount values, Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 04/21] qcow2: Respect error in qcow2_alloc_bytes(), Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 05/21] qcow2: Refcount overflow and qcow2_alloc_bytes(), Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 06/21] qcow2: Helper for refcount array reallocation,
Max Reitz <=
- [Qemu-devel] [PATCH v2 07/21] qcow2: Helper function for refcount modification, Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 08/21] qcow2: More helpers for refcount modification, Max Reitz, 2014/11/14
- [Qemu-devel] [PATCH v2 09/21] qcow2: Open images with refcount order != 4, Max Reitz, 2014/11/14