qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Peter Lieven <pl@kamp.de>
To: Max Reitz <mreitz@redhat.com>, qemu-devel@nongnu.org
Cc: kwolf@redhat.com, carnold@suse.com, jcody@redhat.com,
	stefanha@redhat.com
Subject: Re: [Qemu-devel] [PATCH 4/5] block/vpc: make calculate_geometry spec conform
Date: Tue, 24 Feb 2015 07:49:30 +0100	[thread overview]
Message-ID: <54EC1EFA.8050005@kamp.de> (raw)
In-Reply-To: <54EB7887.7020805@redhat.com>

Am 23.02.2015 um 19:59 schrieb Max Reitz:
> On 2015-02-23 at 09:27, Peter Lieven wrote:
>> The VHD spec [1] allows for total_sectors of 65535 x 16 x 255 (~127GB)
>> represented by a CHS geometry. If total_sectors is greater
>> than 65535 x 16 x 255 this geometry is set as a maximum.
>>
>> Qemu, Hyper-V, VirtualBox and disk2vhd use this special geometry as an
>> indicator to use the image current size from the footer as disk size.
>>
>> This patch changes vpc_create to effectively calculate a CxHxS geometry
>> for the given image size if possible while rounding up if necessary.
>> If the image size is too big to be represented in CHS we set the maximum
>> and write the exact requested image size into the footer.
>>
>> This partly reverts commit 258d2edb, but leaves support for >127G disks
>> intact.
>>
>> [1] http://download.microsoft.com/download/f/f/e/ffef50a5-07dd-4cf8-aaa3-442c0673a029/Virtual%20Hard%20Disk%20Format%20Spec_10_18_06.doc
>>
>> Signed-off-by: Peter Lieven <pl@kamp.de>
>> ---
>>   block/vpc.c |   45 ++++++++++++++++++++++++---------------------
>>   1 file changed, 24 insertions(+), 21 deletions(-)
>>
>> diff --git a/block/vpc.c b/block/vpc.c
>> index 11d3c86..9c5301b 100644
>> --- a/block/vpc.c
>> +++ b/block/vpc.c
>> @@ -46,6 +46,7 @@ enum vhd_type {
>>   #define VHD_TIMESTAMP_BASE 946684800
>>     #define VHD_MAX_SECTORS       (65535LL * 255 * 255)
>> +#define VHD_MAX_GEOMETRY      (65535LL *  16 * 255)
>>     // always big-endian
>>   typedef struct vhd_footer {
>> @@ -218,7 +219,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
>>       /* Images that have exactly the maximum geometry are probably bigger and
>>        * would be truncated if we adhered to the geometry for them. Rely on
>>        * footer->size for them. */
>> -    if (bs->total_sectors == 65535ULL * 16 * 255) {
>> +    if (bs->total_sectors == VHD_MAX_GEOMETRY) {
>>           bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE;
>>       }
>>   @@ -642,26 +643,20 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
>>   {
>>       uint32_t cyls_times_heads;
>>   -    /* Allow a maximum disk size of approximately 2 TB */
>> -    if (total_sectors > 65535LL * 255 * 255) {
>> -        return -EFBIG;
>> -    }
>> +    total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY);
>>   -    if (total_sectors > 65535 * 16 * 63) {
>> +    if (total_sectors > 65535LL * 16 * 63) {
>
> While it is >= in the specification, the "else" branch does work fine with the total_sectors = 65535 * 16 * 63 case. So I'm leaving it up to you whether to make it really really spec-conform or at least functionally spec-conform (although the spec says 
> 16 heads, 16191 cylinders, and 255 sectors per cylinder for this case, whereas the "else" branch leaves us with 16 heads, 65535 cylinders, and 63 sectors per cylinder).

I will adjust it to match the spec exactly.

>
>>           *secs_per_cyl = 255;
>> -        if (total_sectors > 65535 * 16 * 255) {
>> -            *heads = 255;
>> -        } else {
>> -            *heads = 16;
>> -        }
>> +        *heads = 16;
>>           cyls_times_heads = total_sectors / *secs_per_cyl;
>>       } else {
>>           *secs_per_cyl = 17;
>>           cyls_times_heads = total_sectors / *secs_per_cyl;
>>           *heads = (cyls_times_heads + 1023) / 1024;
>>   -        if (*heads < 4)
>> +        if (*heads < 4) {
>>               *heads = 4;
>> +        }
>>             if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
>>               *secs_per_cyl = 31;
>> @@ -817,19 +812,27 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
>>        * Calculate matching total_size and geometry. Increase the number of
>>        * sectors requested until we get enough (or fail). This ensures that
>>        * qemu-img convert doesn't truncate images, but rather rounds up.
>> +     *
>> +     * If the image size can't be represented by a spec conform CHS geometry,
>> +     * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
>> +     * the image size from the VHD footer to calculate total_sectors.
>>        */
>> -    total_sectors = total_size / BDRV_SECTOR_SIZE;
>> +    total_sectors = MIN(VHD_MAX_GEOMETRY,
>> +                        DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE));
>
> DIV_ROUND_UP() is unnecessary, total_size is a multiple of BDRV_SECTOR_SIZE anyway.

ok

>
> I find the MIN() unnecessary, too, because calculate_geometry() will take care of that itself.

Actually the MIN() is the guarantee that the for-loop terminates for images with total_sectors > VHD_MAX_GEOMETRY.
Otherwise (int64_t)cyls * heads * secs_per_cyl would always be VHD_MAX_GEOMETRY which is always
smaller than VHD_MAX_GEOMETRY. This would keep us looping for good.

>
>>       for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
>> -        if (calculate_geometry(total_sectors + i, &cyls, &heads,
>> -                               &secs_per_cyl))
>> -        {
>> -            ret = -EFBIG;
>> -            goto out;
>> -        }
>> +        calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
>>       }
>>   -    total_sectors = (int64_t) cyls * heads * secs_per_cyl;
>> -    total_size = total_sectors * BDRV_SECTOR_SIZE;
>> +    if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
>> +        total_sectors = DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE);
>
> Again, DIV_ROUND_UP() is unnecessary.
>
> So this is equivalent to total_sectors = total_size / BDRV_SECTOR_SIZE, which, if you omit the MIN() above, is exactly what total_sectors already contains, so you can just omit this assignment.
>
>> +        /* Allow a maximum disk size of approximately 2 TB */
>> +        if (total_sectors > VHD_MAX_SECTORS) {
>> +            return -EFBIG;
>
> No goto out;?

Oops.

>
>> +        }
>> +    } else {
>> +        total_sectors = (int64_t)cyls * heads * secs_per_cyl;
>> +        total_size = total_sectors * BDRV_SECTOR_SIZE;
>> +    }
>
> An alternative way of writing this would be:
>
> if ((int64_t)cyls * heads * secs_per_cyl < VHD_MAX_GEOMETRY) {
>     total_sectors = (int64_t)cyls * heads * secs_per_cyl;
> }
> if (total_sectors > VHD_MAX_SECTORS) {
>     ret = -EFBIG;
>     goto out;
> }
> total_size = total_sectors * BDRV_SECTOR_SIZE;
>
> Or, alternatively, it'd be possible to drop the total_size assignment and just use total_sectors * BDRV_SECTOR_SIZE in the assignments of footer->orig_size and footer->size.

Will look at this.

Thank you,
Peter

  reply	other threads:[~2015-02-24  6:49 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-23 14:27 [Qemu-devel] [PATCH 0/5] block/vpc optimizations Peter Lieven
2015-02-23 14:27 ` [Qemu-devel] [PATCH 1/5] block/vpc: optimize vpc_co_get_block_status Peter Lieven
2015-02-23 18:08   ` Max Reitz
2015-02-24  6:41     ` Peter Lieven
2015-02-23 14:27 ` [Qemu-devel] [PATCH 2/5] block/vpc: simplify vpc_read Peter Lieven
2015-02-23 18:29   ` Max Reitz
2015-02-24  6:44     ` Peter Lieven
2015-02-24 14:09       ` Max Reitz
2015-02-23 14:27 ` [Qemu-devel] [PATCH 3/5] vpc: Ignore geometry for large images Peter Lieven
2015-02-23 18:34   ` Max Reitz
2015-02-24  6:45     ` Peter Lieven
2015-02-24 14:12       ` Max Reitz
2015-02-23 14:27 ` [Qemu-devel] [PATCH 4/5] block/vpc: make calculate_geometry spec conform Peter Lieven
2015-02-23 18:59   ` Max Reitz
2015-02-24  6:49     ` Peter Lieven [this message]
2015-02-24 14:14       ` Max Reitz
2015-02-23 14:27 ` [Qemu-devel] [PATCH 5/5] block/vpc: rename footer->size -> footer->current_size Peter Lieven
2015-02-23 19:04   ` Max Reitz

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=54EC1EFA.8050005@kamp.de \
    --to=pl@kamp.de \
    --cc=carnold@suse.com \
    --cc=jcody@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@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).