qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: Peter Lieven <pl@kamp.de>, 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 09:14:04 -0500	[thread overview]
Message-ID: <54EC872C.8000206@redhat.com> (raw)
In-Reply-To: <54EC1EFA.8050005@kamp.de>

On 2015-02-24 at 01:49, Peter Lieven wrote:
> 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.

Right. Very well then.

>>
>>>       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.

Well, it won't work so well with the MIN() being necessary...

Max

  reply	other threads:[~2015-02-24 14:14 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
2015-02-24 14:14       ` Max Reitz [this message]
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=54EC872C.8000206@redhat.com \
    --to=mreitz@redhat.com \
    --cc=carnold@suse.com \
    --cc=jcody@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=pl@kamp.de \
    --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).