From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: John Snow <jsnow@redhat.com>, qemu-devel@nongnu.org
Cc: kwolf@redhat.com, pbonzini@redhat.com, stefanha@redhat.com,
den@openvz.org
Subject: Re: [Qemu-devel] [PATCH 06/17] qcow2-dirty-bitmap: add qcow2_dirty_bitmap_load()
Date: Tue, 16 Feb 2016 22:04:45 +0300 [thread overview]
Message-ID: <56C372CD.7010502@virtuozzo.com> (raw)
In-Reply-To: <561452E5.1090005@redhat.com>
On 07.10.2015 02:01, John Snow wrote:
>
> On 09/05/2015 12:43 PM, Vladimir Sementsov-Ogievskiy wrote:
>> This function loads block dirty bitmap from qcow2.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>> block/qcow2-dirty-bitmap.c | 155 +++++++++++++++++++++++++++++++++++++++++++++
>> block/qcow2.c | 2 +
>> block/qcow2.h | 5 ++
>> include/block/block_int.h | 5 ++
>> 4 files changed, 167 insertions(+)
>>
>> diff --git a/block/qcow2-dirty-bitmap.c b/block/qcow2-dirty-bitmap.c
>> index 1260d1d..ea50137 100644
>> --- a/block/qcow2-dirty-bitmap.c
>> +++ b/block/qcow2-dirty-bitmap.c
>> @@ -99,6 +99,13 @@ static int check_constraints(int cluster_size,
>> return fail ? -EINVAL : 0;
>> }
>>
>> +static QCowDirtyBitmapHeader *bitmap_header(BDRVQcowState *s,
>> + QCowDirtyBitmap *bitmap)
>> +{
> BDRVQcow2State here and everywhere below, again.
>
>> + return (QCowDirtyBitmapHeader *)
>> + (s->dirty_bitmap_directory + bitmap->offset);
>> +}
>> +
>> static int directory_read(BlockDriverState *bs)
>> {
>> int ret;
>> @@ -195,3 +202,151 @@ out:
>> }
>> return ret;
>> }
>> +
>> +static QCowDirtyBitmap *find_dirty_bitmap_by_name(BlockDriverState *bs,
>> + const char *name)
>> +{
>> + BDRVQcowState *s = bs->opaque;
>> + QCowDirtyBitmap *bm, *end = s->dirty_bitmaps + s->nb_dirty_bitmaps;
>> +
>> + for (bm = s->dirty_bitmaps; bm < end; ++bm) {
>> + if (strcmp(bm->name, name) == 0) {
>> + return bm;
>> + }
>> + }
>> +
>> + return NULL;
>> +}
>> +
> Whoops. This says to me we really need to prohibit bitmaps with the same
> name from being stored in the same file, and mention this in the spec,
> and test for it on load.
>
> Perhaps we can create a hash-table and fail verification on open if
> there's a collision. We can then use that hash-table here for
> find_dirty_bitmap_by_name to speed up lookup since we already went
> through the trouble of loading it.
>
> Might help for large cases where we're approaching 64K bitmaps, will not
> be too big of a performance hit for casual use.
So, it (hash table approach) may be implemented later
>
>> +/* dirty sectors in cluster is a number of sectors in the image, corresponding
>> + * to one cluster of bitmap data */
>> +static uint64_t dirty_sectors_in_cluster(const BDRVQcowState *s,
>> + const BdrvDirtyBitmap *bitmap)
>> +{
>> + uint32_t sector_granularity =
>> + bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS;
>> +
>> + return (uint64_t)sector_granularity * (s->cluster_size << 3);
>> +}
>> +
>> +/* load_bitmap()
>> + * load dirty bitmap from Dirty Bitmap Table
>> + * Dirty Bitmap Table entries are assumed to be in big endian format */
>> +static int load_bitmap(BlockDriverState *bs,
>> + const uint64_t *dirty_bitmap_table,
>> + uint32_t dirty_bitmap_table_size,
>> + BdrvDirtyBitmap *bitmap)
>> +{
>> + int ret = 0;
>> + BDRVQcowState *s = bs->opaque;
>> + uint64_t sector, dsc;
>> + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
> I found some of this hard to unwind, bear with me:
>
> AKA, the number of sectors that bitmap tracks ...
>
>> + int cl_size = s->cluster_size;
>> + uint8_t *buf = NULL;
>> + uint32_t i, tab_size =
>> + size_to_clusters(s, bdrv_dirty_bitmap_data_size(bitmap, bm_size));
>> +
> bdrv_dirty_bitmap_data_size(bitmap, COUNT) calculates for us how much
> actual real size the lowest level of the hbitmap actually takes.
>
> Then size_to_clusters tells us how many clusters we need to store that,
> and therefore should map back to be the same as the predicted value,
> dirty_bitmap_table_size.
>
>> + if (tab_size > dirty_bitmap_table_size) {
>> + return -EINVAL;
>> + }
>> +
> I assume this is not == because the real table size might have padding
> or other such things, but if the calculated tab size is bigger than the
> actual then we have a problem.
>
> But I think that you've passed in "birty_ditmap_table_size" as the total
> byte count of the table, but "tab_size" is computed here as the number
> of entries. I think you should multiply tab_size by uint64_t and test if
> they're equal.
>
>> + bdrv_clear_dirty_bitmap(bitmap);
>> +
> Clear takes the aio_context for the associated BDS and then releases it...
>
>> + buf = g_malloc0(cl_size);
>> + dsc = dirty_sectors_in_cluster(s, bitmap);
>> + for (i = 0, sector = 0; i < tab_size; ++i, sector += dsc) {
>> + uint64_t end = MIN(bm_size, sector + dsc);
>> + uint64_t offset = be64_to_cpu(dirty_bitmap_table[i]);
>> +
>> + if (offset & DBM_TABLE_ENTRY_RESERVED_MASK) {
>> + ret = -EINVAL;
>> + goto finish;
>> + }
>> +
>> + /* zero offset means cluster unallocated */
>> + if (offset) {
>> + ret = bdrv_pread(bs->file, offset, buf, cl_size);
>> + if (ret < 0) {
>> + goto finish;
>> + }
>> + bdrv_dirty_bitmap_deserialize_part(bitmap, buf, sector, end);
> ...but at this point, I believe we're editing this bitmap without its
> associated lock, which might be a problem when we go to add QMP commands
> later.
in the next version load_bitmap is changed a lot, and now not using
part-serialization.
>> + }
>> + }
>> + ret = 0;
>> +
>> + bdrv_dirty_bitmap_deserialize_finish(bitmap);
>> +
>> +finish:
>> + g_free(buf);
>> +
>> + return ret;
>> +}
>> +
>> +BdrvDirtyBitmap * qcow2_dirty_bitmap_load(BlockDriverState *bs_for,
>> + BlockDriverState *bs_file,
>> + const char *name,
>> + Error **errp)
>> +{
>> + BDRVQcowState *s = bs_file->opaque;
>> + int ret;
>> + QCowDirtyBitmap *bm;
>> + QCowDirtyBitmapHeader *bmh;
>> + uint64_t *dirty_bitmap_table = NULL;
>> + uint32_t granularity;
>> + uint64_t size = bdrv_nb_sectors(bs_for);
>> + BdrvDirtyBitmap *bitmap = NULL;
>> +
>> + bm = find_dirty_bitmap_by_name(bs_file, name);
>> + if (bm == NULL) {
>> + error_setg(errp, "Could not find bitmap '%s' in the node '%s'", name,
>> + bdrv_get_device_or_node_name(bs_file));
>> + return NULL;
>> + }
>> + bmh = bitmap_header(s, bm);
>> +
>> + if (size != bmh->nb_virtual_bits) {
>> + error_setg(errp,
>> + "Bitmap '%s' in the node '%s' has size = %" PRIu64
>> + "when requested size (for node %s) = %" PRIu64,
>> + name, bdrv_get_device_or_node_name(bs_file),
>> + bmh->nb_virtual_bits,
>> + bdrv_get_device_or_node_name(bs_for), size);
>> + return NULL;
>> + }
>> +
>> +
>> + dirty_bitmap_table = g_try_malloc(bmh->dirty_bitmap_table_size * sizeof(uint64_t));
>> + if (dirty_bitmap_table == NULL) {
>> + error_setg_errno(errp, -ENOMEM, "Could not allocate Dirty Bitmap Table");
>> + return NULL;
>> + }
>> +
>> + ret = bdrv_pread(bs_file->file, bmh->dirty_bitmap_table_offset, dirty_bitmap_table,
>> + bmh->dirty_bitmap_table_size * sizeof(uint64_t));
>> + if (ret < 0) {
>> + error_setg_errno(errp, -ret, "Could not read dirty_bitmap_table table from image");
>> + goto finish;
>> + }
>> +
>> + granularity = BDRV_SECTOR_SIZE << bmh->granularity_bits;
>> + bitmap = bdrv_create_dirty_bitmap(bs_for, granularity, name, errp);
>> + if (bitmap == NULL) {
>> + error_setg_errno(errp, -ENOMEM, "Could not create dirty bitmap");
> why -ENOMEM? create can fail for a number of reasons ... since we've
> been given an errp parameter in this function, and we can trust
> bdrv_create_dirty_bitmap to have set it, we can just return NULL here
> and the caller can check errp to see what went wrong.
>
>> + goto finish;
>> + }
>> +
> Do we need to mark this bitmap as temporarily unusable until we complete
> the load? I guess not in the context of bdrv_open at boot time ...
Also, keep in mind that now we have only bitmaps, related to the same
bds which contains it.
>
>> + ret = load_bitmap(bs_file, dirty_bitmap_table, bmh->dirty_bitmap_table_size, bitmap);
>> + if (ret < 0) {
>> + error_setg_errno(errp, -ret, "Could not read bitmap from image");
>> + goto finish;
>> + }
>> +
>> +finish:
>> + if (*errp != NULL) {
>> + bdrv_release_dirty_bitmap(bs_for, bitmap);
>> + bitmap = NULL;
>> + }
>> + g_free(dirty_bitmap_table);
>
> I think we're not supposed to be reaching into errp to check its
> implementation detail like this ... the usual paradigm I see is just
> "goto fail" or similar statements instead of checking for
> error-or-success in a shared return block.
>
> finish:
> g_free(dirty_bitmap_table);
> return bitmap;
> fail:
> g_free(dirty_bitmap_table);
> bdrv_release_dirty_bitmap(bs_for, bitmap);
> return NULL;
ok
>
>> +
>> + return bitmap;
>> +}
>> diff --git a/block/qcow2.c b/block/qcow2.c
>> index 76c331b..58ebdd3 100644
>> --- a/block/qcow2.c
>> +++ b/block/qcow2.c
>> @@ -2965,6 +2965,8 @@ BlockDriver bdrv_qcow2 = {
>> .bdrv_get_info = qcow2_get_info,
>> .bdrv_get_specific_info = qcow2_get_specific_info,
>>
>> + .bdrv_dirty_bitmap_load = qcow2_dirty_bitmap_load,
>> +
>> .bdrv_save_vmstate = qcow2_save_vmstate,
>> .bdrv_load_vmstate = qcow2_load_vmstate,
>>
>> diff --git a/block/qcow2.h b/block/qcow2.h
>> index 5016fa1..51d1907 100644
>> --- a/block/qcow2.h
>> +++ b/block/qcow2.h
>> @@ -608,6 +608,11 @@ int qcow2_read_snapshots(BlockDriverState *bs);
>> void qcow2_free_dirty_bitmaps(BlockDriverState *bs);
>> int qcow2_read_dirty_bitmaps(BlockDriverState *bs);
>>
>> +BdrvDirtyBitmap *qcow2_dirty_bitmap_load(BlockDriverState *bs_for,
>> + BlockDriverState *bs_file,
>> + const char *name,
>> + Error **errp);
>> +
>> /* qcow2-cache.c functions */
>> Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables);
>> int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
>> diff --git a/include/block/block_int.h b/include/block/block_int.h
>> index 14ad4c3..f982adc 100644
>> --- a/include/block/block_int.h
>> +++ b/include/block/block_int.h
>> @@ -204,6 +204,11 @@ struct BlockDriver {
>> int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
>> ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
>>
>> + BdrvDirtyBitmap *(*bdrv_dirty_bitmap_load)(BlockDriverState *bs_for,
>> + BlockDriverState *bs_file,
>> + const char *name,
>> + Error **errp);
>> +
>> int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov,
>> int64_t pos);
>> int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
>>
> Looking good, thanks!
> --js
--
Best regards,
Vladimir
next prev parent reply other threads:[~2016-02-16 19:05 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] [PATCH " 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
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 [this message]
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=56C372CD.7010502@virtuozzo.com \
--to=vsementsov@virtuozzo.com \
--cc=den@openvz.org \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=pbonzini@redhat.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.