diff mbox

[v2,2/9] qcow2: Factor out refcount accounting for check

Message ID 1408115786-13640-3-git-send-email-mreitz@redhat.com
State New
Headers show

Commit Message

Max Reitz Aug. 15, 2014, 3:16 p.m. UTC
Put the code for calculating the reference counts during qemu-img check
into an own function.

Also, do not use g_realloc() for increasing the size of the in-memory
refcount table, but rather g_try_realloc().

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

Comments

Benoît Canet Aug. 21, 2014, 6:47 p.m. UTC | #1
On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
> Put the code for calculating the reference counts during qemu-img check
> into an own function.
> 
> Also, do not use g_realloc() for increasing the size of the in-memory
> refcount table, but rather g_try_realloc().
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2-refcount.c | 188 ++++++++++++++++++++++++++++++-------------------
>  1 file changed, 115 insertions(+), 73 deletions(-)
> 
> diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
> index d60e2fe..b3c4edd 100644
> --- a/block/qcow2-refcount.c
> +++ b/block/qcow2-refcount.c
> @@ -1496,69 +1496,15 @@ done:
>  }
>  
>  /*
> - * Checks an image for refcount consistency.
> - *
> - * Returns 0 if no errors are found, the number of errors in case the image is
> - * detected as corrupted, and -errno when an internal error occurred.
> + * Checks consistency of refblocks and accounts for each refblock in
> + * *refcount_table.
>   */
> -int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
> -                          BdrvCheckMode fix)
> +static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
> +                           BdrvCheckMode fix, uint16_t **refcount_table,
> +                           int64_t *nb_clusters)
>  {
>      BDRVQcowState *s = bs->opaque;
> -    int64_t size, i, highest_cluster, nb_clusters;
> -    int refcount1, refcount2;
> -    QCowSnapshot *sn;
> -    uint16_t *refcount_table;
> -    int ret;
> -
> -    size = bdrv_getlength(bs->file);
> -    if (size < 0) {
> -        res->check_errors++;
> -        return size;
> -    }
> -
> -    nb_clusters = size_to_clusters(s, size);
> -    if (nb_clusters > INT_MAX) {
> -        res->check_errors++;
> -        return -EFBIG;
> -    }
> -
> -    refcount_table = g_try_malloc0(nb_clusters * sizeof(uint16_t));
> -    if (nb_clusters && refcount_table == NULL) {
> -        res->check_errors++;
> -        return -ENOMEM;
> -    }
> -
> -    res->bfi.total_clusters =
> -        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
> -
> -    /* header */
> -    inc_refcounts(bs, res, refcount_table, nb_clusters,
> -        0, s->cluster_size);
> -
> -    /* current L1 table */
> -    ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
> -                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
> -    if (ret < 0) {
> -        goto fail;
> -    }
> -
> -    /* snapshots */
> -    for(i = 0; i < s->nb_snapshots; i++) {
> -        sn = s->snapshots + i;
> -        ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
> -            sn->l1_table_offset, sn->l1_size, 0);
> -        if (ret < 0) {
> -            goto fail;
> -        }
> -    }
> -    inc_refcounts(bs, res, refcount_table, nb_clusters,
> -        s->snapshots_offset, s->snapshots_size);
> -
> -    /* refcount data */
> -    inc_refcounts(bs, res, refcount_table, nb_clusters,
> -        s->refcount_table_offset,
> -        s->refcount_table_size * sizeof(uint64_t));
> +    int64_t i;
>  
>      for(i = 0; i < s->refcount_table_size; i++) {
>          uint64_t offset, cluster;
> @@ -1573,7 +1519,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>              continue;
>          }
>  
> -        if (cluster >= nb_clusters) {
> +        if (cluster >= *nb_clusters) {
>              fprintf(stderr, "ERROR refcount block %" PRId64
>                      " is outside image\n", i);
>              res->corruptions++;
> @@ -1581,14 +1527,14 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>          }
>  
>          if (offset != 0) {
> -            inc_refcounts(bs, res, refcount_table, nb_clusters,
> +            inc_refcounts(bs, res, *refcount_table, *nb_clusters,
>                  offset, s->cluster_size);
> -            if (refcount_table[cluster] != 1) {
> +            if ((*refcount_table)[cluster] != 1) {
>                  fprintf(stderr, "%s refcount block %" PRId64
>                      " refcount=%d\n",
>                      fix & BDRV_FIX_ERRORS ? "Repairing" :
>                                              "ERROR",
> -                    i, refcount_table[cluster]);
> +                    i, (*refcount_table)[cluster]);
>  
>                  if (fix & BDRV_FIX_ERRORS) {
>                      int64_t new_offset;
> @@ -1600,17 +1546,24 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>                      }
>  
>                      /* update refcounts */
> -                    if ((new_offset >> s->cluster_bits) >= nb_clusters) {
> +                    if ((new_offset >> s->cluster_bits) >= *nb_clusters) {
>                          /* increase refcount_table size if necessary */
> -                        int old_nb_clusters = nb_clusters;
> -                        nb_clusters = (new_offset >> s->cluster_bits) + 1;
> -                        refcount_table = g_realloc(refcount_table,
> -                                nb_clusters * sizeof(uint16_t));
> -                        memset(&refcount_table[old_nb_clusters], 0, (nb_clusters
> -                                - old_nb_clusters) * sizeof(uint16_t));
> +                        int old_nb_clusters = *nb_clusters;
> +                        *nb_clusters = (new_offset >> s->cluster_bits) + 1;
> +
> +                        *refcount_table = g_try_realloc(*refcount_table,
> +                                *nb_clusters * sizeof(uint16_t));
> +                        if (!*refcount_table) {
> +                            res->check_errors++;
> +                            return -ENOMEM;
> +                        }
> +
> +                        memset(&(*refcount_table)[old_nb_clusters], 0,
> +                               (*nb_clusters - old_nb_clusters) *
> +                               sizeof(uint16_t));
>                      }
> -                    refcount_table[cluster]--;
> -                    inc_refcounts(bs, res, refcount_table, nb_clusters,
> +                    (*refcount_table)[cluster]--;
> +                    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
>                              new_offset, s->cluster_size);
>  
>                      res->corruptions_fixed++;
> @@ -1621,6 +1574,95 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>          }
>      }
>  
> +    return 0;
> +}
> +
> +/*
> + * Calculates an in-memory refcount table.
> + */
> +static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
> +                               BdrvCheckMode fix, uint16_t **refcount_table,
> +                               int64_t *nb_clusters)
> +{
> +    BDRVQcowState *s = bs->opaque;
> +    QCowSnapshot *sn;
> +    int64_t i;
> +    int ret;
> +
> +    if (!*refcount_table) {
> +        *refcount_table = g_try_malloc0(*nb_clusters * sizeof(uint16_t));
> +        if (*nb_clusters && !*refcount_table) {
> +            res->check_errors++;
> +            return -ENOMEM;
> +        }
> +    }
> +
> +    /* header */
> +    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
> +        0, s->cluster_size);
> +
> +    /* current L1 table */
> +    ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
> +                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    /* snapshots */
> +    for (i = 0; i < s->nb_snapshots; i++) {
> +        sn = s->snapshots + i;
> +        ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
> +            sn->l1_table_offset, sn->l1_size, 0);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +    }
> +    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
> +        s->snapshots_offset, s->snapshots_size);
> +
> +    /* refcount data */
> +    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
> +        s->refcount_table_offset,
> +        s->refcount_table_size * sizeof(uint64_t));
> +
> +    return check_refblocks(bs, res, fix, refcount_table, nb_clusters);
> +}
> +
> +/*
> + * Checks an image for refcount consistency.
> + *
> + * Returns 0 if no errors are found, the number of errors in case the image is
> + * detected as corrupted, and -errno when an internal error occurred.
> + */
> +int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
> +                          BdrvCheckMode fix)
> +{
> +    BDRVQcowState *s = bs->opaque;
> +    int64_t size, i, highest_cluster, nb_clusters;
> +    int refcount1, refcount2;
> +    uint16_t *refcount_table = NULL;
> +    int ret;
> +
> +    size = bdrv_getlength(bs->file);
> +    if (size < 0) {
> +        res->check_errors++;
> +        return size;
> +    }
> +
> +    nb_clusters = size_to_clusters(s, size);
> +    if (nb_clusters > INT_MAX) {
> +        res->check_errors++;
> +        return -EFBIG;
> +    }
> +
> +    res->bfi.total_clusters =
> +        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
> +
> +    ret = calculate_refcounts(bs, res, fix, &refcount_table, &nb_clusters);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +
>      /* compare ref counts */
>      for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
>          refcount1 = get_refcount(bs, i);
> -- 
> 2.0.3
> 

I tried during half an hour to find a git option (--patience etc) or a tool
(Kompare) showing me that these code motions are right.

The problem is that you extract subfunctions and move them up at the same time.

I will try again tomorow to find something making sense of this patch without
having to spell check every single character.

Best regards

Benoît
Benoît Canet Aug. 21, 2014, 6:57 p.m. UTC | #2
On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
> Put the code for calculating the reference counts during qemu-img check
> into an own function.
> 
> Also, do not use g_realloc() for increasing the size of the in-memory
> refcount table, but rather g_try_realloc().
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2-refcount.c | 188 ++++++++++++++++++++++++++++++-------------------
>  1 file changed, 115 insertions(+), 73 deletions(-)
> 
> diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
> index d60e2fe..b3c4edd 100644
> --- a/block/qcow2-refcount.c
> +++ b/block/qcow2-refcount.c
> @@ -1496,69 +1496,15 @@ done:
>  }
>  
>  /*
> - * Checks an image for refcount consistency.
> - *
> - * Returns 0 if no errors are found, the number of errors in case the image is
> - * detected as corrupted, and -errno when an internal error occurred.
> + * Checks consistency of refblocks and accounts for each refblock in
> + * *refcount_table.
>   */
> -int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
> -                          BdrvCheckMode fix)
> +static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
> +                           BdrvCheckMode fix, uint16_t **refcount_table,
> +                           int64_t *nb_clusters)
>  {
>      BDRVQcowState *s = bs->opaque;
> -    int64_t size, i, highest_cluster, nb_clusters;
> -    int refcount1, refcount2;
> -    QCowSnapshot *sn;
> -    uint16_t *refcount_table;
> -    int ret;
> -
> -    size = bdrv_getlength(bs->file);
> -    if (size < 0) {
> -        res->check_errors++;
> -        return size;
> -    }
> -
> -    nb_clusters = size_to_clusters(s, size);
> -    if (nb_clusters > INT_MAX) {
> -        res->check_errors++;
> -        return -EFBIG;
> -    }
> -
> -    refcount_table = g_try_malloc0(nb_clusters * sizeof(uint16_t));
> -    if (nb_clusters && refcount_table == NULL) {
> -        res->check_errors++;
> -        return -ENOMEM;
> -    }
> -
> -    res->bfi.total_clusters =
> -        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
> -
> -    /* header */
> -    inc_refcounts(bs, res, refcount_table, nb_clusters,
> -        0, s->cluster_size);
> -
> -    /* current L1 table */
> -    ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
> -                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
> -    if (ret < 0) {
> -        goto fail;
> -    }
> -
> -    /* snapshots */
> -    for(i = 0; i < s->nb_snapshots; i++) {
> -        sn = s->snapshots + i;
> -        ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
> -            sn->l1_table_offset, sn->l1_size, 0);
> -        if (ret < 0) {
> -            goto fail;
> -        }
> -    }
> -    inc_refcounts(bs, res, refcount_table, nb_clusters,
> -        s->snapshots_offset, s->snapshots_size);
> -
> -    /* refcount data */
> -    inc_refcounts(bs, res, refcount_table, nb_clusters,
> -        s->refcount_table_offset,
> -        s->refcount_table_size * sizeof(uint64_t));
> +    int64_t i;
>  
>      for(i = 0; i < s->refcount_table_size; i++) {
>          uint64_t offset, cluster;
> @@ -1573,7 +1519,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>              continue;
>          }
>  
> -        if (cluster >= nb_clusters) {
> +        if (cluster >= *nb_clusters) {
>              fprintf(stderr, "ERROR refcount block %" PRId64
>                      " is outside image\n", i);
>              res->corruptions++;
> @@ -1581,14 +1527,14 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>          }
>  
>          if (offset != 0) {
> -            inc_refcounts(bs, res, refcount_table, nb_clusters,
> +            inc_refcounts(bs, res, *refcount_table, *nb_clusters,
>                  offset, s->cluster_size);
> -            if (refcount_table[cluster] != 1) {
> +            if ((*refcount_table)[cluster] != 1) {
>                  fprintf(stderr, "%s refcount block %" PRId64
>                      " refcount=%d\n",
>                      fix & BDRV_FIX_ERRORS ? "Repairing" :
>                                              "ERROR",
> -                    i, refcount_table[cluster]);
> +                    i, (*refcount_table)[cluster]);
>  
>                  if (fix & BDRV_FIX_ERRORS) {
>                      int64_t new_offset;
> @@ -1600,17 +1546,24 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>                      }
>  
>                      /* update refcounts */
> -                    if ((new_offset >> s->cluster_bits) >= nb_clusters) {
> +                    if ((new_offset >> s->cluster_bits) >= *nb_clusters) {
>                          /* increase refcount_table size if necessary */
> -                        int old_nb_clusters = nb_clusters;
> -                        nb_clusters = (new_offset >> s->cluster_bits) + 1;
> -                        refcount_table = g_realloc(refcount_table,
> -                                nb_clusters * sizeof(uint16_t));
> -                        memset(&refcount_table[old_nb_clusters], 0, (nb_clusters
> -                                - old_nb_clusters) * sizeof(uint16_t));
> +                        int old_nb_clusters = *nb_clusters;
> +                        *nb_clusters = (new_offset >> s->cluster_bits) + 1;
> +
> +                        *refcount_table = g_try_realloc(*refcount_table,
> +                                *nb_clusters * sizeof(uint16_t));
> +                        if (!*refcount_table) {
> +                            res->check_errors++;
> +                            return -ENOMEM;
> +                        }
> +
> +                        memset(&(*refcount_table)[old_nb_clusters], 0,
> +                               (*nb_clusters - old_nb_clusters) *
> +                               sizeof(uint16_t));
>                      }
> -                    refcount_table[cluster]--;
> -                    inc_refcounts(bs, res, refcount_table, nb_clusters,
> +                    (*refcount_table)[cluster]--;
> +                    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
>                              new_offset, s->cluster_size);
>  
>                      res->corruptions_fixed++;
> @@ -1621,6 +1574,95 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
>          }
>      }
>  
> +    return 0;
> +}
> +
> +/*
> + * Calculates an in-memory refcount table.
> + */
> +static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
> +                               BdrvCheckMode fix, uint16_t **refcount_table,
> +                               int64_t *nb_clusters)
> +{
> +    BDRVQcowState *s = bs->opaque;
> +    QCowSnapshot *sn;
> +    int64_t i;
> +    int ret;
> +
> +    if (!*refcount_table) {
> +        *refcount_table = g_try_malloc0(*nb_clusters * sizeof(uint16_t));
> +        if (*nb_clusters && !*refcount_table) {
> +            res->check_errors++;
> +            return -ENOMEM;
> +        }
> +    }
> +
> +    /* header */
> +    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
> +        0, s->cluster_size);
> +
> +    /* current L1 table */
> +    ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
> +                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    /* snapshots */
> +    for (i = 0; i < s->nb_snapshots; i++) {
> +        sn = s->snapshots + i;
> +        ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
> +            sn->l1_table_offset, sn->l1_size, 0);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +    }
> +    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
> +        s->snapshots_offset, s->snapshots_size);
> +
> +    /* refcount data */
> +    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
> +        s->refcount_table_offset,
> +        s->refcount_table_size * sizeof(uint64_t));
> +
> +    return check_refblocks(bs, res, fix, refcount_table, nb_clusters);
> +}
> +
> +/*
> + * Checks an image for refcount consistency.
> + *
> + * Returns 0 if no errors are found, the number of errors in case the image is
> + * detected as corrupted, and -errno when an internal error occurred.
> + */
> +int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
> +                          BdrvCheckMode fix)
> +{
> +    BDRVQcowState *s = bs->opaque;
> +    int64_t size, i, highest_cluster, nb_clusters;
> +    int refcount1, refcount2;
> +    uint16_t *refcount_table = NULL;
> +    int ret;
> +
> +    size = bdrv_getlength(bs->file);
> +    if (size < 0) {
> +        res->check_errors++;
> +        return size;
> +    }
> +
> +    nb_clusters = size_to_clusters(s, size);
> +    if (nb_clusters > INT_MAX) {
> +        res->check_errors++;
> +        return -EFBIG;
> +    }
> +
> +    res->bfi.total_clusters =
> +        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
> +
> +    ret = calculate_refcounts(bs, res, fix, &refcount_table, &nb_clusters);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +
>      /* compare ref counts */
>      for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
>          refcount1 = get_refcount(bs, i);
> -- 
> 2.0.3
> 

Another point is that I think these two extractions patches will totaly confuse
git blame the day we will need it.

Best regards

Benoît
Eric Blake Aug. 21, 2014, 7:13 p.m. UTC | #3
On 08/21/2014 12:57 PM, Benoît Canet wrote:
> On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
>> Put the code for calculating the reference counts during qemu-img check
>> into an own function.
>>
>> Also, do not use g_realloc() for increasing the size of the in-memory
>> refcount table, but rather g_try_realloc().

The "Also," may be a sign of doing two things in one patch that could
have been two.

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

But overall, this patch seems like a reasonable refactor to me,
splitting out a reusable piece.

> Another point is that I think these two extractions patches will totaly confuse
> git blame the day we will need it.

I disagree.  When refactoring to split a large function into multiple
smaller functions, where the original function calls out to a helper
function for what it used to do inline, git blame tracks things
perfectly.  Someone following blame may end up on this commit as the
source of a given line in its new location, but the blame viewer will
also show this commit is a refactor, and it should be fairly easy to
find where the line was moved from, and resume blaming even further.
Benoît Canet Aug. 21, 2014, 9:16 p.m. UTC | #4
On Thu, Aug 21, 2014 at 01:13:20PM -0600, Eric Blake wrote:
> On 08/21/2014 12:57 PM, Benoît Canet wrote:
> > On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
> >> Put the code for calculating the reference counts during qemu-img check
> >> into an own function.
> >>
> >> Also, do not use g_realloc() for increasing the size of the in-memory
> >> refcount table, but rather g_try_realloc().
> 
> The "Also," may be a sign of doing two things in one patch that could
> have been two.
> 
> >>
> >> Signed-off-by: Max Reitz <mreitz@redhat.com>
> >> ---
> >>  block/qcow2-refcount.c | 188 ++++++++++++++++++++++++++++++-------------------
> >>  1 file changed, 115 insertions(+), 73 deletions(-)
> 
> But overall, this patch seems like a reasonable refactor to me,
> splitting out a reusable piece.
> 
> > Another point is that I think these two extractions patches will totaly confuse
> > git blame the day we will need it.
> 
> I disagree.  When refactoring to split a large function into multiple
> smaller functions, where the original function calls out to a helper
> function for what it used to do inline, git blame tracks things
> perfectly.  Someone following blame may end up on this commit as the
> source of a given line in its new location, but the blame viewer will
> also show this commit is a refactor, and it should be fairly easy to
> find where the line was moved from, and resume blaming even further.

I think we could separate the extractions and the respectives moves into their own
patches.
This way the extraction patch would be cleaner (no code disapearing and appearing
elsewere with another author) and the operations could be reviewed more easily
with the various code review tools.

> 
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
>
Max Reitz Aug. 22, 2014, 3:26 p.m. UTC | #5
On 21.08.2014 23:16, Benoît Canet wrote:
> On Thu, Aug 21, 2014 at 01:13:20PM -0600, Eric Blake wrote:
>> On 08/21/2014 12:57 PM, Benoît Canet wrote:
>>> On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
>>>> Put the code for calculating the reference counts during qemu-img check
>>>> into an own function.
>>>>
>>>> Also, do not use g_realloc() for increasing the size of the in-memory
>>>> refcount table, but rather g_try_realloc().
>> The "Also," may be a sign of doing two things in one patch that could
>> have been two.

The second change fit really well into this patch, so I left it in. On 
the other hand, I just noticed I don't need to do that at all, because 
that call will be dropped in patch 5 anyway.

>>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>>> ---
>>>>   block/qcow2-refcount.c | 188 ++++++++++++++++++++++++++++++-------------------
>>>>   1 file changed, 115 insertions(+), 73 deletions(-)
>> But overall, this patch seems like a reasonable refactor to me,
>> splitting out a reusable piece.
>>
>>> Another point is that I think these two extractions patches will totaly confuse
>>> git blame the day we will need it.
>> I disagree.  When refactoring to split a large function into multiple
>> smaller functions, where the original function calls out to a helper
>> function for what it used to do inline, git blame tracks things
>> perfectly.  Someone following blame may end up on this commit as the
>> source of a given line in its new location, but the blame viewer will
>> also show this commit is a refactor, and it should be fairly easy to
>> find where the line was moved from, and resume blaming even further.
> I think we could separate the extractions and the respectives moves into their own
> patches.
> This way the extraction patch would be cleaner (no code disapearing and appearing
> elsewere with another author) and the operations could be reviewed more easily
> with the various code review tools.

The main cause for the diff cluttering is making nb_clusters and 
refcount_table pointers in the function. Unfortunately (or maybe 
fortunately?) this is not C++, so I cannot make them references, but 
they need to be passed by reference. The function absolutely must be 
able to change their values and I don't see any way of avoiding this 
even in the first extraction patch.

I'll do my best on the rest, but the diff not being trivial is mainly 
simply due to the movement not being absolutely trivial; all accesses to 
nb_clusters and refcount_table have to be modified.

Max
Benoît Canet Aug. 22, 2014, 3:37 p.m. UTC | #6
On Fri, Aug 22, 2014 at 05:26:45PM +0200, Max Reitz wrote:
> On 21.08.2014 23:16, Benoît Canet wrote:
> >On Thu, Aug 21, 2014 at 01:13:20PM -0600, Eric Blake wrote:
> >>On 08/21/2014 12:57 PM, Benoît Canet wrote:
> >>>On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
> >>>>Put the code for calculating the reference counts during qemu-img check
> >>>>into an own function.
> >>>>
> >>>>Also, do not use g_realloc() for increasing the size of the in-memory
> >>>>refcount table, but rather g_try_realloc().
> >>The "Also," may be a sign of doing two things in one patch that could
> >>have been two.
> 
> The second change fit really well into this patch, so I left it in.
> On the other hand, I just noticed I don't need to do that at all,
> because that call will be dropped in patch 5 anyway.
> 
> >>>>Signed-off-by: Max Reitz <mreitz@redhat.com>
> >>>>---
> >>>>  block/qcow2-refcount.c | 188 ++++++++++++++++++++++++++++++-------------------
> >>>>  1 file changed, 115 insertions(+), 73 deletions(-)
> >>But overall, this patch seems like a reasonable refactor to me,
> >>splitting out a reusable piece.
> >>
> >>>Another point is that I think these two extractions patches will totaly confuse
> >>>git blame the day we will need it.
> >>I disagree.  When refactoring to split a large function into multiple
> >>smaller functions, where the original function calls out to a helper
> >>function for what it used to do inline, git blame tracks things
> >>perfectly.  Someone following blame may end up on this commit as the
> >>source of a given line in its new location, but the blame viewer will
> >>also show this commit is a refactor, and it should be fairly easy to
> >>find where the line was moved from, and resume blaming even further.
> >I think we could separate the extractions and the respectives moves into their own
> >patches.
> >This way the extraction patch would be cleaner (no code disapearing and appearing
> >elsewere with another author) and the operations could be reviewed more easily
> >with the various code review tools.
> 
> The main cause for the diff cluttering is making nb_clusters and
> refcount_table pointers in the function. Unfortunately (or maybe
> fortunately?) this is not C++, so I cannot make them references, but
> they need to be passed by reference. The function absolutely must be
> able to change their values and I don't see any way of avoiding this
> even in the first extraction patch.

Do a:  git show f75c744 | kompare -

You will see a whole block of code being morphed to int64_t i and the same whole
block reapear below.
I think this is a consequence of the extracted functions moving up at the same time
they are extracted.

Best regards

Benoît

> 
> I'll do my best on the rest, but the diff not being trivial is
> mainly simply due to the movement not being absolutely trivial; all
> accesses to nb_clusters and refcount_table have to be modified.
> 
> Max
Max Reitz Aug. 22, 2014, 3:44 p.m. UTC | #7
On 22.08.2014 17:37, Benoît Canet wrote:
> On Fri, Aug 22, 2014 at 05:26:45PM +0200, Max Reitz wrote:
>> On 21.08.2014 23:16, Benoît Canet wrote:
>>> On Thu, Aug 21, 2014 at 01:13:20PM -0600, Eric Blake wrote:
>>>> On 08/21/2014 12:57 PM, Benoît Canet wrote:
>>>>> On Fri, Aug 15, 2014 at 05:16:19PM +0200, Max Reitz wrote:
>>>>>> Put the code for calculating the reference counts during qemu-img check
>>>>>> into an own function.
>>>>>>
>>>>>> Also, do not use g_realloc() for increasing the size of the in-memory
>>>>>> refcount table, but rather g_try_realloc().
>>>> The "Also," may be a sign of doing two things in one patch that could
>>>> have been two.
>> The second change fit really well into this patch, so I left it in.
>> On the other hand, I just noticed I don't need to do that at all,
>> because that call will be dropped in patch 5 anyway.
>>
>>>>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>>>>> ---
>>>>>>   block/qcow2-refcount.c | 188 ++++++++++++++++++++++++++++++-------------------
>>>>>>   1 file changed, 115 insertions(+), 73 deletions(-)
>>>> But overall, this patch seems like a reasonable refactor to me,
>>>> splitting out a reusable piece.
>>>>
>>>>> Another point is that I think these two extractions patches will totaly confuse
>>>>> git blame the day we will need it.
>>>> I disagree.  When refactoring to split a large function into multiple
>>>> smaller functions, where the original function calls out to a helper
>>>> function for what it used to do inline, git blame tracks things
>>>> perfectly.  Someone following blame may end up on this commit as the
>>>> source of a given line in its new location, but the blame viewer will
>>>> also show this commit is a refactor, and it should be fairly easy to
>>>> find where the line was moved from, and resume blaming even further.
>>> I think we could separate the extractions and the respectives moves into their own
>>> patches.
>>> This way the extraction patch would be cleaner (no code disapearing and appearing
>>> elsewere with another author) and the operations could be reviewed more easily
>>> with the various code review tools.
>> The main cause for the diff cluttering is making nb_clusters and
>> refcount_table pointers in the function. Unfortunately (or maybe
>> fortunately?) this is not C++, so I cannot make them references, but
>> they need to be passed by reference. The function absolutely must be
>> able to change their values and I don't see any way of avoiding this
>> even in the first extraction patch.
> Do a:  git show f75c744 | kompare -
>
> You will see a whole block of code being morphed to int64_t i and the same whole
> block reapear below.
> I think this is a consequence of the extracted functions moving up at the same time
> they are extracted.

This is not a consequence of the extracted functions moving up, but 
because of check_refblocks() preceding calculate_refcounts(). Splitting 
the check won't help; and I know that Eric dislikes 
(forward-)declarations of static functions (which I have to do if I pull 
calculate_refcounts() before check_refblocks()).

Max
Max Reitz Aug. 22, 2014, 3:46 p.m. UTC | #8
On 22.08.2014 17:44, Max Reitz wrote:
> [snip]
>
> […] Splitting the check won't help; […]

Sorry, I meant “patch” instead of “check”.

Max
Eric Blake Aug. 22, 2014, 3:48 p.m. UTC | #9
On 08/22/2014 09:44 AM, Max Reitz wrote:
> This is not a consequence of the extracted functions moving up, but
> because of check_refblocks() preceding calculate_refcounts(). Splitting
> the check won't help; and I know that Eric dislikes
> (forward-)declarations of static functions (which I have to do if I pull
> calculate_refcounts() before check_refblocks()).

I may not like them in the end result, but they are perfectly fine for
an intermediate patch that is refactoring things, when coupled with a
followup patch that moves the refactored code into topological order.

Anything we can do to make reviewing easier, even if it requires going
through more commits and more lines of churn to get there, pays off in
the long run.
diff mbox

Patch

diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index d60e2fe..b3c4edd 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1496,69 +1496,15 @@  done:
 }
 
 /*
- * Checks an image for refcount consistency.
- *
- * Returns 0 if no errors are found, the number of errors in case the image is
- * detected as corrupted, and -errno when an internal error occurred.
+ * Checks consistency of refblocks and accounts for each refblock in
+ * *refcount_table.
  */
-int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
-                          BdrvCheckMode fix)
+static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
+                           BdrvCheckMode fix, uint16_t **refcount_table,
+                           int64_t *nb_clusters)
 {
     BDRVQcowState *s = bs->opaque;
-    int64_t size, i, highest_cluster, nb_clusters;
-    int refcount1, refcount2;
-    QCowSnapshot *sn;
-    uint16_t *refcount_table;
-    int ret;
-
-    size = bdrv_getlength(bs->file);
-    if (size < 0) {
-        res->check_errors++;
-        return size;
-    }
-
-    nb_clusters = size_to_clusters(s, size);
-    if (nb_clusters > INT_MAX) {
-        res->check_errors++;
-        return -EFBIG;
-    }
-
-    refcount_table = g_try_malloc0(nb_clusters * sizeof(uint16_t));
-    if (nb_clusters && refcount_table == NULL) {
-        res->check_errors++;
-        return -ENOMEM;
-    }
-
-    res->bfi.total_clusters =
-        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
-
-    /* header */
-    inc_refcounts(bs, res, refcount_table, nb_clusters,
-        0, s->cluster_size);
-
-    /* current L1 table */
-    ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
-                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
-    if (ret < 0) {
-        goto fail;
-    }
-
-    /* snapshots */
-    for(i = 0; i < s->nb_snapshots; i++) {
-        sn = s->snapshots + i;
-        ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
-            sn->l1_table_offset, sn->l1_size, 0);
-        if (ret < 0) {
-            goto fail;
-        }
-    }
-    inc_refcounts(bs, res, refcount_table, nb_clusters,
-        s->snapshots_offset, s->snapshots_size);
-
-    /* refcount data */
-    inc_refcounts(bs, res, refcount_table, nb_clusters,
-        s->refcount_table_offset,
-        s->refcount_table_size * sizeof(uint64_t));
+    int64_t i;
 
     for(i = 0; i < s->refcount_table_size; i++) {
         uint64_t offset, cluster;
@@ -1573,7 +1519,7 @@  int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
             continue;
         }
 
-        if (cluster >= nb_clusters) {
+        if (cluster >= *nb_clusters) {
             fprintf(stderr, "ERROR refcount block %" PRId64
                     " is outside image\n", i);
             res->corruptions++;
@@ -1581,14 +1527,14 @@  int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
         }
 
         if (offset != 0) {
-            inc_refcounts(bs, res, refcount_table, nb_clusters,
+            inc_refcounts(bs, res, *refcount_table, *nb_clusters,
                 offset, s->cluster_size);
-            if (refcount_table[cluster] != 1) {
+            if ((*refcount_table)[cluster] != 1) {
                 fprintf(stderr, "%s refcount block %" PRId64
                     " refcount=%d\n",
                     fix & BDRV_FIX_ERRORS ? "Repairing" :
                                             "ERROR",
-                    i, refcount_table[cluster]);
+                    i, (*refcount_table)[cluster]);
 
                 if (fix & BDRV_FIX_ERRORS) {
                     int64_t new_offset;
@@ -1600,17 +1546,24 @@  int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
                     }
 
                     /* update refcounts */
-                    if ((new_offset >> s->cluster_bits) >= nb_clusters) {
+                    if ((new_offset >> s->cluster_bits) >= *nb_clusters) {
                         /* increase refcount_table size if necessary */
-                        int old_nb_clusters = nb_clusters;
-                        nb_clusters = (new_offset >> s->cluster_bits) + 1;
-                        refcount_table = g_realloc(refcount_table,
-                                nb_clusters * sizeof(uint16_t));
-                        memset(&refcount_table[old_nb_clusters], 0, (nb_clusters
-                                - old_nb_clusters) * sizeof(uint16_t));
+                        int old_nb_clusters = *nb_clusters;
+                        *nb_clusters = (new_offset >> s->cluster_bits) + 1;
+
+                        *refcount_table = g_try_realloc(*refcount_table,
+                                *nb_clusters * sizeof(uint16_t));
+                        if (!*refcount_table) {
+                            res->check_errors++;
+                            return -ENOMEM;
+                        }
+
+                        memset(&(*refcount_table)[old_nb_clusters], 0,
+                               (*nb_clusters - old_nb_clusters) *
+                               sizeof(uint16_t));
                     }
-                    refcount_table[cluster]--;
-                    inc_refcounts(bs, res, refcount_table, nb_clusters,
+                    (*refcount_table)[cluster]--;
+                    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
                             new_offset, s->cluster_size);
 
                     res->corruptions_fixed++;
@@ -1621,6 +1574,95 @@  int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
         }
     }
 
+    return 0;
+}
+
+/*
+ * Calculates an in-memory refcount table.
+ */
+static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
+                               BdrvCheckMode fix, uint16_t **refcount_table,
+                               int64_t *nb_clusters)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshot *sn;
+    int64_t i;
+    int ret;
+
+    if (!*refcount_table) {
+        *refcount_table = g_try_malloc0(*nb_clusters * sizeof(uint16_t));
+        if (*nb_clusters && !*refcount_table) {
+            res->check_errors++;
+            return -ENOMEM;
+        }
+    }
+
+    /* header */
+    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
+        0, s->cluster_size);
+
+    /* current L1 table */
+    ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
+                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* snapshots */
+    for (i = 0; i < s->nb_snapshots; i++) {
+        sn = s->snapshots + i;
+        ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
+            sn->l1_table_offset, sn->l1_size, 0);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
+        s->snapshots_offset, s->snapshots_size);
+
+    /* refcount data */
+    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
+        s->refcount_table_offset,
+        s->refcount_table_size * sizeof(uint64_t));
+
+    return check_refblocks(bs, res, fix, refcount_table, nb_clusters);
+}
+
+/*
+ * Checks an image for refcount consistency.
+ *
+ * Returns 0 if no errors are found, the number of errors in case the image is
+ * detected as corrupted, and -errno when an internal error occurred.
+ */
+int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
+                          BdrvCheckMode fix)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t size, i, highest_cluster, nb_clusters;
+    int refcount1, refcount2;
+    uint16_t *refcount_table = NULL;
+    int ret;
+
+    size = bdrv_getlength(bs->file);
+    if (size < 0) {
+        res->check_errors++;
+        return size;
+    }
+
+    nb_clusters = size_to_clusters(s, size);
+    if (nb_clusters > INT_MAX) {
+        res->check_errors++;
+        return -EFBIG;
+    }
+
+    res->bfi.total_clusters =
+        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
+
+    ret = calculate_refcounts(bs, res, fix, &refcount_table, &nb_clusters);
+    if (ret < 0) {
+        goto fail;
+    }
+
     /* compare ref counts */
     for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
         refcount1 = get_refcount(bs, i);