All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.