qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Kevin Wolf <kwolf@redhat.com>
To: Devin Nakamura <devin122@gmail.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [RFC 21/24] qcow2: add qcow2_open_conversion_target()
Date: Mon, 01 Aug 2011 17:26:51 +0200	[thread overview]
Message-ID: <4E36C5BB.2020606@redhat.com> (raw)
In-Reply-To: <1311914994-20482-22-git-send-email-devin122@gmail.com>

Am 29.07.2011 06:49, schrieb Devin Nakamura:
> Still in very raw form.  Not likely to work yet
> 
> Signed-off-by: Devin Nakamura <devin122@gmail.com>

I don't think it's quite as easy.

The problem is that qcow2 will access the image header in some places,
for example when growing the L1 or refcount table. You need to make sure
that it doesn't destroy the source format header, but writes to the
target format header at a different offset.

I think much of qcow2_open and qcow2_open_conversion_target could be the
same. That is, both could call a common function that takes a parameter
for the header offset.

The other part of qcow2_open_conversion_target (creating a new header
and a basic L1/refcount table) could possibly be shared with
qcow2_create2, though I'm not quite as sure about this one.

> ---
>  block/qcow2.c |  124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 124 insertions(+), 0 deletions(-)
> 
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 86df65d..f1e1e12 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -1444,6 +1444,129 @@ static int qcow2_get_conversion_options(BlockDriverState *bs,
>      }
>      return 0;
>  }
> +
> +
> +static int qcow2_open_conversion_target(BlockDriverState *bs,
> +                                        BlockConversionOptions *drv_options,
> +                                        QEMUOptionParameter *usr_options)
> +{
> +    uint64_t imgsize, sectorsize, clusterbits;
> +    uint64_t num_refcount_blocks;
> +    uint16_t *refcount_block;
> +    uint64_t table_index, table_limit, i;
> +
> +    sectorsize = drv_options->cluster_size;

Sectors aren't clusters.

> +    clusterbits = 0;
> +    while (~sectorsize & 1) {
> +        sectorsize >>=1;
> +        clusterbits++;
> +    }
> +    if (sectorsize != 1) {
> +        return -EINVAL;
> +    }

Have a look at qcow2_create2, it uses ffs() to do this.

> +
> +
> +    imgsize = drv_options->image_size;
> +
> +    BDRVQcowState *s = bs->opaque;
> +    s->crypt_method_header = QCOW_CRYPT_NONE;

Shouldn't this be taken from drv_options?

> +    s->cluster_bits = clusterbits;
> +    s->cluster_size = 1 << s->cluster_bits;
> +    s->cluster_sectors = 1 << (s->cluster_bits - 9);
> +    s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
> +    s->l2_size = 1 << s->l2_bits;
> +    bs->total_sectors = imgsize / 512;
> +    s->csize_shift = (62 - (s->cluster_bits - 8));
> +    s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
> +    s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
> +    //s->refcount_table_offset = 0;
> +    //s->refcount_table_size = 0;
> +    s->snapshots_offset = 0;
> +    s->nb_snapshots = 0;
> +    /* alloc L2 table/refcount block cache */
> +    s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE, true); //todo: double check writethrough

writethrough mode will hurt performance and isn't required here.

> +    s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE,
> +        true);
> +
> +    s->cluster_cache = qemu_malloc(s->cluster_size);
> +    /* one more sector for decompressed data alignment */
> +    s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size
> +                                  + 512);
> +    s->cluster_cache_offset = -1;
> +
> +    //Make up a refcount table
> +    const uint64_t num_clusters = bs->file->total_sectors >>
> +        (clusterbits - BDRV_SECTOR_BITS);

This is the number of cluster in the image file.

> +    const uint64_t cluster_size = 1 << s->cluster_bits;
> +    const uint64_t uint64_per_cluster = cluster_size / sizeof(uint64_t);
> +    const uint64_t uint16_per_cluster = cluster_size / sizeof(uint16_t);
> +    const uint64_t num_l2_tables = (num_clusters + uint64_per_cluster - 1) / 
> +                                    uint64_per_cluster;
> +    const uint64_t num_l1_clusters = (num_l2_tables + uint64_per_cluster - 1)
> +                                     / uint64_per_cluster);

L1/L2 tables are for clusters on the virtual disk. This can be something
completely different, and with some bad luck even larger than what you
calculate here (consider an image where in each L2 table only one
cluster is allocated - this will make the image files very small, but
requires still many L2 tables).

> +    s->l1_size = num_l2_tables;
> +    s->l1_table = qemu_mallocz(s->l1_size * s->cluster_size);
> +    num_refcount_blocks = num_clusters + num_l2_tables + num_l1_clusters;
> +    num_refcount_blocks = (num_refcount_blocks + uint16_per_cluster - 1) /
> +                          uint16_per_cluster;
> +
> +    /* Account for refcount blocks used to track refcount blocks */
> +    num_refcount_blocks *= uint16_per_cluster;
> +    num_refcount_blocks /= uint16_per_cluster -1;
> +    num_refcount_blocks += 1; //todo: fix rounding

Not sure what you're trying to do here, but align_offset() from qcow2.h
could help.

> +
> +    /* Account for refcount blocks used to track refcount table */
> +    num_refcount_blocks *= uint64_per_cluster;
> +    num_refcount_blocks /= uint64_per_cluster -1;
> +    num_refcount_blocks += 1; // todo: fix rounding
> +
> +    s->refcount_table_size = (num_refcount_blocks / uint64_per_cluster) +1;

I think the algorithm to calculate the required refcount table size
should be shared with qcow2_alloc_refcount_block.

Doesn't your algorithm forget to count the clusters used by refcount
tables/block themselves?

> +    s->refcount_table = qemu_mallocz(s->refcount_table_size * s->cluster_size);
> +    refcount_block = qemu_mallocz(s->cluster_size);
> +
> +//    s->refcount_table_size = num_refcount_blocks;
> +    s->free_cluster_index = num_clusters;
> +    s->l1_table_offset = s->free_cluster_index << s->cluster_bits;
> +    s->free_cluster_index += num_l1_clusters;
> +    s->refcount_table_offset = s->free_cluster_index << s->cluster_bits;
> +    s->free_cluster_index++;
> +
> +    /* assign offsets for all refcount blocks */
> +    for (table_index = 0; table_index < num_refcount_blocks; table_index++) {
> +        s->refcount_table[table_index] = s->free_cluster_index++ << s->cluster_bits;
> +    }
> +
> +    /* set all refcounts in the block to 1 */
> +    for (i=0; i < uint16_per_cluster; i++) {
> +        refcount_block[i] = cpu_to_be16(1);
> +    }
> +
> +    //TODO: double check this, it seems a bit fishy

That's my impression of the whole operation, too. :-)

This is why I thought it would be nice to share this code with normal
image creation. Then we have this fishy code at least only once.

Kevin

  reply	other threads:[~2011-08-01 15:24 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-29  4:49 [Qemu-devel] [RFC 00/24] inplace image conversion Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 01/24] block: add block conversion api Devin Nakamura
2011-08-01 13:34   ` Kevin Wolf
2011-08-02  4:43     ` Devin Nakamura
2011-08-02  8:56   ` Stefan Hajnoczi
2011-08-02  9:24     ` Kevin Wolf
2011-07-29  4:49 ` [Qemu-devel] [RFC 02/24] block: add bdrv_get_conversion_options() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 03/24] block: add bdrv_open_conversion_target() Devin Nakamura
2011-08-01 13:42   ` Kevin Wolf
2011-08-02  8:57   ` Stefan Hajnoczi
2011-07-29  4:49 ` [Qemu-devel] [RFC 04/24] block: add bdrv_get_mapping() Devin Nakamura
2011-08-02  8:58   ` Stefan Hajnoczi
2011-07-29  4:49 ` [Qemu-devel] [RFC 05/24] block: add bdrv_map() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 06/24] block: add bdrv_copy_header() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 07/24] qed: make qed_alloc_clusters round up offset to nearest cluster Devin Nakamura
2011-08-01 13:51   ` Kevin Wolf
2011-07-29  4:49 ` [Qemu-devel] [RFC 08/24] qed: add qed_find_cluster_sync() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 09/24] qed: add qed_bdrv_get_mapping() Devin Nakamura
2011-08-02  8:59   ` Stefan Hajnoczi
2011-07-29  4:49 ` [Qemu-devel] [RFC 10/24] qed: add qed_bdrv_map() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 11/24] qed: add open_conversion_target() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 12/24] qed: add bdrv_qed_copy_header() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 13/24] qed: add bdrv_qed_get_conversion_options() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 14/24] qcow2: fix typo in documentation for qcow2_get_cluster_offset() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 15/24] qcow2: split up the creation of new refcount table from the act of checking it Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 16/24] qcow2: add qcow2_drop_leaked_clusters() Devin Nakamura
2011-08-01 14:18   ` Kevin Wolf
2011-07-29  4:49 ` [Qemu-devel] [RFC 17/24] qcow2: add qcow2_get_mapping Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 18/24] qcow2: add qcow2_map Devin Nakamura
2011-08-01 14:32   ` Kevin Wolf
     [not found]     ` <CAJ1AwB5ohCMOeSgcUKpKHbqGuK8Eioq5dr-z+a6+vGzdMrJJ6w@mail.gmail.com>
2011-08-02  8:05       ` Kevin Wolf
2011-07-29  4:49 ` [Qemu-devel] [RFC 19/24] qcow2: add qcow2_copy_header() Devin Nakamura
2011-08-01 14:57   ` Kevin Wolf
2011-07-29  4:49 ` [Qemu-devel] [RFC 20/24] qcow2: add get_conversion_options() Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 21/24] qcow2: add qcow2_open_conversion_target() Devin Nakamura
2011-08-01 15:26   ` Kevin Wolf [this message]
2011-08-02  4:37     ` Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 22/24] qemu-io: make map command use new block mapping function Devin Nakamura
2011-08-01 15:38   ` Kevin Wolf
2011-08-02  4:02     ` Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 23/24] qemu-io: add setmap command Devin Nakamura
2011-07-29  4:49 ` [Qemu-devel] [RFC 24/24] qemu-img: add inplace conversion to qemu-img Devin Nakamura

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=4E36C5BB.2020606@redhat.com \
    --to=kwolf@redhat.com \
    --cc=devin122@gmail.com \
    --cc=qemu-devel@nongnu.org \
    /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).