qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: John Snow <jsnow@redhat.com>
To: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>,
	qemu-devel@nongnu.org
Cc: kwolf@redhat.com, pbonzini@redhat.com, stefanha@redhat.com,
	den@openvz.org
Subject: Re: [Qemu-devel] [PATCH 05/17] qcow2-dirty-bitmap: read dirty bitmap directory
Date: Tue, 6 Oct 2015 17:27:26 -0400	[thread overview]
Message-ID: <56143CBE.8000908@redhat.com> (raw)
In-Reply-To: <1441471439-6157-6-git-send-email-vsementsov@virtuozzo.com>



On 09/05/2015 12:43 PM, Vladimir Sementsov-Ogievskiy wrote:
> Adds qcow2_read_dirty_bitmaps, reading Dirty Bitmap Directory as
> specified in docs/specs/qcow2.txt
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  block/qcow2-dirty-bitmap.c | 155 +++++++++++++++++++++++++++++++++++++++++++++
>  block/qcow2.h              |  10 +++
>  2 files changed, 165 insertions(+)
> 
> diff --git a/block/qcow2-dirty-bitmap.c b/block/qcow2-dirty-bitmap.c
> index fd4e0ef..1260d1d 100644
> --- a/block/qcow2-dirty-bitmap.c
> +++ b/block/qcow2-dirty-bitmap.c
> @@ -25,6 +25,9 @@
>   * THE SOFTWARE.
>   */
>  
> +#include "block/block_int.h"
> +#include "block/qcow2.h"
> +
>  /* NOTICE: DBM here means Dirty Bitmap and used as a namespace for _internal_
>   * constants. Please do not use this _internal_ abbreviation for other needs
>   * and/or outside of this file. */
> @@ -40,3 +43,155 @@
>  
>  /* bits [0, 8] U [56, 63] are reserved */
>  #define DBM_TABLE_ENTRY_RESERVED_MASK 0xff000000000001ff
> +
> +void qcow2_free_dirty_bitmaps(BlockDriverState *bs)
> +{
> +    BDRVQcowState *s = bs->opaque;

BDRVQcow2State here and everywhere else in this patch, now.

> +    int i;
> +
> +    for (i = 0; i < s->nb_dirty_bitmaps; i++) {
> +        g_free(s->dirty_bitmaps[i].name);
> +    }
> +    g_free(s->dirty_bitmaps);
> +    s->dirty_bitmaps = NULL;
> +    s->nb_dirty_bitmaps = 0;
> +
> +    g_free(s->dirty_bitmap_directory);
> +    s->dirty_bitmap_directory = NULL;
> +}
> +
> +static void bitmap_header_to_cpu(QCowDirtyBitmapHeader *h)
> +{
> +    be64_to_cpus(&h->dirty_bitmap_table_offset);
> +    be64_to_cpus(&h->nb_virtual_bits);
> +    be32_to_cpus(&h->dirty_bitmap_table_size);
> +    be32_to_cpus(&h->granularity_bits);
> +    be32_to_cpus(&h->flags);
> +    be16_to_cpus(&h->name_size);

I realize you probably got these functions by example from the other
qcow2 files, but what exactly is cpu*s* here? What does the *s* stand for?

I guess it refers to the in-place swapping variants that the Linux
kernel defines?

hmm, just a curiosity on my part ...

the function looks correct, anyway. :)

> +}
> +
> +static int calc_dir_entry_size(size_t name_size)
> +{
> +    return align_offset(sizeof(QCowDirtyBitmapHeader) + name_size, 8);

Matches spec.

> +}
> +
> +static int dir_entry_size(QCowDirtyBitmapHeader *h)
> +{
> +    return calc_dir_entry_size(h->name_size);

OK.

> +}
> +
> +static int check_constraints(int cluster_size,
> +                             QCowDirtyBitmapHeader *h)
> +{
> +    uint64_t phys_bitmap_bytes =
> +        (uint64_t)h->dirty_bitmap_table_size * cluster_size;
> +    uint64_t max_virtual_bits = (phys_bitmap_bytes * 8) << h->granularity_bits;
> +
> +    int fail =
> +            (h->dirty_bitmap_table_offset % cluster_size) ||
> +            (h->dirty_bitmap_table_size > DBM_MAX_TABLE_SIZE) ||
> +            (phys_bitmap_bytes > DBM_MAX_PHYS_SIZE) ||
> +            (h->nb_virtual_bits > max_virtual_bits) ||
> +            (h->granularity_bits > DBM_MAX_GRANULARITY_BITS) ||
> +            (h->flags & DBM_RESERVED_FLAGS) ||
> +            (h->name_size > DBM_MAX_NAME_SIZE);
> +

Function is a little dense, but appears to be correct -- apart from the
DMB_RESERVED_FLAGS issue I mentioned earlier.

> +    return fail ? -EINVAL : 0;
> +}
> +
> +static int directory_read(BlockDriverState *bs)
> +{
> +    int ret;
> +    BDRVQcowState *s = bs->opaque;
> +    uint8_t *entry, *end;
> +
> +    if (s->dirty_bitmap_directory != NULL) {
> +        /* already read */
> +        return -EEXIST;
> +    }
> +
> +    s->dirty_bitmap_directory = g_try_malloc0(s->dirty_bitmap_directory_size);
> +    if (s->dirty_bitmap_directory == NULL) {
> +        return -ENOMEM;
> +    }
> +

I assume we're trying here in case the directory size is garbage, as a
method of preventing garbage from crashing our program. Since
dirty_bitmap_directory_size was in theory already read in (by a function
checked in later in this series), did we not validate that input value?

> +    ret = bdrv_pread(bs->file,
> +                     s->dirty_bitmap_directory_offset,
> +                     s->dirty_bitmap_directory,
> +                     s->dirty_bitmap_directory_size);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +

Alright, so we read the entire directory into memory... which can be as
large as 64K * 1024, or 64MiB. A non-trivial size.

> +    entry = s->dirty_bitmap_directory;
> +    end = s->dirty_bitmap_directory + s->dirty_bitmap_directory_size;
> +    while (entry < end) {
> +        QCowDirtyBitmapHeader *h = (QCowDirtyBitmapHeader *)entry;
> +        bitmap_header_to_cpu(h);
> +

OK, so we're interpreting the values in-place in memory, but leaving
them in the table.

> +        ret = check_constraints(s->cluster_size, h);
> +        if (ret < 0) {
> +            goto fail;
> +        }
> +
> +        entry += dir_entry_size(h);
> +    }
> +
> +    return 0;
> +
> +fail:
> +    g_free(s->dirty_bitmap_directory);
> +    s->dirty_bitmap_directory = NULL;
> +
> +    return ret;
> +}
> +
> +int qcow2_read_dirty_bitmaps(BlockDriverState *bs)
> +{
> +    int ret;
> +    BDRVQcowState *s = bs->opaque;
> +    size_t offset;
> +    QCowDirtyBitmap *bm, *end;
> +
> +    if (s->dirty_bitmap_directory != NULL || s->dirty_bitmaps != NULL) {
> +        /* already read */
> +        return -EEXIST;
> +    }
> +
> +    if (s->nb_dirty_bitmaps == 0) {
> +        /* No bitmaps - nothing to do */
> +        return 0;
> +    }
> +

OK, so this assumes that the extension header has been read, but that
code comes later in this series.

> +    ret = directory_read(bs);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +

At the end of this call we have interpreted the header into a CPU native
format, but not performed any processing on it whatsoever.

> +    s->dirty_bitmaps = g_try_new0(QCowDirtyBitmap, s->nb_dirty_bitmaps);
> +    if (s->dirty_bitmaps == NULL) {
> +        ret = -ENOMEM;
> +        goto out;
> +    }
> +

I think we could actually allocate this block of memory sooner (we
already have read and validated nb_dirty_bitmaps) and then during the
initial read, after validation, we can just fill the QcowDirtyBitmap
structures as we go.

If we keep "int n" as we parse bitmaps in the header, we can just unwind
on failure with:

for (i = n; i >= 0; i--) {
   bm = s->dirty_bitmaps[i];
   g_free(bm->name);
}
g_free(s->dirty_bitmaps);

Then we don't have to re-crawl through the structure looking for names,
getting sizes again, etc. It should be a little faster.

> +    offset = 0;
> +    end = s->dirty_bitmaps + s->nb_dirty_bitmaps;
> +    for (bm = s->dirty_bitmaps; bm < end; ++bm) {
> +        QCowDirtyBitmapHeader *h =
> +                (QCowDirtyBitmapHeader *)(s->dirty_bitmap_directory + offset);
> +
> +        bm->offset = offset;
> +        bm->name = g_malloc(h->name_size + 1);
> +        memcpy(bm->name, h + 1, h->name_size);
> +        bm->name[h->name_size] = '\0';

You can replace the last three lines if you want with just:

bm->name = g_strndup(h + 1, h->name_size);

> +
> +        offset += dir_entry_size(h);
> +    }
> +    ret = 0;
> +
> +out:
> +    if (ret < 0) {
> +        qcow2_free_dirty_bitmaps(bs);
> +    }
> +    return ret;
> +}
> diff --git a/block/qcow2.h b/block/qcow2.h
> index a2a5d4a..5016fa1 100644
> --- a/block/qcow2.h
> +++ b/block/qcow2.h
> @@ -288,6 +288,12 @@ typedef struct BDRVQcowState {
>      unsigned int nb_snapshots;
>      QCowSnapshot *snapshots;
>  
> +    uint64_t dirty_bitmap_directory_offset;
> +    size_t dirty_bitmap_directory_size;

I guess these two are from the extension header.

> +    uint8_t *dirty_bitmap_directory;
> +    unsigned int nb_dirty_bitmaps;

This one is also from the extension header. Pointing out only for review
purposes that these values are set "elsewhere" in future patches.

> +    QCowDirtyBitmap *dirty_bitmaps;
> +
>      int flags;
>      int qcow_version;
>      bool use_lazy_refcounts;
> @@ -598,6 +604,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
>  void qcow2_free_snapshots(BlockDriverState *bs);
>  int qcow2_read_snapshots(BlockDriverState *bs);
>  
> +/* qcow2-dirty-bitmap.c functions */
> +void qcow2_free_dirty_bitmaps(BlockDriverState *bs);
> +int qcow2_read_dirty_bitmaps(BlockDriverState *bs);
> +
>  /* qcow2-cache.c functions */
>  Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables);
>  int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
> 

Patch order is a little strange in that we expect to have parsed the
header already, but nothing criminal if this was just the easiest way to
do it. I'll defer to your judgment.

  reply	other threads:[~2015-10-06 21:27 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-05 16:43 [Qemu-devel] [PATCH v3 RFC 0/17] block: persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 01/17] block: fix bdrv_dirty_bitmap_granularity() Vladimir Sementsov-Ogievskiy
2015-09-15 15:36   ` Eric Blake
2015-10-05 22:47   ` John Snow
2015-09-05 16:43 ` [Qemu-devel] [PATCH 02/17] block: add bdrv_dirty_bitmap_size() Vladimir Sementsov-Ogievskiy
2015-09-15 15:37   ` Eric Blake
2015-10-05 22:48   ` John Snow
2015-09-05 16:43 ` [Qemu-devel] [PATCH 03/17] spec: add qcow2-dirty-bitmaps specification Vladimir Sementsov-Ogievskiy
2015-09-05 17:33   ` Vladimir Sementsov-Ogievskiy
2015-10-06 20:22     ` John Snow
2015-10-06 20:33       ` Eric Blake
2015-09-15 16:24   ` Eric Blake
2015-09-16  8:52     ` Vladimir Sementsov-Ogievskiy
2015-10-06  0:09     ` John Snow
2015-10-07 16:47   ` Max Reitz
2015-10-07 19:05     ` Denis V. Lunev
2015-10-08 20:28       ` John Snow
2015-10-08 20:56         ` Denis V. Lunev
2015-10-09 18:14           ` [Qemu-devel] [PAT​CH " Max Reitz
2015-10-09 17:07         ` [Qemu-devel] [PATCH " Max Reitz
2015-10-09 20:14           ` [Qemu-devel] [Qemu-block] " Eric Blake
2015-09-05 16:43 ` [Qemu-devel] [PATCH 04/17] qcow2: Dirty Bitmaps Ext: structs and consts Vladimir Sementsov-Ogievskiy
2015-10-06 20:12   ` John Snow
2015-10-06 20:16   ` John Snow
2016-02-16 17:04     ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 05/17] qcow2-dirty-bitmap: read dirty bitmap directory Vladimir Sementsov-Ogievskiy
2015-10-06 21:27   ` John Snow [this message]
2016-02-16 18:51     ` Vladimir Sementsov-Ogievskiy
2016-02-17 15:03     ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 06/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_load() Vladimir Sementsov-Ogievskiy
2015-10-06 23:01   ` John Snow
2015-10-07 17:05     ` Eric Blake
2016-02-16 19:04     ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 07/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_store() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 08/17] qcow2: add dirty bitmaps extension Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 09/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_load_check() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 10/17] block: store persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 11/17] block: add bdrv_load_dirty_bitmap() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 12/17] qcow2-dirty-bitmap: add autoclear bit Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 13/17] qemu: command line option for dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 14/17] qcow2-dirty-bitmap: add IN_USE flag Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 15/17] qcow2-dirty-bitmaps: handle store reqursion Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 16/17] iotests: add VM.test_launcn() Vladimir Sementsov-Ogievskiy
2015-09-05 16:43 ` [Qemu-devel] [PATCH 17/17] iotests: test internal persistent dirty bitmap Vladimir Sementsov-Ogievskiy
2015-09-05 16:48 ` [Qemu-devel] [PATCH v3 RFC 0/17] block: persistent dirty bitmaps Vladimir Sementsov-Ogievskiy
2015-09-05 16:51 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:53 ` Vladimir Sementsov-Ogievskiy
2015-09-05 16:57 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:03 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:09 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:16 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:25 ` Vladimir Sementsov-Ogievskiy
2015-09-05 17:30 ` Vladimir Sementsov-Ogievskiy

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=56143CBE.8000908@redhat.com \
    --to=jsnow@redhat.com \
    --cc=den@openvz.org \
    --cc=kwolf@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    --cc=vsementsov@virtuozzo.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).