From: Max Reitz <mreitz@redhat.com>
To: Eric Blake <eblake@redhat.com>, qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Stefan Hajnoczi <stefanha@redhat.com>
Subject: Re: [Qemu-devel] [PATCH] qcow2: Rewrite qcow2_alloc_bytes()
Date: Thu, 05 Feb 2015 08:25:41 -0500 [thread overview]
Message-ID: <54D36F55.3080904@redhat.com> (raw)
In-Reply-To: <54D2965B.3040309@redhat.com>
On 2015-02-04 at 16:59, Eric Blake wrote:
> On 02/04/2015 12:53 PM, Max Reitz wrote:
>
> My review jumps around a bit; try to follow the numbers to learn my
> stream of consciousness on reviewing it.
>
> tl;dr:
> It looks like this is functionally correct (you won't break behavior),
> but that you have a pessimization bug (see [7]), so you ought to spin v2.
>
>> qcow2_alloc_bytes() is a function with insufficient error handling and
>> an unnecessary goto. This patch rewrites it.
> Not clear what the added error handling is from just the commit message. [1]
Well, there are two cases and I didn't really mind writing it into the
commit message since this patch completely rewrites the function anyway.
[snip]
>> +
>> + cluster_end = start_of_cluster(s, s->free_byte_offset) +
>> + s->cluster_size;
> [6] cluster_end is now either s->cluster_size (s->free_byte_offset was
> 0) or the first cluster-aligned address after s->free_byte_offset (will
> tell us if new_cluster is contiguous). If I'm not mistaken, you could
> also write this as:
>
> cluster_end = ROUND_UP(s->free_byte_offset, s->cluster_size);
>
> which would give the same value for a non-zero s->free_byte_offset, but
> the value 0 instead of s->cluster_size if s->free_byte_offset is 0.
> I'll analyze at point [7] what semantic difference that would make.
Indeed. I was wondering about s->free_byte_offset maybe being aligned to
a cluster boundary and thus breaking this, but that can never happen (if
it is aligned, it will be set to 0 at the end of this function). If it
did happen, the free_in_cluster calculation would break, too.
I'll add an assert(!s->free_byte_offset || offset_into_cluster(s,
s->free_byte_offset)); at the start of this function (which might have
avoided "[0] ... I finally figured it out").
>> +
>> + if (!s->free_cluster_index || cluster_end != new_cluster) {
>> + s->free_byte_offset = new_cluster;
>> + }
> [7] This was the only mention of s->free_cluster_index in the patch.
Oops, that's because I meant to use s->free_byte_offset.
> I
> had to hunt for it's use in the code base;
Sorry. :-/
> there are only two places in
> the code base that set it to non-zero: alloc_clusters_noref() and
> update_refcount(). But alloc_clusters_noref() is called by
> qcow2_alloc_clusters(), which we just called. Therefore, the left half
> of the || is always true, and you always send up changing
> s->free_byte_offset (that is, when you allocate a new cluster, you never
> reuse the tail of an existing cluster, even if the two were contiguous).
> [11]
>
> At one point in my review, I wondered if point [0] should assert that
> !s->free_byte_offset should imply !s->free_cluster_index (more on that
> at [10], but I convinced myself that would be wrong).
>
> Let's ignore the left half of the ||, and focus on the right half. If
> s->free_byte_offset was non-zero, you now know whether the new cluster
> is contiguous (use the existing tail, and the overflow into the new
> cluster is safe) or not (use only the new cluster); either way,
> s->free_byte_offset is now the point where the compressed data will be
> written. If s->free_byte_offset was 0, you want to unconditionally
> change it to the start of the just-allocated cluster.
>
> Note that if you used my suggestion above at making cluster_end == 0
> rather than s->cluster_size for the !s->free_byte_offset case, then you
> are guaranteed that cluster_end != new_cluster is a sufficient test for
> when to correctly rewrite s->free_byte_offset (since new_cluster will be
> non-zero, because qcow2_alloc_clusters() will never return the header
> cluster); whereas with your code, there is a very minute chance that
> qcow2_alloc_clusters() could be equal to s->cluster_size (that is, the
> very first cluster after the header, perhaps possible if earlier actions
> on the file moved the L1 and refcount table to later in the file and
> freed up cluster index 1). Using just the right half of the ||, if that
> rare chance under your code happened, then we would NOT rewrite
> s->free_byte_offset, and actually have a bug of returning 0 to the caller.
That silly small mistake made your whole review so much more
difficult... I'm sorry, again.
>> + }
>> +
>> + if (offset_into_cluster(s, s->free_byte_offset)) {
> I'd feel a bit better if you added
> assert(s->free_byte_offset);
> just before this if (which happens to be the case with your
> pessimization on the left half of || [7], and would also be the case
> with my proposed simplification of [6]).
Yes, why not.
Thanks for your review!
Max
prev parent reply other threads:[~2015-02-05 13:25 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-02-04 19:53 [Qemu-devel] [PATCH] qcow2: Rewrite qcow2_alloc_bytes() Max Reitz
2015-02-04 21:59 ` Eric Blake
2015-02-04 22:07 ` Eric Blake
2015-02-05 13:25 ` Max Reitz [this message]
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=54D36F55.3080904@redhat.com \
--to=mreitz@redhat.com \
--cc=eblake@redhat.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).