qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v5 09/26] qcow2: Helper for refcount array reall


From: Kevin Wolf
Subject: Re: [Qemu-devel] [PATCH v5 09/26] qcow2: Helper for refcount array reallocation
Date: Wed, 4 Feb 2015 14:21:38 +0100
User-agent: Mutt/1.5.21 (2010-09-15)

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 <address@hidden>
> Reviewed-by: Eric Blake <address@hidden>
> ---
>  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 via email to

[Prev in Thread] Current Thread [Next in Thread]