From: Kevin Wolf <kwolf@redhat.com>
To: Max Reitz <mreitz@redhat.com>
Cc: qemu-devel@nongnu.org, Stefan Hajnoczi <stefanha@redhat.com>
Subject: Re: [Qemu-devel] [PATCH v5 09/26] qcow2: Helper for refcount array reallocation
Date: Wed, 4 Feb 2015 14:21:38 +0100 [thread overview]
Message-ID: <20150204132138.GC5641@noname.redhat.com> (raw)
In-Reply-To: <1418647857-3589-10-git-send-email-mreitz@redhat.com>
Am 15.12.2014 um 13:50 hat Max Reitz geschrieben:
> 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>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> ---
> block/qcow2-refcount.c | 137 +++++++++++++++++++++++++++++++------------------
> 1 file changed, 88 insertions(+), 49 deletions(-)
>
> diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
> index fd28a13..4ede971 100644
> --- a/block/qcow2-refcount.c
> +++ b/block/qcow2-refcount.c
> @@ -1130,6 +1130,70 @@ 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 DIV_ROUND_UP(entries, 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
Considering this, which implies that a multiplication by 64 can't
overflow, why can't this function be as simple as the following?
assert(entries <= (1 << (64 - 9)));
return DIV_ROUND_UP(entries * s->refcount_bits, 8)
> + * 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);
> + }
> +}
> +
> +/**
> + * 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 and *size
> + * will be set to new_size.
And 0 is 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)
> +{
> + /* 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);
size_to_clusters()? (Unfortunately still not short enough to keep each
initialisation on a single line...)
> + uint16_t *new_ptr;
> +
> + if (new_byte_size == old_byte_size) {
> + *size = new_size;
> + return 0;
> + }
This is only correct if the array was previously allocated by the same
function, or at least rounded up to a cluster boundary. What do we win
by keeping a smaller byte count in *size than is actually allocated? If
we had the real allocation size in it, we wouldn't have to make
assumptions about the real array size.
> + 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.
> @@ -1146,6 +1210,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;
> @@ -1157,23 +1222,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) {
> @@ -1542,8 +1596,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;
> @@ -1560,22 +1613,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;
> @@ -1635,10 +1681,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);
Here the returned new size is thrown away.
With the implementation of realloc_refcount_array() as above this is not
incorrect because it is *nb_clusters anyway when the function succeeds,
but it's a bit sloppy.
If you decide to allow realloc_refcount_array() returning a size bigger
than was requested (i.e. the rounded value is returned) as I suggested
above, then you need to change this call.
> + if (ret < 0) {
> res->check_errors++;
> - return -ENOMEM;
> + return ret;
> }
> }
Kevin
next prev parent reply other threads:[~2015-02-04 13:21 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-12-15 12:50 [Qemu-devel] [PATCH v5 00/26] qcow2: Support refcount orders != 4 Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 01/26] qcow2: Add two new fields to BDRVQcowState Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 02/26] qcow2: Add refcount_bits to format-specific info Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 03/26] qcow2: Do not return new value after refcount update Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 04/26] qcow2: Only return status from qcow2_get_refcount Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 05/26] qcow2: Use unsigned addend for update_refcount() Max Reitz
2015-01-22 15:33 ` Eric Blake
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 06/26] qcow2: Use 64 bits for refcount values Max Reitz
2015-01-22 15:35 ` Eric Blake
2015-02-03 19:26 ` Kevin Wolf
2015-02-03 19:48 ` Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 07/26] qcow2: Respect error in qcow2_alloc_bytes() Max Reitz
2015-02-04 11:40 ` Kevin Wolf
2015-02-04 15:04 ` Max Reitz
2015-02-04 15:12 ` Kevin Wolf
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 08/26] qcow2: Refcount overflow and qcow2_alloc_bytes() Max Reitz
2015-02-04 11:55 ` Kevin Wolf
2015-02-04 15:33 ` Max Reitz
2015-02-04 16:10 ` Kevin Wolf
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 09/26] qcow2: Helper for refcount array reallocation Max Reitz
2015-02-04 13:21 ` Kevin Wolf [this message]
2015-02-04 15:57 ` Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 10/26] qcow2: Helper function for refcount modification Max Reitz
2015-02-04 16:06 ` Kevin Wolf
2015-02-04 17:12 ` Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 11/26] qcow2: More helpers " Max Reitz
2015-02-04 13:53 ` Kevin Wolf
2015-02-04 15:59 ` Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 12/26] qcow2: Open images with refcount order != 4 Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 13/26] qcow2: refcount_order parameter for qcow2_create2 Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 14/26] qcow2: Use symbolic macros in qcow2_amend_options Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 15/26] iotests: Prepare for refcount_bits option Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 16/26] qcow2: Allow creation with refcount order != 4 Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 17/26] progress: Allow regressing progress Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 18/26] block: Add opaque value to the amend CB Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 19/26] qcow2: Use error_report() in qcow2_amend_options() Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 20/26] qcow2: Use abort() instead of assert(false) Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 21/26] qcow2: Split upgrade/downgrade paths for amend Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 22/26] qcow2: Use intermediate helper CB " Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 23/26] qcow2: Add function for refcount order amendment Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 24/26] qcow2: Invoke refcount order amendment function Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 25/26] qcow2: Point to amend function in check Max Reitz
2014-12-15 12:50 ` [Qemu-devel] [PATCH v5 26/26] iotests: Add test for different refcount widths Max Reitz
2015-01-20 22:48 ` [Qemu-devel] [PATCH v5 00/26] qcow2: Support refcount orders != 4 Max Reitz
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=20150204132138.GC5641@noname.redhat.com \
--to=kwolf@redhat.com \
--cc=mreitz@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).