On Fri 09 Jun 2017 04:53:05 PM CEST, Eric Blake wrote:
> Let's suppose we have a guest issuing 512-byte aligned requests and a
> host that requires 4k alignment; and the guest does an operation that
> needs a COW with one sector at both the front and end of the cluster.
>
>> @@ -760,22 +776,59 @@ static int perform_cow(BlockDriverState *bs,
>> QCowL2Meta *m)
>> BDRVQcow2State *s = bs->opaque;
>> Qcow2COWRegion *start = &m->cow_start;
>> Qcow2COWRegion *end = &m->cow_end;
>> + unsigned buffer_size = start->nb_bytes + end->nb_bytes;
>
> This sets buffer_size to 1024 initially.
>
>> + uint8_t *start_buffer, *end_buffer;
>> int ret;
>>
>> + assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
>> +
>> if (start->nb_bytes == 0 && end->nb_bytes == 0) {
>> return 0;
>> }
>>
>> + /* Reserve a buffer large enough to store the data from both the
>> + * start and end COW regions */
>> + start_buffer = qemu_try_blockalign(bs, buffer_size);
>
> This is going to allocate a bigger buffer, namely one that is at least
> 4k in size (at least, that's my understanding - the block device is
> able to track its preferred IO size/alignment).
>
>> + if (start_buffer == NULL) {
>> + return -ENOMEM;
>> + }
>> + /* The part of the buffer where the end region is located */
>> + end_buffer = start_buffer + start->nb_bytes;
>
> But now end_buffer does not have optimal alignment. In the old code,
> we called qemu_try_blockalign() twice, so that both read()s were
> called on a 4k boundary; but now, the end read() is called unaligned
> to a 4k boundary. Of course, since we're only reading 512 bytes,
> instead of 4k, it MIGHT not matter, but I don't know if we are going
> to cause a bounce buffer to come into play that we could otherwise
> avoid if we were smarter with our alignments. Is that something we
> need to analyze further, or even possibly intentionally over-allocate
> our buffer to ensure optimal read alignments?
I think you're right, I actually thought about this but forgot it when
preparing the series for publishing.
If I'm not wrong it should be simply a matter of replacing
buffer_size = start->nb_bytes + end->nb_bytes;
with
buffer_size = QEMU_ALIGN_UP(start->nb_bytes, bdrv_opt_mem_align(bs))
+ end->nb_bytes;
and then
end_buffer = start_buffer + buffer_size - end->nb_bytes;
(this one we do anyway in patch 5)
Berto