qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: Fam Zheng <famz@redhat.com>, qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>,
	qemu-block@nongnu.org, jsnow@redhat.com,
	Markus Armbruster <armbru@redhat.com>,
	mreitz@redhat.com, vsementsov@parallels.com,
	Stefan Hajnoczi <stefanha@redhat.com>
Subject: Re: [Qemu-devel] [RFC PATCH 10/16] qbm: Implement format driver
Date: Wed, 17 Feb 2016 16:30:40 +0300	[thread overview]
Message-ID: <56C47600.20806@virtuozzo.com> (raw)
In-Reply-To: <1453804705-7205-11-git-send-email-famz@redhat.com>

As I understand, the difference between our driver interfaces:

Fam:
   methods:
     bdrv_dirty_bitmap_set_persistent
   all persistent bitmaps are loaded, and they all are enabled

Me:
   methods:
     bdrv_dirty_bitmap_load              \   bitmaps loaded on demand, 
by name (by cmd line at qemu start for example). So there are may be 
disabled bitmaps in qcow2, which are not loaded.
     bdrv_dirty_bitmap_load_check   /   I'm not sure that is necessary 
feature, it should be discussed
     bdrv_dirty_bitmap_store   - called from bdrv_close, exists as 
mirrored _load method.

The other difference is sync-policy:

Fam:
    - use meta bitmaps
    - sync (aio write) after each write to disk. We have discussed it a 
lot, and I can't see real application of it (will aio requests be lost 
on qemu crash?). On the other hand, I'm afraid that performance will 
suffer.. However, now it may be tested easily.

Me:
    - no meta bitmaps, so I have to save whole bitmaps on close...
    - just save bitmaps on close.

On 26.01.2016 13:38, Fam Zheng wrote:
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---
>   block/Makefile.objs |    1 +
>   block/qbm.c         | 1315 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 1316 insertions(+)
>   create mode 100644 block/qbm.c
>
> diff --git a/block/Makefile.objs b/block/Makefile.objs
> index cdd8655..1111ba7 100644
> --- a/block/Makefile.objs
> +++ b/block/Makefile.objs
> @@ -5,6 +5,7 @@ block-obj-y += qed-check.o
>   block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
>   block-obj-y += quorum.o
>   block-obj-y += parallels.o blkdebug.o blkverify.o
> +block-obj-y += qbm.o
>   block-obj-y += block-backend.o snapshot.o qapi.o
>   block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
>   block-obj-$(CONFIG_POSIX) += raw-posix.o
> diff --git a/block/qbm.c b/block/qbm.c
> new file mode 100644
> index 0000000..91e129f
> --- /dev/null
> +++ b/block/qbm.c
> @@ -0,0 +1,1315 @@
> +/*
> + * Block driver for the QBM format
> + *
> + * Copyright (c) 2016 Red Hat Inc.
> + *
> + * Authors:
> + *     Fam Zheng <famz@redhat.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu-common.h"
> +#include "block/block_int.h"
> +#include "qapi/qmp/qerror.h"
> +#include "qemu/error-report.h"
> +#include "qemu/module.h"
> +#include "migration/migration.h"
> +#include "qapi/qmp/qint.h"
> +#include "qapi/qmp/qjson.h"
> +
> +#define QBM_BUF_SIZE_MAX (32 << 20)
> +
> +typedef enum QBMBitmapType {
> +    QBM_TYPE_DIRTY,
> +    QBM_TYPE_ALLOC,
> +} QBMBitmapType;
> +
> +typedef struct QBMBitmap {
> +    BdrvDirtyBitmap *bitmap;
> +    BdrvChild *file;
> +    char *name;
> +    QBMBitmapType type;
> +} QBMBitmap;
> +
> +typedef struct BDRVQBMState {
> +    BdrvChild *image;
> +    BdrvDirtyBitmap *alloc_bitmap;
> +    QDict *desc;
> +    QDict *backing_dict;
> +    QBMBitmap *bitmaps;
> +    int num_bitmaps;
> +} BDRVQBMState;
> +
> +static const char *qbm_token_consume(const char *p, const char *token)
> +{
> +    size_t len = strlen(token);
> +
> +    if (!p) {
> +        return NULL;
> +    }
> +    while (*p && (*p == ' ' ||
> +                  *p == '\t' ||
> +                  *p == '\n' ||
> +                  *p == '\r')) {
> +        p++;
> +    }
> +    if (strncmp(p, token, len)) {
> +        return p + len;
> +    }
> +    return NULL;
> +}
> +
> +static int qbm_probe(const uint8_t *buf, int buf_size, const char *filename)
> +{
> +    const char *p;
> +    p = strstr((const char *)buf, "\"QBM\"");
> +    if (!p) {
> +        p = strstr((const char *)buf, "'QBM'");
> +    }
> +    if (!p) {
> +        return 0;
> +    }
> +    p = qbm_token_consume(p, ":");
> +    p = qbm_token_consume(p, "{");
> +    if (p && *p) {
> +        return 100;
> +    }
> +    return 0;
> +}
> +
> +static void qbm_load_bitmap(BlockDriverState *bs, QBMBitmap *bm, Error **errp)
> +{
> +    int r;
> +    BDRVQBMState *s = bs->opaque;
> +    int64_t bitmap_file_size;
> +    int64_t bitmap_size;
> +    uint8_t *buf = NULL;
> +    BlockDriverState *file = bm->file->bs;
> +    int64_t image_size = bdrv_getlength(s->image->bs);
> +
> +    if (image_size < 0) {
> +        error_setg(errp, "Cannot get image size: %s", s->image->bs->filename);
> +        return;
> +    }
> +    bitmap_size = bdrv_dirty_bitmap_serialization_size(bm->bitmap, 0,
> +                        bdrv_dirty_bitmap_size(bm->bitmap));
> +    if (bitmap_size > QBM_BUF_SIZE_MAX) {
> +        error_setg(errp, "Bitmap too big");
> +        return;
> +    }
> +    bitmap_file_size = bdrv_getlength(file);
> +    if (bitmap_file_size < bitmap_size) {
> +        error_setg(errp,
> +                   "Bitmap \"%s\" file too small "
> +                   "(expecting at least %ld bytes but got %ld bytes): %s",
> +                   bm->name, bitmap_size, bitmap_file_size, file->filename);
> +        goto out;
> +    }
> +    buf = qemu_blockalign(file, bitmap_size);
> +    r = bdrv_pread(file, 0, buf, bitmap_size);
> +    if (r < 0) {
> +        error_setg(errp, "Failed to read bitmap file \"%s\"",
> +                   file->filename);
> +        goto out;
> +    }
> +    bdrv_dirty_bitmap_deserialize_part(bm->bitmap, buf, 0, bs->total_sectors,
> +                                       true);

I now think, that to reduce copying, it is better to implement 
hbitmap_{load,store}, wait for my v4 for qcow2 bitmaps

> +
> +out:
> +    g_free(buf);
> +}
> +
> +static int qbm_reopen_prepare(BDRVReopenState *state,
> +                              BlockReopenQueue *queue, Error **errp)
> +{
> +    return 0;
> +}
> +
> +static void qbm_get_fullname(BlockDriverState *bs, char *dest, size_t sz,
> +                             const char *filename)
> +{
> +    const char *base, *p;
> +
> +    base = bs->exact_filename[0] ? bs->exact_filename : bs->filename;
> +
> +    if (strstart(base, "json:", NULL)) {
> +        /* There is not much we can do with a json: file name, try bs->file and
> +         * cross our fingers. */
> +        if (bs->file) {
> +            qbm_get_fullname(bs->file->bs, dest, sz, filename);
> +        } else {
> +            pstrcpy(dest, sz, filename);
> +        }
> +        return;
> +    }
> +
> +    p = strrchr(base, '/');
> +
> +    assert(sz > 0);
> +    if (path_has_protocol(filename) || path_is_absolute(filename)) {
> +        pstrcpy(dest, sz, filename);
> +        return;
> +    }
> +
> +    if (p) {
> +        pstrcpy(dest, MIN(sz, p - base + 2), base);
> +    } else {
> +        dest[0] = '\0';
> +    }
> +    pstrcat(dest, sz, filename);
> +}
> +
> +static BdrvChild *qbm_open_image(BlockDriverState *bs,
> +                                 QDict *image, QDict *options,
> +                                 Error **errp)
> +{
> +    BdrvChild *child;
> +    const char *filename = qdict_get_try_str(image, "file");
> +    const char *fmt = qdict_get_try_str(image, "format");
> +    const char *checksum = qdict_get_try_str(image, "checksum");
> +    char fullname[PATH_MAX];
> +
> +    if (!filename) {
> +        error_setg(errp, "Image missing 'file' field");
> +        return NULL;
> +    }
> +    if (!fmt) {
> +        error_setg(errp, "Image missing 'format' field");
> +        return NULL;
> +    }
> +    qbm_get_fullname(bs, fullname, sizeof(fullname), filename);
> +    qdict_put(options, "image.driver", qstring_from_str(fmt));
> +    child = bdrv_open_child(fullname, options, "image", bs, &child_file, false,
> +                            errp);
> +    if (!child) {
> +        goto out;
> +    }
> +    if (checksum) {
> +        /* TODO: compare checksum when we support this */
> +        error_setg(errp, "Checksum not supported");
> +    }
> +out:
> +    return child;
> +}
> +
> +/* Open and load the persistent bitmap and return the created QBMBitmap object.
> + * If reuse_bitmap is not NULL, we skip bdrv_create_dirty_bitmap and reuse it.
> + **/
> +static QBMBitmap *qbm_open_bitmap(BlockDriverState *bs,
> +                                  const char *name,
> +                                  const char *filename, int granularity,
> +                                  QBMBitmapType type,
> +                                  BdrvDirtyBitmap *reuse_bitmap,
> +                                  Error **errp)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    QBMBitmap *bm;
> +    BdrvChild *file;
> +    BdrvDirtyBitmap *bdrv_bitmap;
> +    char *key;
> +    QDict *options;
> +    char fullname[PATH_MAX];
> +    Error *local_err = NULL;
> +
> +    qbm_get_fullname(bs, fullname, sizeof(fullname), filename);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return NULL;
> +    }
> +    s->bitmaps = g_realloc_n(s->bitmaps, s->num_bitmaps + 1,
> +                             sizeof(QBMBitmap));
> +
> +    /* Create options for the bitmap child BDS */
> +    options = qdict_new();
> +    key = g_strdup_printf("bitmap-%s.driver", name);
> +    qdict_put(options, key, qstring_from_str("raw"));
> +    g_free(key);
> +
> +    /* Open the child as plain "file" */
> +    key = g_strdup_printf("bitmap-%s", name);
> +    file = bdrv_open_child(fullname, options, key, bs, &child_file, false,
> +                           errp);
> +    g_free(key);
> +    QDECREF(options);
> +    if (!file) {
> +        return NULL;
> +    }
> +
> +    if (reuse_bitmap) {
> +        bdrv_bitmap = reuse_bitmap;
> +    } else {
> +        bdrv_bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
> +        if (!bdrv_bitmap) {
> +            bdrv_unref_child(bs, file);
> +            return NULL;
> +        }
> +        bdrv_dirty_bitmap_set_persistent(bs, bdrv_bitmap, true, true, NULL);
> +    }
> +    bdrv_create_meta_dirty_bitmap(bdrv_bitmap, BDRV_SECTOR_SIZE);
> +
> +    bm = &s->bitmaps[s->num_bitmaps++];
> +    bm->file = file;
> +    bm->name = g_strdup(name);
> +    bm->type = type;
> +    bm->bitmap = bdrv_bitmap;
> +    if (type == QBM_TYPE_ALLOC) {
> +        assert(!s->alloc_bitmap);
> +        s->alloc_bitmap = bdrv_bitmap;
> +        /* Align the request to granularity so the block layer will take care
> +         * of RMW for partial writes. */
> +        bs->request_alignment = granularity;
> +    }
> +    return bm;
> +}
> +
> +typedef struct QBMIterState {
> +    QDict *options;
> +    BlockDriverState *bs;
> +    Error *err;
> +    bool has_backing;
> +} QBMIterState;
> +
> +static void qbm_bitmap_iter(const char *key, QObject *obj, void *opaque)
> +{
> +    QDict *dict;
> +    const char *filename, *typename;
> +    QBMBitmapType type;
> +    int granularity;
> +    QBMIterState *state = opaque;
> +    BDRVQBMState *s = state->bs->opaque;
> +    QBMBitmap *bm;
> +
> +    if (state->err) {
> +        return;
> +    }
> +    dict = qobject_to_qdict(obj);
> +    if (!dict) {
> +        error_setg(&state->err, "'%s' is not a dicionary", key);
> +        return;
> +    }
> +    filename = qdict_get_try_str(dict, "file");
> +    if (!filename) {
> +        error_setg(&state->err, "\"file\" is missing in bitmap \"%s\"", key);
> +        return;
> +    }
> +    typename = qdict_get_try_str(dict, "type");
> +    if (!typename) {
> +        error_setg(&state->err, "\"value\" is missing in bitmap \"%s\"", key);
> +        return;
> +    } else if (!strcmp(typename, "dirty")) {
> +        type = QBM_TYPE_DIRTY;
> +    } else if (!strcmp(typename, "allocation")) {
> +        QDict *backing_dict = qdict_get_qdict(dict, "backing");
> +        type = QBM_TYPE_ALLOC;
> +        if (backing_dict) {
> +            if (state->has_backing) {
> +                error_setg(&state->err, "Multiple backing is not supported");
> +                return;
> +            }
> +            state->has_backing = true;
> +            pstrcpy(state->bs->backing_file, PATH_MAX,
> +                    qdict_get_try_str(backing_dict, "file"));
> +            if (qdict_haskey(backing_dict, "format")) {
> +                pstrcpy(state->bs->backing_format,
> +                        sizeof(state->bs->backing_format),
> +                        qdict_get_try_str(backing_dict, "format"));
> +                }
> +            s->backing_dict = backing_dict;
> +            if (!strlen(state->bs->backing_file)) {
> +                error_setg(&state->err, "Backing file name not specified");
> +                return;
> +            }
> +        }
> +    } else {
> +        error_setg(&state->err, "\"value\" is missing in bitmap \"%s\"", key);
> +        return;
> +    }
> +    granularity = qdict_get_try_int(dict, "granularity-bytes", -1);
> +    if (granularity == -1) {
> +        error_setg(&state->err, "\"granularity\" is missing in bitmap \"%s\"",
> +                   key);
> +        return;
> +    } else if (granularity & (granularity - 1)) {
> +        error_setg(&state->err, "\"granularity\" must be power of two");
> +        return;
> +    } else if (granularity < 512) {
> +        error_setg(&state->err, "\"granularity\" too small");
> +        return;
> +    }
> +
> +    bm = qbm_open_bitmap(state->bs, key, filename, granularity,
> +                         type, NULL, &state->err);
> +    if (!bm) {
> +        return;
> +    }
> +    qbm_load_bitmap(state->bs, bm, &state->err);
> +}
> +
> +static void qbm_release_bitmap(BlockDriverState *bs, QBMBitmap *bm)
> +{
> +    bdrv_release_meta_dirty_bitmap(bm->bitmap);
> +    bdrv_release_dirty_bitmap(bs, bm->bitmap);
> +    bdrv_unref_child(bs, bm->file);
> +}
> +
> +static void qbm_release_bitmaps(BlockDriverState *bs)
> +{
> +    int i;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    for (i = 0; i < s->num_bitmaps; i++) {
> +        QBMBitmap *bm = &s->bitmaps[i];
> +        bdrv_flush(bm->file->bs);
> +        qbm_release_bitmap(bs, bm);
> +        g_free(bm->name);
> +    }
> +}
> +
> +static int qbm_open_bitmaps(BlockDriverState *bs, QDict *bitmaps,
> +                            QDict *options, Error **errp)
> +{
> +    QBMIterState state = (QBMIterState) {
> +        .bs = bs,
> +        .options = options,
> +    };
> +    qdict_iter(bitmaps, qbm_bitmap_iter, &state);
> +    if (state.err) {
> +        qbm_release_bitmaps(bs);
> +        error_propagate(errp, state.err);
> +        return -EINVAL;
> +    }
> +    return 0;
> +}
> +
> +static int qbm_open(BlockDriverState *bs, QDict *options, int flags,
> +                    Error **errp)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    int ret;
> +    int64_t len;
> +    char *desc;
> +    QDict *dict, *image_dict, *bitmaps;
> +
> +    len = bdrv_getlength(bs->file->bs);
> +    if (len > QBM_BUF_SIZE_MAX) {
> +        error_setg(errp, "QBM description file too big.");
> +        return -ENOMEM;
> +    } else if (len < 0) {
> +        error_setg(errp, "Failed to get descriptor file size");
> +        return len;
> +    } else if (!len) {
> +        error_setg(errp, "Empty file");
> +        return -EINVAL;
> +    }
> +
> +    desc = qemu_blockalign(bs->file->bs, len);
> +    ret = bdrv_pread(bs->file->bs, 0, desc, len);
> +    if (ret < 0) {
> +        goto out;
> +    }
> +    dict = qobject_to_qdict(qobject_from_json(desc));
> +    if (!dict || !qdict_haskey(dict, "QBM")) {
> +        error_setg(errp, "Failed to parse json from file");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +    s->desc = qdict_get_qdict(dict, "QBM");
> +    if (!s->desc) {
> +        error_setg(errp, "Json doesn't have key \"QBM\"");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +    if (qdict_get_try_int(s->desc, "version", -1) != 1) {
> +        error_setg(errp, "Invalid version of json file");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +    if (!qdict_haskey(s->desc, "image")) {
> +        error_setg(errp, "Key \"image\" not found in json file");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +    image_dict = qdict_get_qdict(s->desc, "image");
> +    if (!image_dict) {
> +        error_setg(errp, "\"image\" information invalid");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +
> +    s->image = qbm_open_image(bs, image_dict, options, errp);
> +    if (!s->image) {
> +        ret = -EIO;
> +        goto out;
> +    }
> +    bs->total_sectors = bdrv_nb_sectors(s->image->bs);
> +    if (bs->total_sectors < 0) {
> +        error_setg(errp, "Failed to get image size");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +
> +    bitmaps = qdict_get_qdict(s->desc, "bitmaps");
> +    if (!bitmaps) {
> +        error_setg(errp, "\"bitmaps\" not found");
> +        ret = -EINVAL;
> +        goto out;
> +    }
> +
> +    ret = qbm_open_bitmaps(bs, bitmaps, options, errp);
> +
> +out:
> +    g_free(desc);
> +    return ret;
> +}
> +
> +
> +static void qbm_refresh_limits(BlockDriverState *bs, Error **errp)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    Error *local_err = NULL;
> +
> +    bdrv_refresh_limits(s->image->bs, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +    bs->bl.min_mem_alignment = s->image->bs->bl.min_mem_alignment;
> +    bs->bl.opt_mem_alignment = s->image->bs->bl.opt_mem_alignment;
> +    bs->bl.write_zeroes_alignment = bdrv_get_cluster_size(bs);
> +}
> +
> +static int64_t coroutine_fn qbm_co_get_block_status(BlockDriverState *bs,
> +                                                    int64_t sector_num,
> +                                                    int nb_sectors,
> +                                                    int *pnum,
> +                                                    BlockDriverState **file)
> +{
> +    bool alloc = true;
> +    int64_t next;
> +    int cluster_sectors;
> +    BDRVQBMState *s = bs->opaque;
> +    int64_t ret = BDRV_BLOCK_OFFSET_VALID;
> +
> +    if (!s->alloc_bitmap) {
> +        return bdrv_get_block_status(s->image->bs, sector_num, nb_sectors,
> +                                     pnum, file);
> +    }
> +
> +    ret |= BDRV_BLOCK_OFFSET_MASK & (sector_num << BDRV_SECTOR_BITS);
> +    next = sector_num;
> +    cluster_sectors = bdrv_dirty_bitmap_granularity(s->alloc_bitmap)
> +                            >> BDRV_SECTOR_BITS;
> +    while (next < sector_num + nb_sectors) {
> +        if (next == sector_num) {
> +            alloc = bdrv_get_dirty(bs, s->alloc_bitmap, next);
> +        } else if (bdrv_get_dirty(bs, s->alloc_bitmap, next) != alloc) {
> +            break;
> +        }
> +        next += cluster_sectors - next % cluster_sectors;
> +    }
> +    *pnum = MIN(next - sector_num, nb_sectors);
> +
> +    ret |= alloc ? BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED : 0;
> +    *file = alloc ? s->image->bs : NULL;
> +
> +    return ret;
> +}
> +
> +static int qbm_save_desc(BlockDriverState *desc_file, QDict *desc)
> +{
> +    int ret;
> +    const char *str;
> +    size_t len;
> +    QString *json_str = NULL;
> +    QDict *td;
> +
> +    ret = bdrv_truncate(desc_file, 0);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    td = qdict_new();
> +    /* Grab an extra reference so it doesn't get freed with td */
> +    QINCREF(desc);
> +    qdict_put(td, "QBM", desc);
> +
> +    json_str = qobject_to_json_pretty(QOBJECT(td));
> +    str = qstring_get_str(json_str);
> +    len = strlen(str);
> +    ret = bdrv_pwrite(desc_file, 0, str, len);
> +    /* End the json file with a new line, doesn't hurt if it fails. */
> +    bdrv_pwrite(desc_file, len, "\n", 1);
> +    /* bdrv_pwrite write padding zeros to align to sector, we don't need that
> +     * for a text file */
> +    bdrv_truncate(desc_file, len + 1);
> +    QDECREF(json_str);
> +    QDECREF(td);
> +    return ret == len ? 0 : -EIO;
> +}
> +
> +static coroutine_fn int qbm_co_readv(BlockDriverState *bs, int64_t sector_num,
> +                                     int nb_sectors, QEMUIOVector *qiov)
> +{
> +    QEMUIOVector local_qiov;
> +    BDRVQBMState *s = bs->opaque;
> +    BdrvDirtyBitmapIter *iter;
> +    int done_sectors = 0;
> +    int ret;
> +    int64_t next_allocated;
> +    int64_t cur_sector = sector_num;
> +    int granularity_sectors;
> +
> +    if (!s->alloc_bitmap) {
> +        return bdrv_co_readv(s->image->bs, sector_num, nb_sectors, qiov);
> +    }
> +    granularity_sectors = bdrv_dirty_bitmap_granularity(s->alloc_bitmap)
> +                            >> BDRV_SECTOR_BITS;
> +    iter = bdrv_dirty_iter_new(s->alloc_bitmap, sector_num);
> +    qemu_iovec_init(&local_qiov, qiov->niov);
> +    do {
> +        int64_t n;
> +        int64_t consective_end;
> +        next_allocated = bdrv_dirty_iter_next(iter);
> +        if (next_allocated < 0) {
> +            next_allocated = sector_num + nb_sectors;
> +        } else {
> +            next_allocated = MIN(next_allocated, sector_num + nb_sectors);
> +        }
> +        if (next_allocated > cur_sector) {
> +            /* Read [cur_sector, next_allocated) from backing */
> +            n = next_allocated - cur_sector;
> +            qemu_iovec_reset(&local_qiov);
> +            qemu_iovec_concat(&local_qiov, qiov,
> +                              done_sectors << BDRV_SECTOR_BITS,
> +                              n << BDRV_SECTOR_BITS);
> +            ret = bdrv_co_readv(bs->backing->bs, cur_sector, n, &local_qiov);
> +            if (ret) {
> +                goto out;
> +            }
> +            done_sectors += n;
> +            cur_sector += n;
> +            if (done_sectors == nb_sectors) {
> +                break;
> +            }
> +        }
> +        consective_end = next_allocated;
> +        /* Find consective allocated sectors */
> +        while (consective_end < sector_num + nb_sectors) {
> +            int64_t next = bdrv_dirty_iter_next(iter);
> +            if (next < 0 || next - consective_end > granularity_sectors) {
> +                /* No more consective sectors */
> +                consective_end += granularity_sectors
> +                                  - consective_end % granularity_sectors;
> +                break;
> +            }
> +            consective_end = next;
> +        }
> +        consective_end = MIN(consective_end, sector_num + nb_sectors);
> +        n = consective_end - cur_sector;
> +        assert(n > 0);
> +        /* Read [cur_sector, consective_end] from image */
> +        qemu_iovec_reset(&local_qiov);
> +        qemu_iovec_concat(&local_qiov, qiov,
> +                          done_sectors << BDRV_SECTOR_BITS,
> +                          n << BDRV_SECTOR_BITS);
> +        ret = bdrv_co_readv(s->image->bs, cur_sector, n, &local_qiov);
> +        if (ret) {
> +            goto out;
> +        }
> +        done_sectors += n;
> +        cur_sector += n;
> +    } while (done_sectors < nb_sectors);
> +out:
> +    qemu_iovec_destroy(&local_qiov);
> +    bdrv_dirty_iter_free(iter);
> +    return ret;
> +}
> +
> +static inline void qbm_check_alignment(BDRVQBMState *s, int64_t sector_num,
> +                                       int nb_sectors)
> +{
> +    if (s->alloc_bitmap) {
> +        int cluster_sectors = bdrv_dirty_bitmap_granularity(s->alloc_bitmap)
> +                                >> BDRV_SECTOR_BITS;
> +        assert(sector_num % cluster_sectors == 0);
> +        assert(nb_sectors % cluster_sectors == 0);
> +    }
> +}
> +
> +typedef struct {
> +    int inflight;
> +    Coroutine *co;
> +    int ret;
> +} QBMBitmapWriteTracker;
> +
> +typedef struct {
> +    QEMUIOVector qiov;
> +    uint8_t *buf;
> +    QBMBitmapWriteTracker *tracker;
> +    BlockDriverState *bs;
> +    QBMBitmap *bitmap;
> +    int64_t sector_num;
> +    int nb_sectors;
> +} QBMBitmapWriteData;
> +
> +static void qbm_write_bitmap_cb(void *opaque, int ret)
> +{
> +    QBMBitmapWriteData *data = opaque;
> +    QBMBitmapWriteTracker *tracker = data->tracker;
> +
> +    qemu_iovec_destroy(&data->qiov);
> +    qemu_vfree(data->buf);
> +    if (!ret) {
> +        bdrv_dirty_bitmap_reset_meta(data->bs,
> +                                     data->bitmap->bitmap,
> +                                     data->sector_num, data->nb_sectors);
> +    }
> +    g_free(data);
> +    tracker->ret = tracker->ret ? : ret;
> +    if (!--tracker->inflight) {
> +        qemu_coroutine_enter(tracker->co, NULL);
> +    }
> +}
> +
> +static int qbm_write_bitmap(BlockDriverState *bs, QBMBitmap *bm,
> +                            int64_t sector_num, int nb_sectors,
> +                            QBMBitmapWriteTracker *tracker)
> +{
> +    QBMBitmapWriteData *data;
> +    int64_t start, end;
> +    int64_t file_sector_num;
> +    int file_nb_sectors;
> +    size_t buf_size;
> +    /* Each bit in the bitmap tracks bdrv_dirty_bitmap_granularity(bm->bitmap)
> +     * bytes of guest data, so each sector in the bitmap tracks
> +     * (bdrv_dirty_bitmap_granularity(bm->bitmap) * BDRV_SECTOR_SIZE *
> +     * BITS_PER_BYTE) bytes of guest data, so in sector unit is: */
> +    int64_t sectors_per_bitmap_sector =
> +        BITS_PER_BYTE * bdrv_dirty_bitmap_granularity(bm->bitmap);
> +    int align = MAX(bdrv_dirty_bitmap_serialization_align(bm->bitmap),
> +                    sectors_per_bitmap_sector);
> +
> +    /* The start sector that is being marked dirty. */
> +    start = QEMU_ALIGN_DOWN(sector_num, align);
> +    /* The end sector that is being marked dirty. */
> +    end = MIN(QEMU_ALIGN_UP(sector_num + nb_sectors, align),
> +              bs->total_sectors);
> +
> +    if (!bdrv_dirty_bitmap_get_meta(bs, bm->bitmap, sector_num, nb_sectors)) {
> +        return 0;
> +    }
> +
> +    file_sector_num = start / sectors_per_bitmap_sector;
> +    buf_size = bdrv_dirty_bitmap_serialization_size(bm->bitmap, start,
> +                                                    end - start);
> +    buf_size = QEMU_ALIGN_UP(buf_size, BDRV_SECTOR_SIZE);
> +    file_nb_sectors = buf_size >> BDRV_SECTOR_BITS;
> +
> +    data = g_new(QBMBitmapWriteData, 1);
> +    data->buf = qemu_blockalign0(bm->file->bs, buf_size);
> +    bdrv_dirty_bitmap_serialize_part(bm->bitmap, data->buf, start,
> +                                     end - start);
> +    qemu_iovec_init(&data->qiov, 1);
> +    qemu_iovec_add(&data->qiov, data->buf, buf_size);
> +    data->tracker = tracker;
> +    data->sector_num = start;
> +    data->nb_sectors = end - start;
> +    data->bs = bm->file->bs;
> +    data->bitmap = bm;
> +    bdrv_aio_writev(bm->file->bs, file_sector_num, &data->qiov,
> +                    file_nb_sectors, qbm_write_bitmap_cb,
> +                    data);
> +    return -EINPROGRESS;
> +}
> +
> +static int qbm_write_bitmaps(BlockDriverState *bs, int64_t sector_num,
> +                             int nb_sectors)
> +{
> +    int i;
> +    BDRVQBMState *s = bs->opaque;
> +    QBMBitmapWriteTracker tracker = (QBMBitmapWriteTracker) {
> +        .inflight = 1, /* So that no aio completion will call
> +                          qemu_coroutine_enter before we yield. */
> +        .co = qemu_coroutine_self(),
> +    };
> +
> +    for (i = 0; i < s->num_bitmaps; i++) {
> +        int ret = qbm_write_bitmap(bs, &s->bitmaps[i],
> +                                   sector_num, nb_sectors, &tracker);
> +        if (ret == -EINPROGRESS) {
> +            tracker.inflight++;
> +        } else if (ret < 0) {
> +            tracker.ret = ret;
> +            break;
> +        }
> +    }
> +    tracker.inflight--;
> +    if (tracker.inflight) {
> +        /* At least one aio in submitted, wait. */
> +        qemu_coroutine_yield();
> +    }
> +    return tracker.ret;
> +}
> +
> +static coroutine_fn int qbm_co_writev(BlockDriverState *bs, int64_t sector_num,
> +                                      int nb_sectors, QEMUIOVector *qiov)
> +{
> +    int ret;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    qbm_check_alignment(s, sector_num, nb_sectors);
> +    ret = bdrv_co_writev(s->image->bs, sector_num, nb_sectors, qiov);
> +    if (ret) {
> +        return ret;
> +    }
> +    return qbm_write_bitmaps(bs, sector_num, nb_sectors);

So, you create aio write request for each bitmap on each write to disk. 
Isn't it damage for performance?

> +}
> +
> +static int coroutine_fn qbm_co_write_zeroes(BlockDriverState *bs,
> +                                            int64_t sector_num,
> +                                            int nb_sectors,
> +                                            BdrvRequestFlags flags)
> +{
> +    int ret;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    qbm_check_alignment(s, sector_num, nb_sectors);
> +    ret = bdrv_co_write_zeroes(s->image->bs, sector_num, nb_sectors, flags);
> +    if (ret) {
> +        return ret;
> +    }
> +    return qbm_write_bitmaps(bs, sector_num, nb_sectors);
> +}
> +
> +static coroutine_fn int qbm_co_discard(BlockDriverState *bs,
> +                                       int64_t sector_num,
> +                                       int nb_sectors)
> +{
> +    int ret;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    ret = bdrv_co_discard(s->image->bs, sector_num, nb_sectors);
> +    if (ret) {
> +        return ret;
> +    }
> +    return qbm_write_bitmaps(bs, sector_num, nb_sectors);
> +}
> +
> +static int qbm_make_empty(BlockDriverState *bs)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    BlockDriverState *image_bs = s->image->bs;
> +    int ret = 0;
> +
> +    if (image_bs->drv->bdrv_make_empty) {
> +        ret = image_bs->drv->bdrv_make_empty(s->image->bs);
> +        if (ret) {
> +            return ret;
> +        }
> +    } else if (!s->alloc_bitmap) {
> +        return -ENOTSUP;
> +    }
> +    if (s->alloc_bitmap) {
> +        int i;
> +        bdrv_clear_dirty_bitmap(s->alloc_bitmap, NULL);
> +        for (i = 0; i < s->num_bitmaps; i++) {
> +            QBMBitmap *bm = &s->bitmaps[i];
> +            if (bm->bitmap != s->alloc_bitmap) {
> +                continue;
> +            }
> +            ret = bdrv_write_zeroes(bm->file->bs, 0,
> +                                    DIV_ROUND_UP(bdrv_getlength(bm->file->bs),
> +                                                 BDRV_SECTOR_SIZE),
> +                                    BDRV_REQ_MAY_UNMAP);
> +        }
> +    }
> +    return ret;
> +}
> +
> +/* Create a file with given size, and return the relative path. */
> +static char *qbm_create_file(BlockDriverState *bs, const char *name,
> +                             const char *ext,
> +                             int64_t size, Error **errp)
> +{
> +    char *filename = NULL;
> +    Error *local_err = NULL;
> +    char fullname[PATH_MAX];
> +    char path[PATH_MAX];
> +    char prefix[PATH_MAX];
> +    char postfix[PATH_MAX];
> +
> +    filename_decompose(bs->filename, path, prefix,
> +                       postfix, PATH_MAX, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return NULL;
> +    }
> +    filename = g_strdup_printf("%s-%s.%s%s", prefix, name, ext, postfix);
> +    qbm_get_fullname(bs, fullname, sizeof(fullname), filename);
> +
> +    bdrv_img_create(fullname, "raw", NULL, NULL, NULL, size, 0,
> +                    &local_err, true);
> +    if (local_err) {
> +        g_free(filename);
> +        filename = NULL;
> +        error_propagate(errp, local_err);
> +    }
> +    return filename;
> +}
> +
> +static QDict *qbm_create_image_dict(BlockDriverState *bs,
> +                                    const char *image_name,
> +                                    const char *format,
> +                                    Error **errp)
> +{
> +    QDict *dict;
> +    char fullname[PATH_MAX];
> +
> +    qbm_get_fullname(bs, fullname, sizeof(fullname), image_name);
> +    dict = qdict_new();
> +    qdict_put(dict, "file", qstring_from_str(image_name));
> +    qdict_put(dict, "format", qstring_from_str(format ? : ""));
> +    /* TODO: Set checksum when we support it. */
> +
> +    return dict;
> +}
> +
> +static inline QDict *qbm_make_bitmap_dict(const char *filename,
> +                                          int granularity,
> +                                          QBMBitmapType type)
> +{
> +    QDict *d = qdict_new();
> +    qdict_put(d, "file", qstring_from_str(filename));
> +    qdict_put(d, "granularity-bytes", qint_from_int(granularity));
> +    switch (type) {
> +    case QBM_TYPE_DIRTY:
> +        qdict_put(d, "type", qstring_from_str("dirty"));
> +        break;
> +    case QBM_TYPE_ALLOC:
> +        qdict_put(d, "type", qstring_from_str("allocation"));
> +        break;
> +    default:
> +        abort();
> +    }
> +    return d;
> +}
> +
> +static QDict *qbm_create_dirty_bitmaps(BlockDriverState *bs,
> +                                       uint64_t image_size,
> +                                       int granularity,
> +                                       int n, Error **errp)
> +{
> +    int i;
> +    QDict *dict = qdict_new();
> +    int64_t bitmap_size = DIV_ROUND_UP(image_size, granularity * BITS_PER_BYTE);
> +
> +    for (i = 0; i < n; i++) {
> +        char *bitmap_filename;
> +        char *key = g_strdup_printf("dirty.%d", i);
> +
> +        bitmap_filename = qbm_create_file(bs, key, "bitmap", bitmap_size,
> +                                          errp);
> +        if (!bitmap_filename) {
> +            g_free(key);
> +            QDECREF(dict);
> +            dict = NULL;
> +            goto out;
> +        }
> +        qdict_put(dict, key,
> +                  qbm_make_bitmap_dict(bitmap_filename, granularity,
> +                                       QBM_TYPE_DIRTY));
> +        g_free(key);
> +    }
> +out:
> +    return dict;
> +}
> +
> +static QDict *qbm_create_allocation(BlockDriverState *bs,
> +                                    uint64_t image_size,
> +                                    int granularity,
> +                                    const char *backing_file,
> +                                    const char *format,
> +                                    Error **errp)
> +{
> +    char *bitmap_filename;
> +    QDict *ret, *backing;
> +    int64_t bitmap_size = DIV_ROUND_UP(image_size, granularity * BITS_PER_BYTE);
> +
> +    bitmap_filename = qbm_create_file(bs, "allocation", "bitmap",
> +                                      bitmap_size,
> +                                      errp);
> +    if (!bitmap_filename) {
> +        return NULL;
> +    }
> +
> +    ret = qdict_new();
> +
> +    qdict_put(ret, "file", qstring_from_str(bitmap_filename));
> +    if (format) {
> +        qdict_put(ret, "format", qstring_from_str(format));
> +    }
> +    qdict_put(ret, "type", qstring_from_str("allocation"));
> +    qdict_put(ret, "granularity-bytes", qint_from_int(granularity));
> +
> +    backing = qbm_create_image_dict(bs, backing_file, format, errp);
> +    if (!backing) {
> +        QDECREF(ret);
> +        ret = NULL;
> +        goto out;
> +    }
> +    qdict_put(ret, "backing", backing);
> +
> +out:
> +    g_free(bitmap_filename);
> +    return ret;
> +}
> +
> +static int qbm_create(const char *filename, QemuOpts *opts, Error **errp)
> +{
> +    char *backing_file;
> +    const char *image_filename;
> +    int granularity, dirty_bitmaps;
> +    int64_t image_size;
> +    int ret;
> +    QDict *dict = NULL, *bitmaps, *image;
> +    BlockDriverState *bs = NULL, *image_bs = NULL;
> +    char fullname[PATH_MAX];
> +
> +    ret = bdrv_create_file(filename, NULL, errp);
> +    if (ret) {
> +        return ret;
> +    }
> +    ret = bdrv_open(&bs, filename, NULL, NULL,
> +                    BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    image_filename = qemu_opt_get_del(opts, "image");
> +    if (!image_filename) {
> +        /* Try to create one */
> +        int64_t size = qemu_opt_get_size_del(opts, "size", -1);
> +        if (size == -1) {
> +            error_setg(errp, "Invalid size specified for data image");
> +            ret = -EINVAL;
> +            goto out;
> +        }
> +        image_filename = qbm_create_file(bs, "data", "img", size, errp);
> +        if (!image_filename) {
> +            ret = -EIO;
> +            goto out;
> +        }
> +    }
> +
> +    granularity = qemu_opt_get_number(opts, "granularity", 65536);
> +    dirty_bitmaps = qemu_opt_get_number(opts, "dirty-bitmaps", 0);
> +
> +    qbm_get_fullname(bs, fullname, sizeof(fullname), image_filename);
> +    ret = bdrv_open(&image_bs, fullname, NULL, NULL, 0, errp);
> +    if (ret) {
> +        goto out;
> +    }
> +    image_size = bdrv_getlength(image_bs);
> +
> +    dict = qdict_new();
> +    bitmaps = qbm_create_dirty_bitmaps(bs, image_size, granularity,
> +                                       dirty_bitmaps, errp);
> +    image = qbm_create_image_dict(bs, image_filename,
> +                                  bdrv_get_format_name(image_bs), errp);
> +    if (!image) {
> +        goto out;
> +    }
> +
> +    qdict_put(dict, "version", qint_from_int(1));
> +    qdict_put(dict, "creator", qstring_from_str("QEMU"));
> +    qdict_put(dict, "bitmaps", bitmaps);
> +    qdict_put(dict, "image", image);
> +
> +    backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
> +    if (backing_file) {
> +        char *backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
> +        QDict *alloc = qbm_create_allocation(bs, image_size,
> +                                             granularity, backing_file,
> +                                             backing_fmt, errp);
> +        if (!alloc) {
> +            ret = -EIO;
> +            goto out;
> +        }
> +        /* Create "allocation" bitmap. */
> +        qdict_put(bitmaps, "allocation", alloc);
> +        g_free(backing_file);
> +        backing_file = NULL;
> +        g_free(backing_fmt);
> +    }
> +
> +    ret = qbm_save_desc(bs, dict);
> +
> +out:
> +    bdrv_unref(image_bs);
> +    bdrv_unref(bs);
> +    QDECREF(dict);
> +    return ret;
> +}
> +
> +static int64_t qbm_getlength(BlockDriverState *bs)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    return bdrv_getlength(s->image->bs);
> +}
> +
> +static void qbm_close(BlockDriverState *bs)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +
> +    qbm_release_bitmaps(bs);
> +    bdrv_unref(s->image->bs);
> +    g_free(s->bitmaps);
> +    QDECREF(s->desc);
> +}
> +
> +static int qbm_truncate(BlockDriverState *bs, int64_t offset)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    /* Truncate the image only, the bitmaps's sizes will be made correct when
> +     * saving. */
> +    return bdrv_truncate(s->image->bs, offset);
> +}
> +
> +static coroutine_fn int qbm_co_flush(BlockDriverState *bs)
> +{
> +    int ret;
> +    int i;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    ret = bdrv_flush(s->image->bs);
> +    for (i = 0; ret >= 0 && i < s->num_bitmaps; i++) {
> +        ret = bdrv_flush(s->bitmaps[i].file->bs);
> +    }
> +    return ret;
> +}
> +
> +static int qbm_change_backing_file(BlockDriverState *bs,
> +                                   const char *backing_file,
> +                                   const char *backing_fmt)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    if (!s->backing_dict) {
> +        return -ENOTSUP;
> +    }
> +    if (backing_file) {
> +        qdict_put(s->backing_dict, "file", qstring_from_str(backing_file));
> +        qdict_put(s->backing_dict, "format",
> +                  qstring_from_str(backing_fmt ? : ""));
> +    } else {
> +        int i;
> +        QDict *bitmaps = qdict_get_qdict(s->desc, "bitmaps");
> +
> +        assert(bitmaps);
> +        if (!qdict_haskey(bitmaps, "allocation")) {
> +            return 0;
> +        }
> +        qdict_del(bitmaps, "allocation");
> +        for (i = 0; i < s->num_bitmaps; i++) {
> +            if (s->bitmaps[i].type == QBM_TYPE_ALLOC) {
> +                qbm_release_bitmap(bs, &s->bitmaps[i]);
> +                s->bitmaps[i] = s->bitmaps[--s->num_bitmaps];
> +                break;
> +            }
> +        }
> +        s->alloc_bitmap = NULL;
> +        s->backing_dict = NULL;
> +    }
> +    return qbm_save_desc(bs->file->bs, s->desc);
> +}
> +
> +static int64_t qbm_get_allocated_file_size(BlockDriverState *bs)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    /* Take the file sizes of descriptor and bitmap files into account? */
> +    return bdrv_get_allocated_file_size(s->image->bs);
> +}
> +
> +static int qbm_has_zero_init(BlockDriverState *bs)
> +{
> +    return 1;
> +}
> +
> +static int qbm_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +
> +    bdi->unallocated_blocks_are_zero = true;
> +    bdi->can_write_zeroes_with_unmap = true;
> +    if (s->alloc_bitmap) {
> +        bdi->cluster_size = bdrv_dirty_bitmap_granularity(s->alloc_bitmap);
> +    } else {
> +        bdi->cluster_size = bdrv_get_cluster_size(s->image->bs);
> +    }
> +    return 0;
> +}
> +
> +static int qbm_check(BlockDriverState *bs, BdrvCheckResult *result,
> +                     BdrvCheckMode fix)
> +{
> +    /* TODO: checksum verification and bitmap size checks? */
> +    return 0;
> +}
> +
> +static void qbm_detach_aio_context(BlockDriverState *bs)
> +{
> +    int i;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    bdrv_detach_aio_context(s->image->bs);
> +    for (i = 0; i < s->num_bitmaps; i++) {
> +        bdrv_detach_aio_context(s->bitmaps[i].file->bs);
> +    }
> +}
> +
> +static void qbm_attach_aio_context(BlockDriverState *bs,
> +                                   AioContext *new_context)
> +{
> +    int i;
> +    BDRVQBMState *s = bs->opaque;
> +
> +    bdrv_attach_aio_context(s->image->bs, new_context);
> +    for (i = 0; i < s->num_bitmaps; i++) {
> +        bdrv_attach_aio_context(s->bitmaps[i].file->bs, new_context);
> +    }
> +}
> +
> +static int qbm_bitmap_set_persistent(BlockDriverState *bs,
> +                                     BdrvDirtyBitmap *bitmap,
> +                                     bool persistent, Error **errp)
> +{
> +    BDRVQBMState *s = bs->opaque;
> +    int ret = 0;
> +    QBMBitmap *bm;
> +    char *filename;
> +    const char *name = bdrv_dirty_bitmap_name(bitmap);
> +    int granularity = bdrv_dirty_bitmap_granularity(bitmap);
> +    QDict *bitmaps = qdict_get_qdict(s->desc, "bitmaps");
> +
> +    if (persistent) {
> +        filename = qbm_create_file(bs, name, "bin",
> +                                   bdrv_dirty_bitmap_size(bitmap), errp);
> +        if (!filename) {
> +            return -EIO;
> +        }
> +
> +        bm = qbm_open_bitmap(bs, name, filename, granularity,
> +                             QBM_TYPE_DIRTY, bitmap, errp);
> +        if (!bm) {
> +            ret = -EIO;
> +        }
> +        qdict_put(bitmaps, name, qbm_make_bitmap_dict(filename, granularity,
> +                                                      QBM_TYPE_DIRTY));
> +        g_free(filename);
> +    } else {
> +        if (!qdict_haskey(bitmaps, name)) {
> +            error_setg(errp, "No persistent bitmap with name '%s'", name);
> +            return -ENOENT;
> +        }
> +        qdict_del(bitmaps, name);
> +    }
> +    ret = qbm_save_desc(bs->file->bs, s->desc);
> +    if (ret) {
> +        error_setg(errp, "Failed to save json description to file");
> +    }
> +    return ret;
> +}
> +
> +static QemuOptsList qbm_create_opts = {
> +    .name = "qbm-create-opts",
> +    .head = QTAILQ_HEAD_INITIALIZER(qbm_create_opts.head),
> +    .desc = {
> +        {
> +            .name = BLOCK_OPT_SIZE,
> +            .type = QEMU_OPT_SIZE,
> +            .help = "Virtual disk size"
> +        },
> +        {
> +            .name = "image",
> +            .type = QEMU_OPT_STRING,
> +            .help = "The file name of the referenced image, if not specified, "
> +                    "one will be created automatically",
> +        },
> +        {
> +            .name = BLOCK_OPT_BACKING_FILE,
> +            .type = QEMU_OPT_STRING,
> +            .help = "File name of a base image"
> +        },
> +        {
> +            .name = BLOCK_OPT_BACKING_FMT,
> +            .type = QEMU_OPT_STRING,
> +            .help = "Image format of the base image"
> +        },
> +        {
> +            .name = "granularity",
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "Bitmap granularity in bytes"
> +        },
> +        {
> +            .name = "dirty-bitmaps",
> +            .type = QEMU_OPT_NUMBER,
> +            .help = "The number of dirty bitmaps to create"
> +        },
> +        { /* end of list */ }
> +    }
> +};
> +
> +static BlockDriver bdrv_qbm = {
> +    .format_name                  = "qbm",
> +    .protocol_name                = "qbm",
> +    .instance_size                = sizeof(BDRVQBMState),
> +    .bdrv_probe                   = qbm_probe,
> +    .bdrv_open                    = qbm_open,
> +    .bdrv_reopen_prepare          = qbm_reopen_prepare,
> +    .bdrv_co_readv                = qbm_co_readv,
> +    .bdrv_co_writev               = qbm_co_writev,
> +    .bdrv_co_write_zeroes         = qbm_co_write_zeroes,
> +    .bdrv_co_discard              = qbm_co_discard,
> +    .bdrv_make_empty              = qbm_make_empty,
> +    .bdrv_close                   = qbm_close,
> +    .bdrv_getlength               = qbm_getlength,
> +    .bdrv_create                  = qbm_create,
> +    .bdrv_co_flush_to_disk        = qbm_co_flush,
> +    .bdrv_truncate                = qbm_truncate,
> +    .bdrv_co_get_block_status     = qbm_co_get_block_status,
> +    .bdrv_get_allocated_file_size = qbm_get_allocated_file_size,
> +    .bdrv_has_zero_init           = qbm_has_zero_init,
> +    .bdrv_refresh_limits          = qbm_refresh_limits,
> +    .bdrv_get_info                = qbm_get_info,
> +    .bdrv_check                   = qbm_check,
> +    .bdrv_detach_aio_context      = qbm_detach_aio_context,
> +    .bdrv_attach_aio_context      = qbm_attach_aio_context,
> +    .bdrv_dirty_bitmap_set_persistent
> +                                  = qbm_bitmap_set_persistent,
> +    .bdrv_change_backing_file     = qbm_change_backing_file,
> +    .supports_backing             = true,
> +    .create_opts                  = &qbm_create_opts,
> +};
> +
> +static void bdrv_qbm_init(void)
> +{
> +    bdrv_register(&bdrv_qbm);
> +}
> +
> +block_init(bdrv_qbm_init);


-- 
Best regards,
Vladimir

  reply	other threads:[~2016-02-17 13:31 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-26 10:38 [Qemu-devel] [RFC PATCH 00/16] Qemu Bit Map (QBM) - an overlay format for persistent dirty bitmap Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 01/16] doc: Add QBM format specification Fam Zheng
2016-01-26 17:51   ` Eric Blake
2016-02-09  0:05     ` John Snow
2016-02-23  8:35     ` Markus Armbruster
2016-02-08 23:51   ` John Snow
2016-02-17 11:48   ` Vladimir Sementsov-Ogievskiy
2016-02-17 16:30   ` Vladimir Sementsov-Ogievskiy
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 02/16] block: Set dirty before doing write Fam Zheng
2016-01-26 17:52   ` Eric Blake
2016-02-09  0:11   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 03/16] block: Allow .bdrv_close callback to release dirty bitmaps Fam Zheng
2016-01-26 17:53   ` Eric Blake
2016-02-09  0:23   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 04/16] block: Move filename_decompose to block.c Fam Zheng
2016-01-27 16:07   ` Eric Blake
2016-02-09 20:56   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 05/16] block: Make bdrv_get_cluster_size public Fam Zheng
2016-01-27 16:08   ` Eric Blake
2016-02-09 21:06   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 06/16] block: Introduce bdrv_dirty_bitmap_set_persistent Fam Zheng
2016-02-09 21:31   ` John Snow
2016-02-09 22:04   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 07/16] block: Only swap non-persistent dirty bitmaps Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 08/16] qmp: Add optional parameter "persistent" in block-dirty-bitmap-add Fam Zheng
2016-02-09 22:05   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 09/16] qmp: Add block-dirty-bitmap-set-persistent Fam Zheng
2016-02-09 22:49   ` John Snow
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 10/16] qbm: Implement format driver Fam Zheng
2016-02-17 13:30   ` Vladimir Sementsov-Ogievskiy [this message]
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 11/16] qapi: Add "qbm" as a generic cow " Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 12/16] iotests: Add qbm format to 041 Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 13/16] iotests: Add qbm to case 097 Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 14/16] iotests: Add qbm to applicable test cases Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 15/16] iotests: Add qbm specific test case 140 Fam Zheng
2016-01-26 10:38 ` [Qemu-devel] [RFC PATCH 16/16] iotests: Add persistent bitmap test case 141 Fam Zheng
2016-02-22 14:24 ` [Qemu-devel] [RFC PATCH 00/16] Qemu Bit Map (QBM) - an overlay format for persistent dirty bitmap Kevin Wolf
2016-02-23  3:40   ` Fam Zheng
2016-02-23 17:43     ` Kevin Wolf
2016-02-24  0:49       ` Fam Zheng
2016-02-23  9:14   ` Markus Armbruster
2016-02-23 11:28     ` Kevin Wolf

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=56C47600.20806@virtuozzo.com \
    --to=vsementsov@virtuozzo.com \
    --cc=armbru@redhat.com \
    --cc=famz@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    --cc=vsementsov@parallels.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).