qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: Jun Li <junmuzi@gmail.com>, qemu-devel@nongnu.org
Cc: kwolf@redhat.com, famz@redhat.com, juli@redhat.com, stefanha@redhat.com
Subject: Re: [Qemu-devel] [PATCH v5 2/3] qcow2: add update refcount table realization for update_refcount
Date: Fri, 21 Nov 2014 13:41:15 +0100	[thread overview]
Message-ID: <546F32EB.6010007@redhat.com> (raw)
In-Reply-To: <1414336849-21179-3-git-send-email-junmuzi@gmail.com>

On 2014-10-26 at 16:20, Jun Li wrote:
> When every item of refcount block is NULL, free refcount block and reset the
> corresponding item of refcount table with NULL. At the same time, put this
> cluster to s->free_cluster_index.
>
> Signed-off-by: Jun Li <junmuzi@gmail.com>
> ---
> v5:
>    Just instead write_reftable_entry with bdrv_pwrite_sync. As write_reftable_entry has deleted from the source code.
>
> v4:
>    Do not change anything for this commit id.
>
> v3:
>    Add handle self-describing refcount blocks.
> ---
>   block/qcow2-refcount.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 48 insertions(+), 1 deletion(-)

Well, this patch will conflict with my "qcow2: Support refcount orders 
!= 4" series, but that's how it is.

> diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
> index 1477031..abb4f6d 100644
> --- a/block/qcow2-refcount.c
> +++ b/block/qcow2-refcount.c
> @@ -596,6 +596,53 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
>           if (refcount == 0 && s->discard_passthrough[type]) {
>               update_refcount_discard(bs, cluster_offset, s->cluster_size);
>           }

We can skip the whole following block if refcount > 0, so we should do that.

> +
> +        unsigned int refcount_table_index;

Declarations only at the start of the block, please. Furthermore, this 
is the same as table_index.

> +        refcount_table_index = cluster_index >> s->refcount_block_bits;
> +
> +        uint64_t refcount_block_offset =
> +            s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK;
> +
> +        int64_t self_block_index = refcount_block_offset >> s->cluster_bits;
> +        int self_block_index2 = (refcount_block_offset >> s->cluster_bits) &
> +            ((1 << s->refcount_block_bits) - 1);

You are assuming here that every refblock is described by itself. While 
that may be true for QEMU's current qcow2 driver, it's by no means 
defined that way.

> +
> +        /* When refcount block is NULL, update refcount table */
> +        if (!be16_to_cpu(refcount_block[0]) || self_block_index2 == 0) {

Is this check really necessary? I think we can just go into the for () 
loop. If the first entry is not 0, it will be exited immediately anyway.

> +            int k;
> +            int refcount_block_entries = s->cluster_size /
> +                                         sizeof(refcount_block[0]);

s->refcount_block_size is what you want (but we probably didn't have it 
in October yet).

> +            for (k = 0; k < refcount_block_entries; k++) {
> +                if (k == self_block_index2) {
> +                    k++;

This should be replaced by a "continue", otherwise the following array 
access will overflow if self_block_index2 == refcount_block_entries - 1;

> +                }
> +
> +                if (be16_to_cpu(refcount_block[k])) {
> +                    break;
> +                }
> +            }
> +
> +            if (k == refcount_block_entries) {
> +                if (self_block_index < s->free_cluster_index) {
> +                    s->free_cluster_index = self_block_index;
> +                }

You should probably flush the refblock cache here. When the cluster 
which was occupied by the refblock is marked free, it may be used for 
different data, while the refblock remains cached. When the cache entry 
is removed later on, it will be written to disk and overwrite that cluster.

Also, you're again assuming that every refblock handles its own 
refcount, which is not necessarily true. The refcount may be handled by 
a different refblock in which case you have to decrement its refcount there.

> +
> +                /* update refcount table */
> +                s->refcount_table[refcount_table_index] = 0;
> +                uint64_t data64 = cpu_to_be64(0);
> +                ret = bdrv_pwrite_sync(bs->file,
> +                                       s->refcount_table_offset +
> +                                       refcount_table_index *
> +                                       sizeof(uint64_t),
> +                                       &data64, sizeof(data64));
> +                if (ret < 0) {
> +                    fprintf(stderr, "Could not update refcount table: %s\n",
> +                            strerror(-ret));
> +                    goto fail;
> +                }
> +

There's an empty line here which is probably unneeded. Also, you may 
want to do an overlap check before updating the reftable.

> +            }
> +        }
>       }
>   
>       ret = 0;
> @@ -1204,7 +1251,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
>   
>           case QCOW2_CLUSTER_NORMAL:
>           {
> -            uint64_t offset = l2_entry & L2E_OFFSET_MASK;
> +            uint64_t offset = (uint64_t)(l2_entry & L2E_OFFSET_MASK);

Is that supposed to be in this patch? Also, what does it do? l2_entry is 
uint64_t anyway, so the result of l2_entry & L2E_OFFSET_MASK should 
already be of type uint64_t.

>               if (flags & CHECK_FRAG_INFO) {
>                   res->bfi.allocated_clusters++;

So, about this whole patch: I'm not sure whether we need it in this 
series but I remember you saying that it's actually worth it, so I'm 
trusting you.

Second, I somehow don't like iterating over the whole refblock for each 
refcount updated or at least for each refcount set to 0. There are 32768 
entries per refblock with 16 bit refcounts and 64 kB clusters. 
Therefore, when freeing all clusters referenced by a refblock (and if 
that refblock doesn't have any free entries yet), we will loop over 
16384 entries (in average) before noticing there are still entries with 
a non-zero refcount, and that we will do 32768 times (okay, 32767, 
because you're assuming a refblock always contains its own refcount). I 
think that a bit much. But I don't have any idea to fix this other than 
keeping the number of entries per refblock with refcount != 0 in a table 
in memory (which is probably not too bad, but not too nice either).

Max

  reply	other threads:[~2014-11-21 12:41 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-26 15:20 [Qemu-devel] [PATCH v5 0/3] qcow2: Patch for shrinking qcow2 disk image Jun Li
2014-10-26 15:20 ` [Qemu-devel] [PATCH v5 1/3] qcow2: Add qcow2_shrink_l1_and_l2_table for qcow2 shrinking Jun Li
2014-11-21 10:56   ` Max Reitz
2014-11-24 17:49     ` Eric Blake
2015-01-03 12:23     ` Jun Li
2015-01-15 18:47       ` Max Reitz
2015-01-19 13:16         ` Jun Li
2015-01-22 19:14           ` Max Reitz
2015-01-27 14:06             ` Jun Li
2014-10-26 15:20 ` [Qemu-devel] [PATCH v5 2/3] qcow2: add update refcount table realization for update_refcount Jun Li
2014-11-21 12:41   ` Max Reitz [this message]
2014-11-24 18:11     ` Eric Blake
2014-10-26 15:20 ` [Qemu-devel] [PATCH v5 3/3] qcow2: Add qemu-iotests for qcow2 shrinking Jun Li
2014-11-21 13:01   ` Max Reitz
2014-11-10  8:36 ` [Qemu-devel] [PATCH v5 0/3] qcow2: Patch for shrinking qcow2 disk image Jun Li
2014-11-10  9:17   ` 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=546F32EB.6010007@redhat.com \
    --to=mreitz@redhat.com \
    --cc=famz@redhat.com \
    --cc=juli@redhat.com \
    --cc=junmuzi@gmail.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).