qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: Fam Zheng <famz@redhat.com>, qemu-devel@nongnu.org
Cc: kwolf@redhat.com,
	Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>,
	jsnow@redhat.com, qemu-block@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v4 12/15] hbitmap: serialization
Date: Fri, 11 Mar 2016 17:27:21 +0100	[thread overview]
Message-ID: <56E2F1E9.9050509@redhat.com> (raw)
In-Reply-To: <1457412306-18940-13-git-send-email-famz@redhat.com>


[-- Attachment #1.1: Type: text/plain, Size: 7920 bytes --]

On 08.03.2016 05:45, Fam Zheng wrote:
> From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> 
> Functions to serialize / deserialize(restore) HBitmap. HBitmap should be
> saved to linear sequence of bits independently of endianness and bitmap
> array element (unsigned long) size. Therefore Little Endian is chosen.
> 
> These functions are appropriate for dirty bitmap migration, restoring
> the bitmap in several steps is available. To save performance, every
> step writes only the last level of the bitmap. All other levels are
> restored by hbitmap_deserialize_finish() as a last step of restoring.
> So, HBitmap is inconsistent while restoring.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> [Fix left shift operand to 1UL; add "finish" parameter. - Fam]
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---
>  include/qemu/hbitmap.h |  79 ++++++++++++++++++++++++++++
>  util/hbitmap.c         | 137 +++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 216 insertions(+)

[...]

> diff --git a/util/hbitmap.c b/util/hbitmap.c
> index 2d3d04c..5f02c17 100644
> --- a/util/hbitmap.c
> +++ b/util/hbitmap.c
> @@ -395,6 +395,143 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item)
>      return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0;
>  }
>  
> +uint64_t hbitmap_serialization_granularity(const HBitmap *hb)
> +{
> +    /* Require at least 64 bit granularity to be safe on both 64 bit and 32 bit
> +     * hosts. */
> +    return 64 << hb->granularity;
> +}
> +
> +/* Start should be aligned to serialization granularity, chunk size should be
> + * aligned to serialization granularity too, except for last chunk.
> + */
> +static void serialization_chunk(const HBitmap *hb,
> +                                uint64_t start, uint64_t count,
> +                                unsigned long **first_el, size_t *el_count)
> +{
> +    uint64_t last = start + count - 1;
> +    uint64_t gran = hbitmap_serialization_granularity(hb);
> +
> +    assert((start & (gran - 1)) == 0);
> +    assert((last >> hb->granularity) < hb->size);
> +    if ((last >> hb->granularity) != hb->size - 1) {
> +        assert((count & (gran - 1)) == 0);
> +    }
> +
> +    start = (start >> hb->granularity) >> BITS_PER_LEVEL;
> +    last = (last >> hb->granularity) >> BITS_PER_LEVEL;
> +
> +    *first_el = &hb->levels[HBITMAP_LEVELS - 1][start];
> +    *el_count = last - start + 1;
> +}
> +
> +uint64_t hbitmap_serialization_size(const HBitmap *hb,
> +                                    uint64_t start, uint64_t count)
> +{
> +    uint64_t el_count;
> +    unsigned long *cur;
> +
> +    if (!count) {
> +        return 0;
> +    }
> +    serialization_chunk(hb, start, count, &cur, &el_count);
> +
> +    return el_count * sizeof(unsigned long);
> +}
> +
> +void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf,
> +                            uint64_t start, uint64_t count)
> +{
> +    uint64_t el_count;
> +    unsigned long *cur, *end;
> +
> +    if (!count) {
> +        return;
> +    }
> +    serialization_chunk(hb, start, count, &cur, &el_count);
> +    end = cur + el_count;
> +
> +    while (cur != end) {
> +        unsigned long el =
> +            (BITS_PER_LONG == 32 ? cpu_to_le32(*cur) : cpu_to_le64(*cur));

Looks a bit fishy, but I can't come up with anything better.

(Other than adding cpu_to_leul(); we already do have leul_to_cpu(), so
that wouldn't be too far off.)

> +
> +        memcpy(buf, &el, sizeof(el));

One could have used cpu_to_le32/64w((uint32/64_t *)buf, *cur); instead.

Maybe I'd like the following better:

#if BITS_PER_LONG == 32
cpu_to_le32w((uint32_t *)buf, *cur);
#elif BITS_PER_LONG == 64
cpu_to_le64w((uint64_t *)buf, *cur);
#else
#error Unknown long size
#endif

Or just

#else /* BITS_PER_LONG == 64 */

instead of the #elif. I think that's safe to assume.

> +        buf += sizeof(el);
> +        cur++;
> +    }
> +}
> +
> +void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf,
> +                              uint64_t start, uint64_t count,
> +                              bool finish)
> +{
> +    uint64_t el_count;
> +    unsigned long *cur, *end;
> +
> +    if (!count) {
> +        return;
> +    }
> +    serialization_chunk(hb, start, count, &cur, &el_count);
> +    end = cur + el_count;
> +
> +    while (cur != end) {
> +        memcpy(cur, buf, sizeof(*cur));
> +
> +        if (BITS_PER_LONG == 32) {
> +            le32_to_cpus((uint32_t *)cur);
> +        } else {
> +            le64_to_cpus((uint64_t *)cur);
> +        }

Here, I'd definitely like that variant better, i.e.

#if BITS_PER_LONG == 32
le32_to_cpuw(cur, *(uint32_t *)buf);
#else /* BITS_PER_LONG == 64 */
le64_to_cpuw(cur, *(uint64_t *)buf);
#endif

Unless a language lawyer NACKs this because the pointer cast violates
strict aliasing.

If so, I still strongly recommend replacing the if by an #if, not least
because this saves us the pointer cast on cur.

(Or does it? Maybe one still needs to explicitly cast an unsigned long *
to uint32_t * even if both have the same size. However, in that case we
can still do *cur = le32/64_to_cpu(*cur). The above would then become:

#if BITS_PER_LONG == 32
*cur = le32_to_cpu(*(uint32_t *)buf);
#else /* BITS_PER_LONG == 64 */
*cur = le64_to_cpu(*(uint64_t *)buf);
#endif

)

> +
> +        buf += sizeof(unsigned long);
> +        cur++;
> +    }
> +    if (finish) {
> +        hbitmap_deserialize_finish(hb);
> +    }
> +}
> +
> +void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count,
> +                                bool finish)
> +{
> +    uint64_t el_count;
> +    unsigned long *first;
> +
> +    if (!count) {
> +        return;
> +    }
> +    serialization_chunk(hb, start, count, &first, &el_count);
> +
> +    memset(first, 0, el_count * sizeof(unsigned long));
> +    if (finish) {
> +        hbitmap_deserialize_finish(hb);
> +    }
> +}
> +
> +void hbitmap_deserialize_finish(HBitmap *bitmap)
> +{
> +    int64_t i, size, prev_size;
> +    int lev;
> +
> +    /* restore levels starting from penultimate to zero level, assuming
> +     * that the last level is ok */
> +    size = MAX((bitmap->size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1);
> +    for (lev = HBITMAP_LEVELS - 1; lev-- > 0; ) {

I'm always amazed of how much this notation (x-- > 0, or x --> 0, even
worse) confuses me. Or rather I'm always amazed of how much it confuses
me and how much other people love it (as opposed to me, who's rather
unhappy every time he doesn't use all of the three "parameters" for a
for loop).

This time I wondered why we start at HBITMAP_LEVELS - 1 -- that's the
ultimate and not the penultimate level after all. But of course, the
loop condition is evaluated before the loop is executed for the first
time, so we will indeed start at HBITMAP_LEVELS - 2.

So, yeah, this is correct.

> +        prev_size = size;
> +        size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1);
> +        memset(bitmap->levels[lev], 0, size * sizeof(unsigned long));
> +
> +        for (i = 0; i < prev_size; ++i) {

I hate using stand-alone pre-increment for scalars with a passion.

Unfortunately, checkpatch does not.

Reviewed-by: Max Reitz <mreitz@redhat.com>

(All of the above are me just nagging and giving optional suggestions.)

> +            if (bitmap->levels[lev + 1][i]) {
> +                bitmap->levels[lev][i >> BITS_PER_LEVEL] |=
> +                    1UL << (i & (BITS_PER_LONG - 1));
> +            }
> +        }
> +    }
> +
> +    bitmap->levels[0][0] |= 1UL << (BITS_PER_LONG - 1);
> +}
> +
>  void hbitmap_free(HBitmap *hb)
>  {
>      unsigned i;
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

  reply	other threads:[~2016-03-11 16:27 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-08  4:44 [Qemu-devel] [PATCH v4 00/15] Dirty bitmap changes for migration/persistence work Fam Zheng
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 01/15] backup: Use Bitmap to replace "s->bitmap" Fam Zheng
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 02/15] block: Include hbitmap.h in block.h Fam Zheng
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 03/15] typedefs: Add BdrvDirtyBitmap Fam Zheng
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 04/15] block: Move block dirty bitmap code to separate files Fam Zheng
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 05/15] block: Remove unused typedef of BlockDriverDirtyHandler Fam Zheng
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 06/15] block: Hide HBitmap in block dirty bitmap interface Fam Zheng
2016-03-11 13:54   ` Max Reitz
2016-03-11 14:27     ` Max Reitz
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 07/15] HBitmap: Introduce "meta" bitmap to track bit changes Fam Zheng
2016-03-11 14:48   ` Max Reitz
2016-03-08  4:44 ` [Qemu-devel] [PATCH v4 08/15] tests: Add test code for meta bitmap Fam Zheng
2016-03-11 14:58   ` Max Reitz
2016-06-03  2:38     ` Fam Zheng
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 09/15] block: Support meta dirty bitmap Fam Zheng
2016-03-11 15:17   ` Max Reitz
2016-06-03  2:42     ` Fam Zheng
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 10/15] block: Add two dirty bitmap getters Fam Zheng
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 11/15] block: Assert that bdrv_release_dirty_bitmap succeeded Fam Zheng
2016-03-11 15:25   ` Max Reitz
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 12/15] hbitmap: serialization Fam Zheng
2016-03-11 16:27   ` Max Reitz [this message]
2016-03-14 11:58     ` Vladimir Sementsov-Ogievskiy
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 13/15] block: BdrvDirtyBitmap serialization interface Fam Zheng
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 14/15] tests: Add test code for hbitmap serialization Fam Zheng
2016-03-08  4:45 ` [Qemu-devel] [PATCH v4 15/15] block: More operations for meta dirty bitmap Fam Zheng
2016-03-11 16:32   ` Max Reitz
2016-03-11 13:57 ` [Qemu-devel] [PATCH v4 00/15] Dirty bitmap changes for migration/persistence work Max Reitz
2016-03-11 19:30   ` John Snow
2016-05-25 14:45 ` Vladimir Sementsov-Ogievskiy
2016-05-26  0:47   ` Fam Zheng
2016-06-02 11:43     ` Vladimir Sementsov-Ogievskiy
2016-06-02 22:49       ` John Snow
2016-06-03  2:22         ` Fam Zheng

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=56E2F1E9.9050509@redhat.com \
    --to=mreitz@redhat.com \
    --cc=famz@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --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).