qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>,
	Stefan Hajnoczi <stefanha@redhat.com>,
	Max Reitz <mreitz@redhat.com>
Subject: [Qemu-devel] [PATCH v6 07/24] qcow2: Helper for refcount array reallocation
Date: Tue, 10 Feb 2015 15:28:49 -0500	[thread overview]
Message-ID: <1423600146-7642-8-git-send-email-mreitz@redhat.com> (raw)
In-Reply-To: <1423600146-7642-1-git-send-email-mreitz@redhat.com>

Add a helper function for reallocating a refcount array, independent of
the refcount order. The newly allocated space is zeroed and the function
handles failed reallocations gracefully.

The helper function will always align the buffer size to a cluster
boundary; if storing the refcounts in such an array in big endian byte
order, this makes it possible to write parts of the array directly as
refcount blocks into the image file.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2-refcount.c | 130 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 81 insertions(+), 49 deletions(-)

diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index c3f51fa..02b515d 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1107,6 +1107,63 @@ fail:
 /* refcount checking functions */
 
 
+static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries)
+{
+    /* 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, we are well below that limit */
+    assert(entries < (UINT64_C(1) << (64 - 9)));
+
+    /* Thanks to the assertion this will not overflow, because
+     * s->refcount_order < 7.
+     * (note: x << s->refcount_order == x * s->refcount_bits) */
+    return DIV_ROUND_UP(entries << s->refcount_order, 8);
+}
+
+/**
+ * Reallocates *array so that it can hold new_size entries. *size must contain
+ * the current number of entries in *array. If the reallocation fails, *array
+ * and *size will not be modified and -errno will be returned. If the
+ * reallocation is successful, *array will be set to the new buffer, *size
+ * will be set to new_size and 0 will be returned. The size of the reallocated
+ * refcount array buffer will be aligned to a cluster boundary, and the newly
+ * allocated area will be zeroed.
+ */
+static int realloc_refcount_array(BDRVQcowState *s, uint16_t **array,
+                                  int64_t *size, int64_t new_size)
+{
+    size_t old_byte_size, new_byte_size;
+    uint16_t *new_ptr;
+
+    /* Round to clusters so the array can be directly written to disk */
+    old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size))
+                    * s->cluster_size;
+    new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size))
+                    * s->cluster_size;
+
+    if (new_byte_size == old_byte_size) {
+        *size = new_size;
+        return 0;
+    }
+
+    assert(new_byte_size > 0);
+
+    new_ptr = g_try_realloc(*array, new_byte_size);
+    if (!new_ptr) {
+        return -ENOMEM;
+    }
+
+    if (new_byte_size > old_byte_size) {
+        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.
@@ -1123,6 +1180,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;
@@ -1134,23 +1192,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) {
@@ -1519,8 +1566,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;
@@ -1537,22 +1583,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;
@@ -1612,10 +1651,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;
         }
     }
 
@@ -1749,6 +1790,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 */
@@ -1778,28 +1820,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 */
-- 
2.1.0

  parent reply	other threads:[~2015-02-10 20:29 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-10 20:28 [Qemu-devel] [PATCH v6 00/24] qcow2: Support refcount orders != 4 Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 01/24] qcow2: Add two new fields to BDRVQcowState Max Reitz
2015-02-11 13:51   ` Eric Blake
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 02/24] qcow2: Add refcount_bits to format-specific info Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 03/24] qcow2: Do not return new value after refcount update Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 04/24] qcow2: Only return status from qcow2_get_refcount Max Reitz
2015-02-11 15:47   ` Eric Blake
2015-02-17 10:19   ` Kevin Wolf
2015-02-17 16:56     ` Eric Blake
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 05/24] qcow2: Use unsigned addend for update_refcount() Max Reitz
2015-02-11 16:41   ` Eric Blake
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 06/24] qcow2: Use 64 bits for refcount values Max Reitz
2015-02-11 17:01   ` Eric Blake
2015-02-10 20:28 ` Max Reitz [this message]
2015-02-11 18:19   ` [Qemu-devel] [PATCH v6 07/24] qcow2: Helper for refcount array reallocation Eric Blake
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 08/24] qcow2: Helper function for refcount modification Max Reitz
2015-02-11 18:38   ` Eric Blake
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 09/24] qcow2: More helpers " Max Reitz
2015-02-17 13:38   ` Kevin Wolf
2015-02-17 13:54     ` Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 10/24] qcow2: Open images with refcount order != 4 Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 11/24] qcow2: refcount_order parameter for qcow2_create2 Max Reitz
2015-02-18 11:05   ` Kevin Wolf
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 12/24] qcow2: Use symbolic macros in qcow2_amend_options Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 13/24] iotests: Prepare for refcount_bits option Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 14/24] qcow2: Allow creation with refcount order != 4 Max Reitz
2015-02-18 11:01   ` Kevin Wolf
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 15/24] progress: Allow regressing progress Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 16/24] block: Add opaque value to the amend CB Max Reitz
2015-02-10 20:28 ` [Qemu-devel] [PATCH v6 17/24] qcow2: Use error_report() in qcow2_amend_options() Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 18/24] qcow2: Use abort() instead of assert(false) Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 19/24] qcow2: Split upgrade/downgrade paths for amend Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 20/24] qcow2: Use intermediate helper CB " Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 21/24] qcow2: Add function for refcount order amendment Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 22/24] qcow2: Invoke refcount order amendment function Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 23/24] qcow2: Point to amend function in check Max Reitz
2015-02-10 20:29 ` [Qemu-devel] [PATCH v6 24/24] iotests: Add test for different refcount widths Max Reitz
2015-02-18 11:04   ` Kevin Wolf
2015-02-17 11:00 ` [Qemu-devel] [PATCH v6 00/24] qcow2: Support refcount orders != 4 Kevin Wolf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1423600146-7642-8-git-send-email-mreitz@redhat.com \
    --to=mreitz@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).