make dirty-bitmap byte-based
v9 cover make dirty-bitmap byte-based
There are patches floating around to add NBD_CMD_BLOCK_STATUS, but NBD wants to report status on byte granularity (even if the reporting will probably be naturally aligned to sectors or even much higher levels). I've therefore started the task of converting our block status code to report at a byte granularity rather than sectors.
Now that 2.11 is open, I'm rebasing/reposting the remaining patches.
The overall conversion currently looks like:
part 1: bdrv_is_allocated (merged in 2.10, commit 51b0a488) part 2: dirty-bitmap (this series, v8 was here [1])
part 3: bdrv_get_block_status (v4 is posted [2] and is mostly reviewed) part 4: .bdrv_co_block_status (v3 is posted [3], but needs review)
Available as a tag at:
git fetch git://repo.or.cz/qemu/ericb.git nbd-byte-dirty-v8
[1] https://lists.gnu.org/archive/html/qemu-devel/2017-09/msg04733.html
[2] https://lists.gnu.org/archive/html/qemu-devel/2017-09/msg03543.html
[3] https://lists.gnu.org/archive/html/qemu-devel/2017-09/msg03812.html
Since v8:
- add R-b where appropriate
- patch 5 simplified again [Fam]
- patch 18: add a comment, and enhance iotests 165 to test that aspect [Kevin]
001/20:[----] [--] 'block: Make bdrv_img_create() size selection easier to read' 002/20:[----] [--] 'hbitmap: Rename serialization_granularity to serialization_align' 003/20:[----] [--] 'qcow2: Ensure bitmap serialization is aligned' 004/20:[----] [--] 'dirty-bitmap: Drop unused functions'
005/20:[0004] [FC] 'dirty-bitmap: Avoid size query failure during truncate' 006/20:[----] [--] 'dirty-bitmap: Change bdrv_dirty_bitmap_size() to report bytes' 007/20:[----] [--] 'dirty-bitmap: Track bitmap size by bytes' 008/20:[----] [--] 'dirty-bitmap: Change bdrv_dirty_bitmap_*serialize*() to take bytes' 009/20:[----] [--] 'qcow2: Switch sectors_covered_by_bitmap_cluster() to byte-based' 010/20:[----] [--] 'dirty-bitmap: Set iterator start by offset, not sector' 011/20:[----] [--] 'dirty-bitmap: Change bdrv_dirty_iter_next() to report byte offset' 012/20:[----] [--] 'dirty-bitmap: Change bdrv_get_dirty_count() to report bytes' 013/20:[----] [--] 'dirty-bitmap: Change bdrv_get_dirty_locked() to take bytes' 014/20:[----] [--] 'dirty-bitmap: Change bdrv_[re]set_dirty_bitmap() to use bytes' 015/20:[----] [--] 'mirror: Switch mirror_dirty_init() to byte-based iteration' 016/20:[----] [--] 'qcow2: Switch qcow2_measure() to byte-based iteration' 017/20:[----] [--] 'qcow2: Switch load_bitmap_data() to byte-based iteration' 018/20:[0007] [FC] 'qcow2: Switch store_bitmap_data() to byte-based iteration' 019/20:[----] [--] 'dirty-bitmap: Switch bdrv_set_dirty() to bytes' 020/20:[----] [--] 'dirty-bitmap: Convert internal hbitmap size/granularity'
Eric Blake (20):
block: Make bdrv_img_create() size selection easier to read
hbitmap: Rename serialization_granularity to serialization_align
qcow2: Ensure bitmap serialization is aligned
dirty-bitmap: Drop unused functions
dirty-bitmap: Avoid size query failure during truncate
dirty-bitmap: Change bdrv_dirty_bitmap_size() to report bytes
dirty-bitmap: Track bitmap size by bytes
dirty-bitmap: Change bdrv_dirty_bitmap_*serialize*() to take bytes
qcow2: Switch sectors_covered_by_bitmap_cluster() to byte-based
dirty-bitmap: Set iterator start by offset, not sector
dirty-bitmap: Change bdrv_dirty_iter_next() to report byte offset
dirty-bitmap: Change bdrv_get_dirty_count() to report bytes
dirty-bitmap: Change bdrv_get_dirty_locked() to take bytes
dirty-bitmap: Change bdrv_[re]set_dirty_bitmap() to use bytes
mirror: Switch mirror_dirty_init() to byte-based iteration
qcow2: Switch qcow2_measure() to byte-based iteration
qcow2: Switch load_bitmap_data() to byte-based iteration
qcow2: Switch store_bitmap_data() to byte-based iteration
dirty-bitmap: Switch bdrv_set_dirty() to bytes
dirty-bitmap: Convert internal hbitmap size/granularity
include/block/block_int.h | 2 +- include/block/dirty-bitmap.h | 43 ++++++-------- include/qemu/hbitmap.h | 8 +-- block/io.c | 6 +- block.c | 17 ++++-- block/backup.c | 7 +-- block/dirty-bitmap.c | 134 ++++++++++++++----------------------------- block/mirror.c | 79 +++++++++++-------------- block/qcow2-bitmap.c | 62 +++++++++++--------- block/qcow2.c | 22 ++++--- migration/block.c | 12 ++-- tests/test-hbitmap.c | 10 ++-- util/hbitmap.c | 8 +-- tests/qemu-iotests/165 | 2 +- 14 files changed, 173 insertions(+), 239 deletions(-)
Signature · 1 line
2.13.5
PATCH v9 1/20 block: Make bdrv_img_create() size selection easier to read
All callers of bdrv_img_create() pass in a size, or -1 to read the size from the backing file. We then set that size as the QemuOpt default, which means we will reuse that default rather than the final parameter to qemu_opt_get_size() several lines later. But it is rather confusing to read subsequent checks of 'size == -1' when it looks (without seeing the full context) like size defaults to 0; it also doesn't help that a size of 0 is valid (for some formats).
Rework the logic to make things more legible.
---
v6: Combine into a series rather than being a standalone patch (more for ease of tracking than for being on topic)
--- block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
block.c +1 -1
@@ -4393,7 +4393,7 @@ void bdrv_img_create(const char *filename, const char *fmt,/* The size for the image must always be specified, unless we have a backing * file and we have not been forbidden from opening it. */ - size =qemu_opt_get_size (opts,BLOCK_OPT_SIZE ,0 ); + size =qemu_opt_get_size (opts,BLOCK_OPT_SIZE , img_size);if (backing_file && !(flags &BDRV_O_NO_BACKING )) {BlockDriverState *bs;char *full_backing =g_new0 (char,PATH_MAX );
Signature · 1 line
2.13.5
PATCH v9 2/20 hbitmap: Rename serialization_granularity to serialization_align
In reply to Eric BlakeThe only client of hbitmap_serialization_granularity() is dirty-bitmap's bdrv_dirty_bitmap_serialization_align(). Keeping the two names consistent is worthwhile, and the shorter name is more representative of what the function returns (the required alignment to be used for start/count of other serialization functions, where violating the alignment causes assertion failures).
---
v5: no change
v4: new patch
--- include/qemu/hbitmap.h | 8 ++++---- block/dirty-bitmap.c | 2 +- tests/test-hbitmap.c | 10 +++++----- util/hbitmap.c | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-)
include/qemu/hbitmap.h +4 -4
@@ -159,16 +159,16 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item);bool hbitmap_is_serializable (const HBitmap *hb);/** -* hbitmap_serialization_granularity: +* hbitmap_serialization_align: * @hb: HBitmap to operate on. * -* Granularity of serialization chunks, used by other serialization functions. -* For every chunk: +* Required alignment of serialization chunks, used by other serialization +* functions. For every chunk: * 1. Chunk start should be aligned to this granularity. * 2. Chunk size should be aligned too, except for last chunk (for which * start + count == hb->size) */ -uint64_t hbitmap_serialization_granularity (const HBitmap *hb); +uint64_t hbitmap_serialization_align (const HBitmap *hb);/** * hbitmap_serialization_size:
block/dirty-bitmap.c +1 -1
@@ -617,7 +617,7 @@ uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,uint64_t bdrv_dirty_bitmap_serialization_align (const BdrvDirtyBitmap *bitmap) { -return hbitmap_serialization_granularity (bitmap->bitmap); +return hbitmap_serialization_align (bitmap->bitmap); } void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
tests/test-hbitmap.c +5 -5
@@ -738,15 +738,15 @@ static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused) } } -static void test_hbitmap_serialize_granularity (TestHBitmapData *data, -const void *unused) +static void test_hbitmap_serialize_align (TestHBitmapData *data, +const void *unused) {int r;hbitmap_test_init (data,L3 *2 ,3 );g_assert (hbitmap_is_serializable (data->hb)); - r =hbitmap_serialization_granularity (data->hb); + r =hbitmap_serialization_align (data->hb); g_assert_cmpint(r, ==, 64 << 3); }
@@ -974,8 +974,8 @@ int main(int argc, char **argv)hbitmap_test_add ("/hbitmap/meta/word" , test_hbitmap_meta_word);hbitmap_test_add ("/hbitmap/meta/sector" , test_hbitmap_meta_sector); -hbitmap_test_add ("/hbitmap/serialize/granularity" , - test_hbitmap_serialize_granularity); +hbitmap_test_add ("/hbitmap/serialize/align" , + test_hbitmap_serialize_align);hbitmap_test_add ("/hbitmap/serialize/basic" , test_hbitmap_serialize_basic); hbitmap_test_add("/hbitmap/serialize/part",
util/hbitmap.c +4 -4
@@ -413,14 +413,14 @@ bool hbitmap_is_serializable(const HBitmap *hb) {/* Every serialized chunk must be aligned to 64 bits so that endianness * requirements can be fulfilled on both 64 bit and 32 bit hosts. -* We have hbitmap_serialization_granularity() which converts this +* We have hbitmap_serialization_align() which converts this * alignment requirement from bitmap bits to items covered (e.g. sectors). * That value is: * 64 << hb->granularity * Since this value must not exceed UINT64_MAX, hb->granularity must be * less than 58 (== 64 - 6, where 6 is ld(64), i.e. 1 << 6 == 64). * -* In order for hbitmap_serialization_granularity() to always return a +* In order for hbitmap_serialization_align() to always return a * meaningful value, bitmaps that are to be serialized must have a * granularity of less than 58. */
@@ -437,7 +437,7 @@ 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) +uint64_t hbitmap_serialization_align (const HBitmap *hb) {assert (hbitmap_is_serializable (hb));
@@ -454,7 +454,7 @@ static void serialization_chunk(const HBitmap *hb, unsigned long **first_el, uint64_t *el_count) { uint64_t last = start + count - 1; -uint64_t gran =hbitmap_serialization_granularity (hb); +uint64_t gran =hbitmap_serialization_align (hb);assert ((start & (gran -1 )) ==0 );assert ((last >> hb->granularity) < hb->size);
Signature · 1 line
2.13.5
PATCH v9 3/20 qcow2: Ensure bitmap serialization is aligned
In reply to Eric BlakeWhen subdividing a bitmap serialization, the code in hbitmap.c enforces that start/count parameters are aligned (except that count can end early at end-of-bitmap). We exposed this required alignment through bdrv_dirty_bitmap_serialization_align(), but forgot to actually check that we comply with it.
Fortunately, qcow2 is never dividing bitmap serialization smaller than one cluster (which is a minimum of 512 bytes); so we are always compliant with the serialization alignment (which insists that we partition at least 64 bits per chunk) because we are doing at least 4k bits per chunk.
Still, it's safer to add an assertion (for the unlikely case that we'd ever support a cluster smaller than 512 bytes, or if the hbitmap implementation changes what it considers to be aligned), rather than leaving bdrv_dirty_bitmap_serialization_align() without a caller.
---
v5: no change
v4: new patch
--- block/qcow2-bitmap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
block/qcow2-bitmap.c +5 -2
@@ -274,10 +274,13 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)static uint64_t sectors_covered_by_bitmap_cluster (const BDRVQcow2State *s,const BdrvDirtyBitmap *bitmap) { -uint32_t sector_granularity = +uint64_t sector_granularity =bdrv_dirty_bitmap_granularity (bitmap) >>BDRV_SECTOR_BITS ; +uint64_t sbc = sector_granularity * (s->cluster_size <<3 ); -return (uint64_t )sector_granularity * (s->cluster_size <<3 ); +assert (QEMU_IS_ALIGNED (sbc, +bdrv_dirty_bitmap_serialization_align (bitmap))); +return sbc; }/* load_bitmap_data
Signature · 1 line
2.13.5
PATCH v9 4/20 dirty-bitmap: Drop unused functions
In reply to Eric BlakeWe had several functions that no one is currently using, and which use sector-based interfaces. I'm trying to convert towards byte-based interfaces, so it's easier to just drop the unused functions:
bdrv_dirty_bitmap_get_metabdrv_dirty_bitmap_get_meta_lockedbdrv_dirty_bitmap_reset_metabdrv_dirty_bitmap_meta_granularity
---
v5: no change
v4: rebase to Vladimir's persistent bitmaps (bdrv_dirty_bitmap_size now in use), dropped R-b
v3: rebase to upstream changes (bdrv_dirty_bitmap_get_meta_locked was added in b64bd51e with no clients), kept R-b
v2: tweak commit message based on review, no code change
--- include/block/dirty-bitmap.h | 10 ---------- block/dirty-bitmap.c | 44 -------------------------------------------- 2 files changed, 54 deletions(-)
include/block/dirty-bitmap.h +0 -10
@@ -34,7 +34,6 @@ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap);BlockDirtyInfoList *bdrv_query_dirty_bitmaps (BlockDriverState *bs);uint32_t bdrv_get_default_bitmap_granularity (BlockDriverState *bs);uint32_t bdrv_dirty_bitmap_granularity (const BdrvDirtyBitmap *bitmap); -uint32_t bdrv_dirty_bitmap_meta_granularity (BdrvDirtyBitmap *bitmap);bool bdrv_dirty_bitmap_enabled (BdrvDirtyBitmap *bitmap);bool bdrv_dirty_bitmap_frozen (BdrvDirtyBitmap *bitmap);const char *bdrv_dirty_bitmap_name (const BdrvDirtyBitmap *bitmap);
@@ -44,15 +43,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, int64_t cur_sector, int64_t nr_sectors);void bdrv_reset_dirty_bitmap (BdrvDirtyBitmap *bitmap,int64_t cur_sector,int64_t nr_sectors); -int bdrv_dirty_bitmap_get_meta (BlockDriverState *bs, -BdrvDirtyBitmap *bitmap,int64_t sector, -int nb_sectors); -int bdrv_dirty_bitmap_get_meta_locked (BlockDriverState *bs, -BdrvDirtyBitmap *bitmap,int64_t sector, -int nb_sectors); -void bdrv_dirty_bitmap_reset_meta (BlockDriverState *bs, -BdrvDirtyBitmap *bitmap,int64_t sector, -int nb_sectors);BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new (BdrvDirtyBitmap *bitmap);BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap,uint64_t first_sector);
block/dirty-bitmap.c +0 -44
56 lines not on this page · raw message
Signature · 1 line
2.13.5
PATCH v9 5/20 dirty-bitmap: Avoid size query failure during truncate
In reply to Eric BlakeWe've previously fixed several places where we failed to account for possible errors from bdrv_nb_sectors(). Fix another one by making bdrv_dirty_bitmap_truncate() take the new size from the caller instead of querying itself; then adjust the sole caller bdrv_truncate() to pass the size just determined by a successful resize, or to skip the bitmap resize on failure, thus avoiding sizing the bitmaps to -1.
---
v9: skip only bdrv_dirty_bitmap_truncate on error [Fam]
v8: retitle and rework to avoid possibility of secondary failure [John]
v7: new patch [Kevin]
--- include/block/dirty-bitmap.h | 2 +- block.c | 15 ++++++++++----- block/dirty-bitmap.c | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-)
include/block/dirty-bitmap.h +1 -1
@@ -83,7 +83,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t sector_num);int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap);int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap); -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs); +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes);bool bdrv_dirty_bitmap_readonly (const BdrvDirtyBitmap *bitmap);bool bdrv_has_readonly_bitmaps (BlockDriverState *bs);bool bdrv_dirty_bitmap_get_autoload (const BdrvDirtyBitmap *bitmap);
block.c +10 -5
@@ -3450,12 +3450,17 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,assert (!(bs->open_flags &BDRV_O_INACTIVE )); ret = drv->bdrv_truncate (bs, offset, prealloc, errp); -if (ret ==0 ) { - ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS ); -bdrv_dirty_bitmap_truncate (bs); -bdrv_parent_cb_resize (bs); -atomic_inc (&bs->write_gen); +if (ret <0 ) { +return ret; } + ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS ); +if (ret <0 ) { +error_setg_errno (errp, -ret,"Could not refresh total sector count" ); + }else { +bdrv_dirty_bitmap_truncate (bs, bs->total_sectors *BDRV_SECTOR_SIZE ); + } +bdrv_parent_cb_resize (bs); +atomic_inc (&bs->write_gen);return ret; }
block/dirty-bitmap.c +3 -3
@@ -1,7 +1,7 @@/* * Block Dirty Bitmap * -* Copyright (c) 2016 Red Hat. Inc +* Copyright (c) 2016-2017 Red Hat. Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal
@@ -302,10 +302,10 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,* Truncates _all_ bitmaps attached to a BDS. * Called with BQL taken. */ -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs) +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes) {BdrvDirtyBitmap *bitmap; -uint64_t size =bdrv_nb_sectors (bs); +int64_t size =DIV_ROUND_UP (bytes,BDRV_SECTOR_SIZE );bdrv_dirty_bitmaps_lock (bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
Signature · 1 line
2.13.5
Re: [Qemu-devel] [PATCH v9 05/20] dirty-bitmap: Avoid size query failure during truncate
Quoted 43 lines · Eric Blake
We've previously fixed several places where we failed to account for possible errors from
bdrv_nb_sectors(). Fix another one by makingbdrv_dirty_bitmap_truncate()take the new size from the caller instead of querying itself; then adjust the sole callerbdrv_truncate()to pass the size just determined by a successful resize, or to skip the bitmap resize on failure, thus avoiding sizing the bitmaps to -1.Signed-off-by Eric Blake <[email protected]>---
v9: skip onlybdrv_dirty_bitmap_truncateon error [Fam]
v8: retitle and rework to avoid possibility of secondary failure [John]
v7: new patch [Kevin]--- include/block/dirty-bitmap.h | 2 +- block.c | 15 ++++++++++----- block/dirty-bitmap.c | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-)include/block/dirty-bitmap.h +1
-1@@ -83,7 +83,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t sector_num);int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap);int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap); -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs); +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes);bool bdrv_dirty_bitmap_readonly (const BdrvDirtyBitmap *bitmap);bool bdrv_has_readonly_bitmaps (BlockDriverState *bs);bool bdrv_dirty_bitmap_get_autoload (const BdrvDirtyBitmap *bitmap);block.c +0
-2@@ -3450,12 +3450,17 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,assert (!(bs->open_flags &BDRV_O_INACTIVE )); ret = drv->bdrv_truncate (bs, offset, prealloc, errp); -if (ret ==0 ) { - ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS );
block.c int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, · line 3459
Quoted 10 lines
block.c +6
-3-bdrv_dirty_bitmap_truncate (bs); -bdrv_parent_cb_resize (bs); -atomic_inc (&bs->write_gen); +if (ret <0 ) { +return ret; } + ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS ); +if (ret <0 ) { +error_setg_errno (errp, -ret,"Could not refresh total sector count" ); + }else {
Sorry for dragging my feet on this point; if anyone else wants to R-B this I will cede without much of a fuss, but perhaps you can help me understand.
(Copying some questions I asked Eric on IRC, airing to list for wider discussion, and also because I had to drive home before the stores closed for the evening)
Do you suspect that almost certainly if bdrv_truncate() fails overall that the image format driver will either unmount the image or become read-only?
There are calls from parallels, qcow, qcow2-refcount, qcow2, raw-format, vhdx-log, vhdx plus whichever calls from blk_truncate (jobs, all of the same formats, blockdev, qemu-img)
I'm just trying to picture exactly what's going to happen if we manage to resize the drive but then don't resize the bitmap -- say we expand the drive larger but fail to refresh (and fail to resize the bitmap.)
if the block format module does not immediately and dutifully stop all further writes -- is there anything that stops us from writing to new sectors that were beyond EOF previously?
I suppose if *not* that's a bug for callers of bdrv_truncate to allow that kind of monkey business, but if it CAN happen, hbitmap only guards against such things with an assert (which, IIRC, is not guaranteed to be on for all builds)
So the question is: "bdrv_truncate failure is NOT considered recoverable in ANY case, is it?"
It may possibly be safer to, if the initial truncate request succeeds, apply a best-effort to the bitmap before returning the error.
Quoted 34 lines
block.c +4
-0+bdrv_dirty_bitmap_truncate (bs, bs->total_sectors *BDRV_SECTOR_SIZE ); + } +bdrv_parent_cb_resize (bs); +atomic_inc (&bs->write_gen);return ret; }block/dirty-bitmap.c +3
-3@@ -1,7 +1,7 @@/* * Block Dirty Bitmap * -* Copyright (c) 2016 Red Hat. Inc +* Copyright (c) 2016-2017 Red Hat. Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -302,10 +302,10 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,* Truncates _all_ bitmaps attached to a BDS. * Called with BQL taken. */ -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs) +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes) {BdrvDirtyBitmap *bitmap; -uint64_t size =bdrv_nb_sectors (bs); +int64_t size =DIV_ROUND_UP (bytes,BDRV_SECTOR_SIZE );bdrv_dirty_bitmaps_lock (bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
Re: [Qemu-devel] [PATCH v9 05/20] dirty-bitmap: Avoid size query failure during truncate
Quoted 77 lines · John Snow
Quoted 43 lines · Eric Blake
We've previously fixed several places where we failed to account for possible errors from
bdrv_nb_sectors(). Fix another one by makingbdrv_dirty_bitmap_truncate()take the new size from the caller instead of querying itself; then adjust the sole callerbdrv_truncate()to pass the size just determined by a successful resize, or to skip the bitmap resize on failure, thus avoiding sizing the bitmaps to -1.Signed-off-by Eric Blake <[email protected]>---
v9: skip onlybdrv_dirty_bitmap_truncateon error [Fam]
v8: retitle and rework to avoid possibility of secondary failure [John]
v7: new patch [Kevin]--- include/block/dirty-bitmap.h | 2 +- block.c | 15 ++++++++++----- block/dirty-bitmap.c | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-)include/block/dirty-bitmap.h +1
-1@@ -83,7 +83,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t sector_num);int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap);int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap); -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs); +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes);bool bdrv_dirty_bitmap_readonly (const BdrvDirtyBitmap *bitmap);bool bdrv_has_readonly_bitmaps (BlockDriverState *bs);bool bdrv_dirty_bitmap_get_autoload (const BdrvDirtyBitmap *bitmap);block.c +0
-2@@ -3450,12 +3450,17 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,assert (!(bs->open_flags &BDRV_O_INACTIVE )); ret = drv->bdrv_truncate (bs, offset, prealloc, errp); -if (ret ==0 ) { - ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS );block.c int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, · line 3459
Quoted 10 lines
block.c +6
-3-bdrv_dirty_bitmap_truncate (bs); -bdrv_parent_cb_resize (bs); -atomic_inc (&bs->write_gen); +if (ret <0 ) { +return ret; } + ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS ); +if (ret <0 ) { +error_setg_errno (errp, -ret,"Could not refresh total sector count" ); + }else {Sorry for dragging my feet on this point; if anyone else wants to R-B this I will cede without much of a fuss, but perhaps you can help me understand.
(Copying some questions I asked Eric on IRC, airing to list for wider discussion, and also because I had to drive home before the stores closed for the evening)
Do you suspect that almost certainly if
bdrv_truncate()fails overall that the image format driver will either unmount the image or become read-only?There are calls from parallels, qcow, qcow2-refcount, qcow2, raw-format, vhdx-log, vhdx plus whichever calls from
blk_truncate(jobs, all of the same formats, blockdev, qemu-img)I'm just trying to picture exactly what's going to happen if we manage to resize the drive but then don't resize the bitmap -- say we expand the drive larger but fail to refresh (and fail to resize the bitmap.)
Quoted 8 lines
if the block format module does not immediately and dutifully stop all further writes -- is there anything that stops us from writing to new sectors that were beyond EOF previously?
I suppose if *not* that's a bug for callers of
bdrv_truncateto allow that kind of monkey business, but if it CAN happen, hbitmap only guards against such things with an assert (which, IIRC, is not guaranteed to be on for all builds)
It's guaranteed since a few hours ago:
commit 262a69f4282e44426c7a132138581d400053e0a1
Author: Eric Blake <[email protected]> Date: Mon Sep 11 16:13:20 2017 -0500
osdep.h: Prohibit disabling assert() in supported builds
Quoted 7 lines
So the question is: "
bdrv_truncatefailure is NOT considered recoverable in ANY case, is it?"It may possibly be safer to, if the initial truncate request succeeds, apply a best-effort to the bitmap before returning the error.
Like fallback "offset" (or it aligned up to bs cluster size) if refresh_total_sectors() returns error? I think that is okay.
Fam
Re: [Qemu-devel] [PATCH v9 05/20] dirty-bitmap: Avoid size query failure during truncate
Quoted 4 lines · Fam Zheng
Quoted 4 lines
Do you suspect that almost certainly if
bdrv_truncate()fails overall that the image format driver will either unmount the image or become read-only?
Uggh - it feels like I've bitten off more than I can chew with this patch - I'm getting bogged down by trying to fix bad behavior in code that is mostly unrelated to the patch at hand, so I don't have a good opinion on WHAT is supposed to happen if bdrv_truncate() fails, only that I'm trying to avoid compounding that failure even worse.
Quoted 8 lines
Quoted 4 lines
I suppose if *not* that's a bug for callers of
bdrv_truncateto allow that kind of monkey business, but if it CAN happen, hbitmap only guards against such things with an assert (which, IIRC, is not guaranteed to be on for all builds)It's guaranteed since a few hours ago:
commit
262a69f4282e44426c7a132138581d400053e0a1
Indeed - but even without my patch, we would have hit the assertion failures when trying to resize the dirty bitmap to -1 when bdrv_nb_sectors() fails (which was likely if refresh_total_sectors() failed).
Quoted 8 lines
Quoted 5 lines
So the question is: "
bdrv_truncatefailure is NOT considered recoverable in ANY case, is it?"It may possibly be safer to, if the initial truncate request succeeds, apply a best-effort to the bitmap before returning the error.
Like fallback "offset" (or it aligned up to bs cluster size) if
refresh_total_sectors()returns error? I think that is okay.
Here's my proposal for squashing in a best-effort dirty-bitmap resize no matter what happens in refresh_total_sectors() (but really, if you successfully truncate the disk but then get a failure while trying to read back the actual new size, which may differ from the requested size, you're probably doomed down the road anyways).
block.c +0 -0
@@ -3552,8 +3552,9 @@ int bdrv_truncate(BdrvChild *child, int64_t
offset, PreallocMode prealloc,
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not refresh total sector
count");
} else {
- bdrv_dirty_bitmap_truncate(bs, bs->total_sectors *
BDRV_SECTOR_SIZE);
+ offset = bs->total_sectors * BDRV_SECTOR_SIZE;
}
+ bdrv_dirty_bitmap_truncate(bs, offset);
bdrv_parent_cb_resize(bs);
atomic_inc(&bs->write_gen);
return ret;Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Re: [Qemu-devel] [PATCH v9 05/20] dirty-bitmap: Avoid size query failure during truncate
Quoted 7 lines · Eric Blake
Quoted 4 lines · Fam Zheng
Quoted 4 lines
Do you suspect that almost certainly if
bdrv_truncate()fails overall that the image format driver will either unmount the image or become read-only?
Quoted 6 lines
Uggh - it feels like I've bitten off more than I can chew with this patch - I'm getting bogged down by trying to fix bad behavior in code that is mostly unrelated to the patch at hand, so I don't have a good opinion on WHAT is supposed to happen if
bdrv_truncate()fails, only that I'm trying to avoid compounding that failure even worse.
Yes, I apologize -- I realize I'm holding this series hostage. For now I am just trying to legitimately understand the behavior. I am willing to accept "It's sorta busted right now, but -EOUTOFSCOPE"
Quoted 44 lines
Quoted 8 lines
Quoted 4 lines
I suppose if *not* that's a bug for callers of
bdrv_truncateto allow that kind of monkey business, but if it CAN happen, hbitmap only guards against such things with an assert (which, IIRC, is not guaranteed to be on for all builds)It's guaranteed since a few hours ago:
commit
262a69f4282e44426c7a132138581d400053e0a1Indeed - but even without my patch, we would have hit the assertion failures when trying to resize the dirty bitmap to -1 when
bdrv_nb_sectors()fails (which was likely ifrefresh_total_sectors()failed).Quoted 8 lines
Quoted 5 lines
So the question is: "
bdrv_truncatefailure is NOT considered recoverable in ANY case, is it?"It may possibly be safer to, if the initial truncate request succeeds, apply a best-effort to the bitmap before returning the error.
Like fallback "offset" (or it aligned up to bs cluster size) if
refresh_total_sectors()returns error? I think that is okay.Here's my proposal for squashing in a best-effort dirty-bitmap resize no matter what happens in
refresh_total_sectors()(but really, if you successfully truncate the disk but then get a failure while trying to read back the actual new size, which may differ from the requested size, you're probably doomed down the road anyways).block.c +2
-1@@ -3552,8 +3552,9 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, if (ret < 0) {error_setg_errno (errp, -ret,"Could not refresh total sector count"); } else { -bdrv_dirty_bitmap_truncate (bs, bs->total_sectors * BDRV_SECTOR_SIZE); + offset = bs->total_sectors *BDRV_SECTOR_SIZE ; } +bdrv_dirty_bitmap_truncate (bs, offset);
Quoted 5 lines
bdrv_parent_cb_resize(bs);
atomic_inc(&bs->write_gen);
return ret;Don't respin on my accord, I'm trying to find out if there is a problem; I'm not convinced of one yet. Just thinking out loud.
Two cases:
(1) Attempt to resize larger. Resize succeeds, but refresh fails. Possibly a temporary protocol failure, but we'll assume the resize actually worked. Bitmap does not get resized, however any caller of truncate *must* assume that the resize did not succeed. Any calls to write beyond previous EOF are a bug by the calling module.
(2) Attempt to resize smaller, an actual truncate. Call succeeds but refresh doesn't. Bitmap is now larger than the drive. The bitmap itself is perfectly capable of describing reads/writes even to the now-OOB area, but it's unlikely the BB would submit any. Problems may arise if the BB does not treat this as a hard failure and a user later attempts to use this bitmap for a backup operation, as the trailing bits now reference disk segments that may or may not physically exist. Likely to hit EIO problems during block jobs.
If we do decide to resize the bitmap even on refresh failure, We probably do still run the risk of the bitmap being slightly bigger or slightly smaller than the actual size due to alignment.
It sounds like the resize operation itself needs to be able to return to the caller the actual size of the operation instead of forcing the caller to query separately in a follow-up call to really "fix" this.
Considering that either resizing or not resizing the bitmap after a partial failure probably still leaves us with a possibly dangerous bitmap, I don't think I'll hold you to the flames over this one.
--js
Re: [Qemu-devel] [PATCH v9 05/20] dirty-bitmap: Avoid size query failure during truncate
In reply to Eric BlakeQuoted 25 lines · Eric Blake
We've previously fixed several places where we failed to account for possible errors from
bdrv_nb_sectors(). Fix another one by makingbdrv_dirty_bitmap_truncate()take the new size from the caller instead of querying itself; then adjust the sole callerbdrv_truncate()to pass the size just determined by a successful resize, or to skip the bitmap resize on failure, thus avoiding sizing the bitmaps to -1.Signed-off-by Eric Blake <[email protected]>---
v9: skip onlybdrv_dirty_bitmap_truncateon error [Fam]
v8: retitle and rework to avoid possibility of secondary failure [John]
v7: new patch [Kevin]--- include/block/dirty-bitmap.h | 2 +- block.c | 15 ++++++++++----- block/dirty-bitmap.c | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-)include/block/dirty-bitmap.h +0
-0
include/block/dirty-bitmap.h int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); · line 86
Quoted 5 lines
include/block/dirty-bitmap.h +1
-1@@ -83,7 +83,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter);void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t sector_num);int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap);int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap); -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs); +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes);
why not uint64_t as in following patches?
Quoted 12 lines
include/block/dirty-bitmap.h +0
-0bool bdrv_dirty_bitmap_readonly (const BdrvDirtyBitmap *bitmap);bool bdrv_has_readonly_bitmaps (BlockDriverState *bs);bool bdrv_dirty_bitmap_get_autoload (const BdrvDirtyBitmap *bitmap);block.c +0
-1@@ -3450,12 +3450,17 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,assert (!(bs->open_flags &BDRV_O_INACTIVE )); ret = drv->bdrv_truncate (bs, offset, prealloc, errp); -if (ret ==0 ) {
block.c int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, · line 3458
Quoted 10 lines
block.c +5
-4- ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS ); -bdrv_dirty_bitmap_truncate (bs); -bdrv_parent_cb_resize (bs); -atomic_inc (&bs->write_gen); +if (ret <0 ) { +return ret; } + ret =refresh_total_sectors (bs, offset >>BDRV_SECTOR_BITS ); +if (ret <0 ) { +error_setg_errno (errp, -ret,"Could not refresh total sector count" );
looks like a separate bug - we didn't set errp with <0 return value
Quoted 24 lines
block.c +5
-0+ }else { +bdrv_dirty_bitmap_truncate (bs, bs->total_sectors *BDRV_SECTOR_SIZE ); + } +bdrv_parent_cb_resize (bs); +atomic_inc (&bs->write_gen);return ret; }block/dirty-bitmap.c +1
-1@@ -1,7 +1,7 @@/* * Block Dirty Bitmap * -* Copyright (c) 2016 Red Hat. Inc +* Copyright (c) 2016-2017 Red Hat. Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -302,10 +302,10 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,* Truncates _all_ bitmaps attached to a BDS. * Called with BQL taken.
block/dirty-bitmap.c BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, · line 311
Quoted 10 lines
block/dirty-bitmap.c +2
-2*/ -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs) +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes) {BdrvDirtyBitmap *bitmap; -uint64_t size =bdrv_nb_sectors (bs); +int64_t size =DIV_ROUND_UP (bytes,BDRV_SECTOR_SIZE );bdrv_dirty_bitmaps_lock (bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
Looks like this all needs more work to make it really correct and safe (reading last John's comment).. And this patch don't really relate to the series, so if it will be the only obstacle for merging, can we merge all other patches first? I'll then rebase dirty bitmap migration series on master..
Signature · 2 lines
Best regards, Vladimir
Re: [Qemu-devel] [PATCH v9 05/20] dirty-bitmap: Avoid size query failure during truncate
Quoted 21 lines · Vladimir Sementsov-Ogievskiy
Quoted 20 lines · Eric Blake
We've previously fixed several places where we failed to account for possible errors from bdrv_nb_sectors(). Fix another one by making
bdrv_dirty_bitmap_truncate()take the new size from the caller instead of querying itself; then adjust the sole callerbdrv_truncate()to pass the size just determined by a successful resize, or to skip the bitmap resize on failure, thus avoiding sizing the bitmaps to -1.Signed-off-by Eric Blake <[email protected]>---
v9: skip onlybdrv_dirty_bitmap_truncateon error [Fam]
v8: retitle and rework to avoid possibility of secondary failure [John]
v7: new patch [Kevin]
---
include/block/dirty-bitmap.h| 2 +-
block.c | 15 ++++++++++----- block/dirty-bitmap.c | 6 +++---3 files changed, 14 insertions(+), 9 deletions(-)
Quoted 13 lines
include/block/dirty-bitmap.h int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter · line 87
Quoted 11 lines
include/block/dirty-bitmap.h +1
-1@@ -83,7 +83,7 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t sector_num); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); -void bdrv_dirty_bitmap_truncate (BlockDriverState *bs); +void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes);why not
uint64_tas in following patches?
Because off_t is signed, so you can never have more than 2^63 (and NOT 2^64) bytes for your size anyways. The following patches use int64_t, rather than uint64_t, both because of off_t, and because it leaves room for returning negative values on error.
Quoted 12 lines
Quoted 12 lines
include/block/dirty-bitmap.h +0
-0bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); bool bdrv_has_readonly_bitmaps(BlockDriverState *bs);bool
bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap);block.c +0
-0@@ -3450,12 +3450,17 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, assert(!(bs->open_flags & BDRV_O_INACTIVE));
Quoted 15 lines
block.c int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
Quoted 13 lines
block.c +5
-5ret = drv->bdrv_truncate(bs, offset, prealloc, errp); - if (ret == 0) { - ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); - bdrv_dirty_bitmap_truncate(bs); - bdrv_parent_cb_resize(bs); - atomic_inc(&bs->write_gen); + if (ret < 0) { + return ret; } + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count");looks like a separate bug - we didn't set errp with <0 return value
Yes, it was a pre-existing bug. If I have to respin, I can update the commit message to mention it.
Quoted 7 lines
Looks like this all needs more work to make it really correct and safe (reading last John's comment)..
And this patch don't really relate to the series, so if it will be the only obstacle for merging, can we
merge all other patches first? I'll then rebase dirty bitmap migration series on master..
But it does relate, because I have to do something to avoid calling a failing bdrv_nb_sectors/bdrv_getlength.
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
PATCH v9 6/20 dirty-bitmap: Change bdrv_dirty_bitmap_size() to report bytes
In reply to Eric BlakeWe're already reporting bytes for bdrv_dirty_bitmap_granularity(); mixing bytes and sectors in our return values is a recipe for confusion. A later cleanup will convert dirty bitmap internals to be entirely byte-based, but in the meantime, we should report the bitmap size in bytes.
The only external caller in qcow2-bitmap.c is temporarily more verbose (because it is still using sector-based math), but will later be switched to track progress by bytes instead of sectors.
---
v8: no change
v7: split external from internal change [Kevin], drop R-b
v6: no change
v5: fix bdrv_dirty_bitmap_truncate [John], drop R-b
v4: retitle from "Track size in bytes", rebase to persistent bitmaps, round up when converting bytes to sectors
v3: no change
v2: tweak commit message, no code change
--- block/dirty-bitmap.c | 2 +- block/qcow2-bitmap.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-)
block/dirty-bitmap.c +1 -1
@@ -175,7 +175,7 @@ void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)int64_t bdrv_dirty_bitmap_size (const BdrvDirtyBitmap *bitmap) { -return bitmap->size; +return bitmap->size *BDRV_SECTOR_SIZE ; }const char *bdrv_dirty_bitmap_name (const BdrvDirtyBitmap *bitmap)
block/qcow2-bitmap.c +8 -6
@@ -295,10 +295,11 @@ static int load_bitmap_data(BlockDriverState *bs,BDRVQcow2State *s = bs->opaque;uint64_t sector, sbc;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap); +uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );uint8_t *buf =NULL ;uint64_t i, tab_size =size_to_clusters (s, -bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_size)); +bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_sectors));if (tab_size != bitmap_table_size || tab_size >BME_MAX_TABLE_SIZE ) {return -EINVAL ;
@@ -307,7 +308,7 @@ static int load_bitmap_data(BlockDriverState *bs, buf =g_malloc (s->cluster_size); sbc =sectors_covered_by_bitmap_cluster (s, bitmap);for (i =0 , sector =0 ; i < tab_size; ++i, sector += sbc) { -uint64_t count =MIN (bm_size - sector, sbc); +uint64_t count =MIN (bm_sectors - sector, sbc);uint64_t entry = bitmap_table[i];uint64_t offset = entry &BME_TABLE_ENTRY_OFFSET_MASK ;
@@ -1077,13 +1078,14 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,int64_t sector;uint64_t sbc;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap); +uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );const char *bm_name =bdrv_dirty_bitmap_name (bitmap);uint8_t *buf =NULL ;BdrvDirtyBitmapIter *dbi;uint64_t *tb;uint64_t tb_size =size_to_clusters (s, -bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_size)); +bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_sectors));if (tb_size >BME_MAX_TABLE_SIZE || tb_size * s->cluster_size >BME_MAX_PHYS_SIZE )
@@ -1101,7 +1103,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, dbi =bdrv_dirty_iter_new (bitmap,0 ); buf =g_malloc (s->cluster_size); sbc =sectors_covered_by_bitmap_cluster (s, bitmap); -assert (DIV_ROUND_UP (bm_size, sbc) == tb_size); +assert (DIV_ROUND_UP (bm_sectors, sbc) == tb_size);while ((sector =bdrv_dirty_iter_next (dbi)) !=-1 ) {uint64_t cluster = sector / sbc;
@@ -1109,7 +1111,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,int64_t off; sector = cluster * sbc; - end =MIN (bm_size, sector + sbc); + end =MIN (bm_sectors, sector + sbc); write_size =bdrv_dirty_bitmap_serialization_size (bitmap, sector, end - sector);assert (write_size <= s->cluster_size);
@@ -1141,7 +1143,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } -if (end >= bm_size) { +if (end >= bm_sectors) {break ; }
Signature · 1 line
2.13.5
PATCH v9 7/20 dirty-bitmap: Track bitmap size by bytes
In reply to Eric BlakeWe are still using an internal hbitmap that tracks a size in sectors, with the granularity scaled down accordingly, because it lets us use a shortcut for our iterators which are currently sector-based. But there's no reason we can't track the dirty bitmap size in bytes, since it is (mostly) an internal-only variable (remember, the size is how many bytes are covered by the bitmap, not how many bytes the bitmap occupies). A later cleanup will convert dirty bitmap internals to be entirely byte-based, eliminating the intermediate sector rounding added here; and technically, since bdrv_getlength() already rounds up to sectors, our use of DIV_ROUND_UP is more for theoretical completeness than for any actual rounding.
Use is_power_of_2() while at it, instead of open-coding that.
---
v8: rebase to earlier truncate changes, R-b kept
v7: split external from internal [Kevin], drop R-b
v6: no change
v5: fix bdrv_dirty_bitmap_truncate [John], drop R-b
v4: retitle from "Track size in bytes", rebase to persistent bitmaps, round up when converting bytes to sectors
v3: no change
v2: tweak commit message, no code change
--- block/dirty-bitmap.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-)
block/dirty-bitmap.c +14 -12
@@ -42,7 +42,7 @@ struct BdrvDirtyBitmap {HBitmap *meta;/* Meta dirty bitmap */ BdrvDirtyBitmap *successor;/* Anonymous child; implies frozen status */ char *name;/* Optional non-empty unique ID */ -int64_t size;/* Size of the bitmap (Number of sectors) */ +int64_t size;/* Size of the bitmap, in bytes */ bool disabled;/* Bitmap is disabled. It ignores all writes to the device */ int active_iterators;/* How many iterators are active */
@@ -115,17 +115,14 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, {int64_t bitmap_size;BdrvDirtyBitmap *bitmap; -uint32_t sector_granularity; -assert ((granularity & (granularity -1 )) ==0 ); +assert (is_power_of_2 (granularity) && granularity >=BDRV_SECTOR_SIZE );if (name &&bdrv_find_dirty_bitmap (bs, name)) {error_setg (errp,"Bitmap already exists: %s" , name);return NULL ; } - sector_granularity = granularity >>BDRV_SECTOR_BITS ; -assert (sector_granularity); - bitmap_size =bdrv_nb_sectors (bs); + bitmap_size =bdrv_getlength (bs);if (bitmap_size <0 ) {error_setg_errno (errp, -bitmap_size,"could not get length of device" ); errno = -bitmap_size;
@@ -133,7 +130,12 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, } bitmap =g_new0 (BdrvDirtyBitmap,1 ); bitmap->mutex = &bs->dirty_bitmap_mutex; - bitmap->bitmap =hbitmap_alloc (bitmap_size,ctz32 (sector_granularity)); +/* +* TODO - let hbitmap track full granularity. For now, it is tracking +* only sector granularity, as a shortcut for our iterators. +*/ + bitmap->bitmap =hbitmap_alloc (DIV_ROUND_UP (bitmap_size,BDRV_SECTOR_SIZE ), +ctz32 (granularity) -BDRV_SECTOR_BITS ); bitmap->size = bitmap_size; bitmap->name =g_strdup (name); bitmap->disabled = false;
@@ -175,7 +177,7 @@ void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap)int64_t bdrv_dirty_bitmap_size (const BdrvDirtyBitmap *bitmap) { -return bitmap->size *BDRV_SECTOR_SIZE ; +return bitmap->size; }const char *bdrv_dirty_bitmap_name (const BdrvDirtyBitmap *bitmap)
@@ -305,14 +307,13 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes) {BdrvDirtyBitmap *bitmap; -int64_t size =DIV_ROUND_UP (bytes,BDRV_SECTOR_SIZE );bdrv_dirty_bitmaps_lock (bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {assert (!bdrv_dirty_bitmap_frozen (bitmap));assert (!bitmap->active_iterators); -hbitmap_truncate (bitmap->bitmap, size); - bitmap->size = size; +hbitmap_truncate (bitmap->bitmap,DIV_ROUND_UP (bytes,BDRV_SECTOR_SIZE )); + bitmap->size = bytes; }bdrv_dirty_bitmaps_unlock (bs); }
@@ -549,7 +550,8 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)hbitmap_reset_all (bitmap->bitmap); }else {HBitmap *backup = bitmap->bitmap; - bitmap->bitmap =hbitmap_alloc (bitmap->size, + bitmap->bitmap =hbitmap_alloc (DIV_ROUND_UP (bitmap->size, +BDRV_SECTOR_SIZE ),hbitmap_granularity (backup)); *out = backup; }
Signature · 1 line
2.13.5
PATCH v9 8/20 dirty-bitmap: Change bdrv_dirty_bitmap_*serialize*() to take bytes
In reply to Eric BlakeRight now, the dirty-bitmap code exposes the fact that we use a scale of sector granularity in the underlying hbitmap to anything that wants to serialize a dirty bitmap. It's nicer to uniformly expose bytes as our dirty-bitmap interface, matching the previous change to bitmap size. The only caller to serialization is currently qcow2-cluster.c, which becomes a bit more verbose because it is still tracking sectors for other reasons, but a later patch will fix that to more uniformly use byte offsets everywhere. Likewise, within dirty-bitmap, we have to add more assertions that we are not truncating incorrectly, which can go away once the internal hbitmap is byte-based rather than sector-based.
---
v5: no change
v4: new patch
--- include/block/dirty-bitmap.h | 14 +++++++------- block/dirty-bitmap.c | 37 ++++++++++++++++++++++++------------- block/qcow2-bitmap.c | 22 ++++++++++++++-------- 3 files changed, 45 insertions(+), 28 deletions(-)
include/block/dirty-bitmap.h +7 -7
@@ -49,19 +49,19 @@ BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap,void bdrv_dirty_iter_free (BdrvDirtyBitmapIter *iter);uint64_t bdrv_dirty_bitmap_serialization_size (const BdrvDirtyBitmap *bitmap, -uint64_t start,uint64_t count); +uint64_t offset,uint64_t bytes);uint64_t bdrv_dirty_bitmap_serialization_align (const BdrvDirtyBitmap *bitmap);void bdrv_dirty_bitmap_serialize_part (const BdrvDirtyBitmap *bitmap, -uint8_t *buf,uint64_t start, -uint64_t count); +uint8_t *buf,uint64_t offset, +uint64_t bytes);void bdrv_dirty_bitmap_deserialize_part (BdrvDirtyBitmap *bitmap, -uint8_t *buf,uint64_t start, -uint64_t count,bool finish); +uint8_t *buf,uint64_t offset, +uint64_t bytes,bool finish);void bdrv_dirty_bitmap_deserialize_zeroes (BdrvDirtyBitmap *bitmap, -uint64_t start,uint64_t count, +uint64_t offset,uint64_t bytes,bool finish);void bdrv_dirty_bitmap_deserialize_ones (BdrvDirtyBitmap *bitmap, -uint64_t start,uint64_t count, +uint64_t offset,uint64_t bytes,bool finish);void bdrv_dirty_bitmap_deserialize_finish (BdrvDirtyBitmap *bitmap);
block/dirty-bitmap.c +24 -13
@@ -568,42 +568,53 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) }uint64_t bdrv_dirty_bitmap_serialization_size (const BdrvDirtyBitmap *bitmap, -uint64_t start,uint64_t count) +uint64_t offset,uint64_t bytes) { -return hbitmap_serialization_size (bitmap->bitmap, start, count); +assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); +return hbitmap_serialization_size (bitmap->bitmap, + offset >>BDRV_SECTOR_BITS , + bytes >>BDRV_SECTOR_BITS ); }uint64_t bdrv_dirty_bitmap_serialization_align (const BdrvDirtyBitmap *bitmap) { -return hbitmap_serialization_align (bitmap->bitmap); +return hbitmap_serialization_align (bitmap->bitmap) *BDRV_SECTOR_SIZE ; }void bdrv_dirty_bitmap_serialize_part (const BdrvDirtyBitmap *bitmap, -uint8_t *buf,uint64_t start, -uint64_t count) +uint8_t *buf,uint64_t offset, +uint64_t bytes) { -hbitmap_serialize_part (bitmap->bitmap, buf, start, count); +assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); +hbitmap_serialize_part (bitmap->bitmap, buf, offset >>BDRV_SECTOR_BITS , + bytes >>BDRV_SECTOR_BITS ); }void bdrv_dirty_bitmap_deserialize_part (BdrvDirtyBitmap *bitmap, -uint8_t *buf,uint64_t start, -uint64_t count,bool finish) +uint8_t *buf,uint64_t offset, +uint64_t bytes,bool finish) { -hbitmap_deserialize_part (bitmap->bitmap, buf, start, count, finish); +assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); +hbitmap_deserialize_part (bitmap->bitmap, buf, offset >>BDRV_SECTOR_BITS , + bytes >>BDRV_SECTOR_BITS , finish); }void bdrv_dirty_bitmap_deserialize_zeroes (BdrvDirtyBitmap *bitmap, -uint64_t start,uint64_t count, +uint64_t offset,uint64_t bytes,bool finish) { -hbitmap_deserialize_zeroes (bitmap->bitmap, start, count, finish); +assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); +hbitmap_deserialize_zeroes (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , + bytes >>BDRV_SECTOR_BITS , finish); }void bdrv_dirty_bitmap_deserialize_ones (BdrvDirtyBitmap *bitmap, -uint64_t start,uint64_t count, +uint64_t offset,uint64_t bytes,bool finish) { -hbitmap_deserialize_ones (bitmap->bitmap, start, count, finish); +assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); +hbitmap_deserialize_ones (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , + bytes >>BDRV_SECTOR_BITS , finish); }void bdrv_dirty_bitmap_deserialize_finish (BdrvDirtyBitmap *bitmap)
block/qcow2-bitmap.c +14 -8
@@ -278,7 +278,7 @@ static uint64_t sectors_covered_by_bitmap_cluster(const BDRVQcow2State *s,bdrv_dirty_bitmap_granularity (bitmap) >>BDRV_SECTOR_BITS ;uint64_t sbc = sector_granularity * (s->cluster_size <<3 ); -assert (QEMU_IS_ALIGNED (sbc, +assert (QEMU_IS_ALIGNED (sbc <<BDRV_SECTOR_BITS ,bdrv_dirty_bitmap_serialization_align (bitmap)));return sbc; }
@@ -299,7 +299,7 @@ static int load_bitmap_data(BlockDriverState *bs,uint8_t *buf =NULL ;uint64_t i, tab_size =size_to_clusters (s, -bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_sectors)); +bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_size));if (tab_size != bitmap_table_size || tab_size >BME_MAX_TABLE_SIZE ) {return -EINVAL ;
@@ -316,7 +316,9 @@ static int load_bitmap_data(BlockDriverState *bs,if (offset ==0 ) {if (entry &BME_TABLE_ENTRY_FLAG_ALL_ONES ) { -bdrv_dirty_bitmap_deserialize_ones (bitmap, sector, count, +bdrv_dirty_bitmap_deserialize_ones (bitmap, + sector *BDRV_SECTOR_SIZE , + count *BDRV_SECTOR_SIZE , false); }else {/* No need to deserialize zeros because the dirty bitmap is
@@ -327,7 +329,9 @@ static int load_bitmap_data(BlockDriverState *bs,if (ret <0 ) { goto finish; } -bdrv_dirty_bitmap_deserialize_part (bitmap, buf, sector, count, +bdrv_dirty_bitmap_deserialize_part (bitmap, buf, + sector *BDRV_SECTOR_SIZE , + count *BDRV_SECTOR_SIZE , false); } }
@@ -1085,7 +1089,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,uint64_t *tb;uint64_t tb_size =size_to_clusters (s, -bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_sectors)); +bdrv_dirty_bitmap_serialization_size (bitmap,0 , bm_size));if (tb_size >BME_MAX_TABLE_SIZE || tb_size * s->cluster_size >BME_MAX_PHYS_SIZE )
@@ -1112,8 +1116,8 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, sector = cluster * sbc; end =MIN (bm_sectors, sector + sbc); - write_size = -bdrv_dirty_bitmap_serialization_size (bitmap, sector, end - sector); + write_size =bdrv_dirty_bitmap_serialization_size (bitmap, + sector *BDRV_SECTOR_SIZE , (end - sector) *BDRV_SECTOR_SIZE );assert (write_size <= s->cluster_size); off =qcow2_alloc_clusters (bs, s->cluster_size);
@@ -1125,7 +1129,9 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, } tb[cluster] = off; -bdrv_dirty_bitmap_serialize_part (bitmap, buf, sector, end - sector); +bdrv_dirty_bitmap_serialize_part (bitmap, buf, + sector *BDRV_SECTOR_SIZE , + (end - sector) *BDRV_SECTOR_SIZE );if (write_size < s->cluster_size) {memset (buf + write_size,0 , s->cluster_size - write_size); }
Signature · 1 line
2.13.5
PATCH v9 9/20 qcow2: Switch sectors_covered_by_bitmap_cluster() to byte-based
In reply to Eric BlakeWe are gradually converting to byte-based interfaces, as they are easier to reason about than sector-based. Change the qcow2 bitmap helper function sectors_covered_by_bitmap_cluster(), renaming it to bytes_covered_by_bitmap_cluster() in the process.
---
v5: no change
v4: new patch
--- block/qcow2-bitmap.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
block/qcow2-bitmap.c +14 -14
@@ -269,18 +269,16 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)return 0 ; } -/* This function returns the number of disk sectors covered by a single qcow2 -* cluster of bitmap data. */ -static uint64_t sectors_covered_by_bitmap_cluster (const BDRVQcow2State *s, -const BdrvDirtyBitmap *bitmap) +/* Return the disk size covered by a single qcow2 cluster of bitmap data. */ +static uint64_t bytes_covered_by_bitmap_cluster (const BDRVQcow2State *s, +const BdrvDirtyBitmap *bitmap) { -uint64_t sector_granularity = -bdrv_dirty_bitmap_granularity (bitmap) >>BDRV_SECTOR_BITS ; -uint64_t sbc = sector_granularity * (s->cluster_size <<3 ); +uint64_t granularity =bdrv_dirty_bitmap_granularity (bitmap); +uint64_t limit = granularity * (s->cluster_size <<3 ); -assert (QEMU_IS_ALIGNED (sbc <<BDRV_SECTOR_BITS , +assert (QEMU_IS_ALIGNED (limit,bdrv_dirty_bitmap_serialization_align (bitmap))); -return sbc; +return limit; }/* load_bitmap_data
@@ -293,7 +291,7 @@ static int load_bitmap_data(BlockDriverState *bs, {int ret =0 ;BDRVQcow2State *s = bs->opaque; -uint64_t sector, sbc; +uint64_t sector, limit, sbc;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap);uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );uint8_t *buf =NULL ;
@@ -306,7 +304,8 @@ static int load_bitmap_data(BlockDriverState *bs, } buf =g_malloc (s->cluster_size); - sbc =sectors_covered_by_bitmap_cluster (s, bitmap); + limit =bytes_covered_by_bitmap_cluster (s, bitmap); + sbc = limit >>BDRV_SECTOR_BITS ;for (i =0 , sector =0 ; i < tab_size; ++i, sector += sbc) {uint64_t count =MIN (bm_sectors - sector, sbc);uint64_t entry = bitmap_table[i];
@@ -1080,7 +1079,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,int ret;BDRVQcow2State *s = bs->opaque;int64_t sector; -uint64_t sbc; +uint64_t limit, sbc;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap);uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );const char *bm_name =bdrv_dirty_bitmap_name (bitmap);
@@ -1106,8 +1105,9 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, dbi =bdrv_dirty_iter_new (bitmap,0 ); buf =g_malloc (s->cluster_size); - sbc =sectors_covered_by_bitmap_cluster (s, bitmap); -assert (DIV_ROUND_UP (bm_sectors, sbc) == tb_size); + limit =bytes_covered_by_bitmap_cluster (s, bitmap); + sbc = limit >>BDRV_SECTOR_BITS ; +assert (DIV_ROUND_UP (bm_size, limit) == tb_size);while ((sector =bdrv_dirty_iter_next (dbi)) !=-1 ) {uint64_t cluster = sector / sbc;
Signature · 1 line
2.13.5
PATCH v9 10/20 dirty-bitmap: Set iterator start by offset, not sector
In reply to Eric BlakeAll callers to bdrv_dirty_iter_new() passed 0 for their initial starting point, drop that parameter.
Most callers to bdrv_set_dirty_iter() were scaling a byte offset to a sector number; the exception qcow2-bitmap will be converted later to use byte rather than sector iteration. Move the scaling to occur internally to dirty bitmap code instead, so that callers now pass in bytes.
---
v5: no change
v4: rebase to persistent bitmaps
v3: no change
v2: no change
--- include/block/dirty-bitmap.h | 5 ++--- block/backup.c | 5 ++--- block/dirty-bitmap.c | 9 ++++----- block/mirror.c | 4 ++-- block/qcow2-bitmap.c | 4 ++-- 5 files changed, 12 insertions(+), 15 deletions(-)
include/block/dirty-bitmap.h +2 -3
@@ -44,8 +44,7 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,void bdrv_reset_dirty_bitmap (BdrvDirtyBitmap *bitmap,int64_t cur_sector,int64_t nr_sectors);BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new (BdrvDirtyBitmap *bitmap); -BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap, -uint64_t first_sector); +BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap);void bdrv_dirty_iter_free (BdrvDirtyBitmapIter *iter); uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,
@@ -80,7 +79,7 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,void bdrv_reset_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap,int64_t cur_sector,int64_t nr_sectors);int64_t bdrv_dirty_iter_next (BdrvDirtyBitmapIter *iter); -void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t sector_num); +void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t offset);int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap);int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap);void bdrv_dirty_bitmap_truncate (BlockDriverState *bs,int64_t bytes);
block/backup.c +2 -3
@@ -372,7 +372,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) granularity =bdrv_dirty_bitmap_granularity (job->sync_bitmap); clusters_per_iter =MAX ((granularity / job->cluster_size),1 ); - dbi =bdrv_dirty_iter_new (job->sync_bitmap,0 ); + dbi =bdrv_dirty_iter_new (job->sync_bitmap);/* Find the next dirty sector(s) */ while ((offset =bdrv_dirty_iter_next (dbi) *BDRV_SECTOR_SIZE ) >=0 ) {
@@ -403,8 +403,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)/* If the bitmap granularity is smaller than the backup granularity, * we need to advance the iterator pointer to the next cluster. */ if (granularity < job->cluster_size) { -bdrv_set_dirty_iter (dbi, - cluster * job->cluster_size /BDRV_SECTOR_SIZE ); +bdrv_set_dirty_iter (dbi, cluster * job->cluster_size); } last_cluster = cluster -1 ;
block/dirty-bitmap.c +4 -5
@@ -473,11 +473,10 @@ uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap)return BDRV_SECTOR_SIZE <<hbitmap_granularity (bitmap->bitmap); } -BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap, -uint64_t first_sector) +BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap) {BdrvDirtyBitmapIter *iter =g_new (BdrvDirtyBitmapIter,1 ); -hbitmap_iter_init (&iter->hbi, bitmap->bitmap, first_sector); +hbitmap_iter_init (&iter->hbi, bitmap->bitmap,0 ); iter->bitmap = bitmap; bitmap->active_iterators++;return iter;
@@ -645,9 +644,9 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,/** * Advance a BdrvDirtyBitmapIter to an arbitrary offset. */ -void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *iter,int64_t sector_num) +void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *iter,int64_t offset) { -hbitmap_iter_init (&iter->hbi, iter->hbi.hb, sector_num); +hbitmap_iter_init (&iter->hbi, iter->hbi.hb, offset >>BDRV_SECTOR_BITS ); }int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap)
block/mirror.c +2 -2
@@ -373,7 +373,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) next_dirty =bdrv_dirty_iter_next (s->dbi) *BDRV_SECTOR_SIZE ;if (next_dirty > next_offset || next_dirty <0 ) {/* The bitmap iterator's cache is stale, refresh it */ -bdrv_set_dirty_iter (s->dbi, next_offset >>BDRV_SECTOR_BITS ); +bdrv_set_dirty_iter (s->dbi, next_offset); next_dirty =bdrv_dirty_iter_next (s->dbi) *BDRV_SECTOR_SIZE ; }assert (next_dirty == next_offset);
@@ -796,7 +796,7 @@ static void coroutine_fn mirror_run(void *opaque) }assert (!s->dbi); - s->dbi =bdrv_dirty_iter_new (s->dirty_bitmap,0 ); + s->dbi =bdrv_dirty_iter_new (s->dirty_bitmap);for (;;) {uint64_t delay_ns =0 ;int64_t cnt, delta;
block/qcow2-bitmap.c +2 -2
@@ -1103,7 +1103,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,return NULL ; } - dbi =bdrv_dirty_iter_new (bitmap,0 ); + dbi =bdrv_dirty_iter_new (bitmap); buf =g_malloc (s->cluster_size); limit =bytes_covered_by_bitmap_cluster (s, bitmap); sbc = limit >>BDRV_SECTOR_BITS ;
@@ -1153,7 +1153,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,break ; } -bdrv_set_dirty_iter (dbi, end); +bdrv_set_dirty_iter (dbi, end *BDRV_SECTOR_SIZE ); } *bitmap_table_size = tb_size;
Signature · 1 line
2.13.5
PATCH v9 11/20 dirty-bitmap: Change bdrv_dirty_iter_next() to report byte offset
In reply to Eric BlakeThanks to recent cleanups, most callers were scaling a return value of sectors into bytes (the exception, in qcow2-bitmap, will be converted to byte-based iteration later). Update the interface to do the scaling internally instead.
In qcow2-bitmap, the code was specifically checking for an error return of -1. To avoid a regression, we either have to make sure we continue to return -1 (rather than a scaled -512) on error, or we have to fix the caller to treat all negative values as error rather than just one magic value. It's easy enough to make both changes at the same time, even though either one in isolation would work.
---
v8: tweak commit message, add R-b
v7: return -1, not -512; and fix qcow2-bitmap to check all negatives [Kevin]
v5-v6: no change
v4: rebase to persistent bitmap
v3: no change
v2: no change
--- block/backup.c | 2 +- block/dirty-bitmap.c | 3 ++- block/mirror.c | 8 ++++---- block/qcow2-bitmap.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-)
block/backup.c +1 -1
@@ -375,7 +375,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) dbi =bdrv_dirty_iter_new (job->sync_bitmap);/* Find the next dirty sector(s) */ -while ((offset =bdrv_dirty_iter_next (dbi) *BDRV_SECTOR_SIZE ) >=0 ) { +while ((offset =bdrv_dirty_iter_next (dbi)) >=0 ) { cluster = offset / job->cluster_size;/* Fake progress updates for any clusters we skipped */
block/dirty-bitmap.c +2 -1
@@ -503,7 +503,8 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)int64_t bdrv_dirty_iter_next (BdrvDirtyBitmapIter *iter) { -return hbitmap_iter_next (&iter->hbi); +int64_t ret =hbitmap_iter_next (&iter->hbi); +return ret <0 ?-1 : ret *BDRV_SECTOR_SIZE ; }/* Called within bdrv_dirty_bitmap_lock..unlock */
block/mirror.c +4 -4
@@ -336,10 +336,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)int max_io_bytes =MAX (s->buf_size /MAX_IN_FLIGHT ,MAX_IO_BYTES );bdrv_dirty_bitmap_lock (s->dirty_bitmap); - offset =bdrv_dirty_iter_next (s->dbi) *BDRV_SECTOR_SIZE ; + offset =bdrv_dirty_iter_next (s->dbi);if (offset <0 ) {bdrv_set_dirty_iter (s->dbi,0 ); - offset =bdrv_dirty_iter_next (s->dbi) *BDRV_SECTOR_SIZE ; + offset =bdrv_dirty_iter_next (s->dbi);trace_mirror_restart_iter (s,bdrv_get_dirty_count (s->dirty_bitmap) *BDRV_SECTOR_SIZE );assert (offset >=0 );
@@ -370,11 +370,11 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)break ; } - next_dirty =bdrv_dirty_iter_next (s->dbi) *BDRV_SECTOR_SIZE ; + next_dirty =bdrv_dirty_iter_next (s->dbi);if (next_dirty > next_offset || next_dirty <0 ) {/* The bitmap iterator's cache is stale, refresh it */ bdrv_set_dirty_iter (s->dbi, next_offset); - next_dirty =bdrv_dirty_iter_next (s->dbi) *BDRV_SECTOR_SIZE ; + next_dirty =bdrv_dirty_iter_next (s->dbi); }assert (next_dirty == next_offset); nb_chunks++;
block/qcow2-bitmap.c +1 -1
@@ -1109,7 +1109,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, sbc = limit >>BDRV_SECTOR_BITS ;assert (DIV_ROUND_UP (bm_size, limit) == tb_size); -while ((sector =bdrv_dirty_iter_next (dbi)) !=-1 ) { +while ((sector =bdrv_dirty_iter_next (dbi) >>BDRV_SECTOR_BITS ) >=0 ) {uint64_t cluster = sector / sbc;uint64_t end, write_size;int64_t off;
Signature · 1 line
2.13.5
PATCH v9 12/20 dirty-bitmap: Change bdrv_get_dirty_count() to report bytes
In reply to Eric BlakeThanks to recent cleanups, all callers were scaling a return value of sectors into bytes; do the scaling internally instead.
---
v8: no change, add R-b
v7: fix one more trace caller [Kevin]
v4-v6: no change
v3: no change, add R-b
v2: no change
--- block/dirty-bitmap.c | 4 ++-- block/mirror.c | 16 ++++++---------- migration/block.c | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-)
block/dirty-bitmap.c +2 -2
@@ -423,7 +423,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {BlockDirtyInfo *info =g_new0 (BlockDirtyInfo,1 );BlockDirtyInfoList *entry =g_new0 (BlockDirtyInfoList,1 ); - info->count =bdrv_get_dirty_count (bm) <<BDRV_SECTOR_BITS ; + info->count =bdrv_get_dirty_count (bm); info->granularity =bdrv_dirty_bitmap_granularity (bm); info->has_name = !!bm->name; info->name =g_strdup (bm->name);
@@ -652,7 +652,7 @@ void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t offset)int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap) { -return hbitmap_count (bitmap->bitmap); +return hbitmap_count (bitmap->bitmap) <<BDRV_SECTOR_BITS ; }int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap)
block/mirror.c +6 -10
@@ -340,8 +340,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)if (offset <0 ) {bdrv_set_dirty_iter (s->dbi,0 ); offset =bdrv_dirty_iter_next (s->dbi); -trace_mirror_restart_iter (s,bdrv_get_dirty_count (s->dirty_bitmap) * -BDRV_SECTOR_SIZE ); +trace_mirror_restart_iter (s,bdrv_get_dirty_count (s->dirty_bitmap));assert (offset >=0 ); }bdrv_dirty_bitmap_unlock (s->dirty_bitmap);
@@ -811,11 +810,10 @@ static void coroutine_fn mirror_run(void *opaque) cnt =bdrv_get_dirty_count (s->dirty_bitmap);/* s->common.offset contains the number of bytes already processed so -* far, cnt is the number of dirty sectors remaining and +* far, cnt is the number of dirty bytes remaining and * s->bytes_in_flight is the number of bytes currently being * processed; together those are the current total operation length */ - s->common.len = s->common.offset + s->bytes_in_flight + - cnt *BDRV_SECTOR_SIZE ; + s->common.len = s->common.offset + s->bytes_in_flight + cnt;/* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns.
@@ -827,8 +825,7 @@ static void coroutine_fn mirror_run(void *opaque) s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {if (s->in_flight >=MAX_IN_FLIGHT || s->buf_free_count ==0 || (cnt ==0 && s->in_flight >0 )) { -trace_mirror_yield (s, cnt *BDRV_SECTOR_SIZE , - s->buf_free_count, s->in_flight); +trace_mirror_yield (s, cnt, s->buf_free_count, s->in_flight);mirror_wait_for_io (s);continue ; }else if (cnt !=0 ) {
@@ -869,7 +866,7 @@ static void coroutine_fn mirror_run(void *opaque)* whether to switch to target check one last time if I/O has * come in the meanwhile, and if not flush the data to disk. */ -trace_mirror_before_drain (s, cnt *BDRV_SECTOR_SIZE ); +trace_mirror_before_drain (s, cnt);bdrv_drained_begin (bs); cnt =bdrv_get_dirty_count (s->dirty_bitmap);
@@ -888,8 +885,7 @@ static void coroutine_fn mirror_run(void *opaque) } ret =0 ; -trace_mirror_before_sleep (s, cnt *BDRV_SECTOR_SIZE , - s->synced, delay_ns); +trace_mirror_before_sleep (s, cnt, s->synced, delay_ns);if (!s->synced) {block_job_sleep_ns (&s->common,QEMU_CLOCK_REALTIME , delay_ns);if (block_job_is_cancelled (&s->common)) {
migration/block.c +1 -1
@@ -667,7 +667,7 @@ static int64_t get_remaining_dirty(void)aio_context_release (blk_get_aio_context (bmds->blk)); } -return dirty <<BDRV_SECTOR_BITS ; +return dirty; }
Signature · 1 line
2.13.5
PATCH v9 13/20 dirty-bitmap: Change bdrv_get_dirty_locked() to take bytes
In reply to Eric BlakeHalf the callers were already scaling bytes to sectors; the other half can eventually be simplified to use byte iteration. Both callers were already using the result as a bool, so make that explicit. Making the change also makes it easier for a future dirty-bitmap patch to offload scaling over to the internal hbitmap.
Remember, asking whether a byte is dirty is effectively asking whether the entire granularity containing the byte is dirty, since we only track dirtiness by granularity.
---
v4: only context change
v3: rebase to _locked rename was straightforward enough that R-b kept
v2: tweak commit message, no code change
--- include/block/dirty-bitmap.h | 4 ++-- block/dirty-bitmap.c | 8 ++++---- block/mirror.c | 3 +-- migration/block.c | 3 ++- 4 files changed, 9 insertions(+), 9 deletions(-)
include/block/dirty-bitmap.h +2 -2
@@ -72,8 +72,8 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap,/* Functions that require manual locking. */ void bdrv_dirty_bitmap_lock (BdrvDirtyBitmap *bitmap);void bdrv_dirty_bitmap_unlock (BdrvDirtyBitmap *bitmap); -int bdrv_get_dirty_locked (BlockDriverState *bs,BdrvDirtyBitmap *bitmap, -int64_t sector); +bool bdrv_get_dirty_locked (BlockDriverState *bs,BdrvDirtyBitmap *bitmap, +int64_t offset);void bdrv_set_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap,int64_t cur_sector,int64_t nr_sectors); void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
block/dirty-bitmap.c +4 -4
@@ -438,13 +438,13 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) }/* Called within bdrv_dirty_bitmap_lock..unlock */ -int bdrv_get_dirty_locked (BlockDriverState *bs,BdrvDirtyBitmap *bitmap, -int64_t sector) +bool bdrv_get_dirty_locked (BlockDriverState *bs,BdrvDirtyBitmap *bitmap, +int64_t offset) {if (bitmap) { -return hbitmap_get (bitmap->bitmap, sector); +return hbitmap_get (bitmap->bitmap, offset >>BDRV_SECTOR_BITS ); }else { -return 0 ; +return false; } }
block/mirror.c +1 -2
@@ -361,8 +361,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)int64_t next_offset = offset + nb_chunks * s->granularity;int64_t next_chunk = next_offset / s->granularity;if (next_offset >= s->bdev_length || - !bdrv_get_dirty_locked (source, s->dirty_bitmap, - next_offset >>BDRV_SECTOR_BITS )) { + !bdrv_get_dirty_locked (source, s->dirty_bitmap, next_offset)) {break ; }if (test_bit (next_chunk, s->in_flight_bitmap)) {
migration/block.c +2 -1
@@ -530,7 +530,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,blk_mig_unlock (); }bdrv_dirty_bitmap_lock (bmds->dirty_bitmap); -if (bdrv_get_dirty_locked (bs, bmds->dirty_bitmap, sector)) { +if (bdrv_get_dirty_locked (bs, bmds->dirty_bitmap, + sector *BDRV_SECTOR_SIZE )) {if (total_sectors - sector <BDRV_SECTORS_PER_DIRTY_CHUNK ) { nr_sectors = total_sectors - sector; }else {
Signature · 1 line
2.13.5
PATCH v9 14/20 dirty-bitmap: Change bdrv_[re]set_dirty_bitmap() to use bytes
In reply to Eric BlakeSome of the callers were already scaling bytes to sectors; others can be easily converted to pass byte offsets, all in our shift towards a consistent byte interface everywhere. Making the change will also make it easier to write the hold-out callers to use byte rather than sectors for their iterations; it also makes it easier for a future dirty-bitmap patch to offload scaling over to the internal hbitmap. Although all callers happen to pass sector-aligned values, make the internal scaling robust to any sub-sector requests.
---
v5: only context change
v4: only context change, due to rebasing to persistent bitmaps
v3: rebase to addition of _locked interfaces; complex enough that I dropped R-b
v2: no change
--- include/block/dirty-bitmap.h | 8 ++++---- block/dirty-bitmap.c | 22 ++++++++++++++-------- block/mirror.c | 16 ++++++++-------- migration/block.c | 7 +++++-- 4 files changed, 31 insertions(+), 22 deletions(-)
include/block/dirty-bitmap.h +4 -4
@@ -40,9 +40,9 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap);int64_t bdrv_dirty_bitmap_size (const BdrvDirtyBitmap *bitmap);DirtyBitmapStatus bdrv_dirty_bitmap_status (BdrvDirtyBitmap *bitmap);void bdrv_set_dirty_bitmap (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors); +int64_t offset,int64_t bytes);void bdrv_reset_dirty_bitmap (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors); +int64_t offset,int64_t bytes);BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new (BdrvDirtyBitmap *bitmap);BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap);void bdrv_dirty_iter_free (BdrvDirtyBitmapIter *iter);
@@ -75,9 +75,9 @@ void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap);bool bdrv_get_dirty_locked (BlockDriverState *bs,BdrvDirtyBitmap *bitmap,int64_t offset);void bdrv_set_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors); +int64_t offset,int64_t bytes);void bdrv_reset_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors); +int64_t offset,int64_t bytes);int64_t bdrv_dirty_iter_next (BdrvDirtyBitmapIter *iter);void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *hbi,int64_t offset);int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap);
block/dirty-bitmap.c +14 -8
@@ -509,35 +509,41 @@ int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)/* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_set_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors) +int64_t offset,int64_t bytes) { +int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE ); +assert (bdrv_dirty_bitmap_enabled (bitmap));assert (!bdrv_dirty_bitmap_readonly (bitmap)); -hbitmap_set (bitmap->bitmap, cur_sector, nr_sectors); +hbitmap_set (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , + end_sector - (offset >>BDRV_SECTOR_BITS )); }void bdrv_set_dirty_bitmap (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors) +int64_t offset,int64_t bytes) {bdrv_dirty_bitmap_lock (bitmap); -bdrv_set_dirty_bitmap_locked (bitmap, cur_sector, nr_sectors); +bdrv_set_dirty_bitmap_locked (bitmap, offset, bytes);bdrv_dirty_bitmap_unlock (bitmap); }/* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_reset_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors) +int64_t offset,int64_t bytes) { +int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE ); +assert (bdrv_dirty_bitmap_enabled (bitmap));assert (!bdrv_dirty_bitmap_readonly (bitmap)); -hbitmap_reset (bitmap->bitmap, cur_sector, nr_sectors); +hbitmap_reset (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , + end_sector - (offset >>BDRV_SECTOR_BITS )); }void bdrv_reset_dirty_bitmap (BdrvDirtyBitmap *bitmap, -int64_t cur_sector,int64_t nr_sectors) +int64_t offset,int64_t bytes) {bdrv_dirty_bitmap_lock (bitmap); -bdrv_reset_dirty_bitmap_locked (bitmap, cur_sector, nr_sectors); +bdrv_reset_dirty_bitmap_locked (bitmap, offset, bytes);bdrv_dirty_bitmap_unlock (bitmap); }
block/mirror.c +8 -8
@@ -141,8 +141,7 @@ static void mirror_write_complete(void *opaque, int ret)if (ret <0 ) {BlockErrorAction action; -bdrv_set_dirty_bitmap (s->dirty_bitmap, op->offset >>BDRV_SECTOR_BITS , - op->bytes >>BDRV_SECTOR_BITS ); +bdrv_set_dirty_bitmap (s->dirty_bitmap, op->offset, op->bytes); action =mirror_error_action (s, false, -ret);if (action ==BLOCK_ERROR_ACTION_REPORT && s->ret >=0 ) { s->ret = ret;
@@ -161,8 +160,7 @@ static void mirror_read_complete(void *opaque, int ret)if (ret <0 ) {BlockErrorAction action; -bdrv_set_dirty_bitmap (s->dirty_bitmap, op->offset >>BDRV_SECTOR_BITS , - op->bytes >>BDRV_SECTOR_BITS ); +bdrv_set_dirty_bitmap (s->dirty_bitmap, op->offset, op->bytes); action =mirror_error_action (s, true, -ret);if (action ==BLOCK_ERROR_ACTION_REPORT && s->ret >=0 ) { s->ret = ret;
@@ -382,8 +380,8 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)* calling bdrv_get_block_status_above could yield - if some blocks are * marked dirty in this window, we need to know. */ -bdrv_reset_dirty_bitmap_locked (s->dirty_bitmap, offset >>BDRV_SECTOR_BITS , - nb_chunks * sectors_per_chunk); +bdrv_reset_dirty_bitmap_locked (s->dirty_bitmap, offset, + nb_chunks * s->granularity);bdrv_dirty_bitmap_unlock (s->dirty_bitmap);bitmap_set (s->in_flight_bitmap, offset / s->granularity, nb_chunks);
@@ -625,7 +623,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)if (base ==NULL && !bdrv_has_zero_init (target_bs)) {if (!bdrv_can_write_zeroes_with_unmap (target_bs)) { -bdrv_set_dirty_bitmap (s->dirty_bitmap,0 , end); +bdrv_set_dirty_bitmap (s->dirty_bitmap,0 , s->bdev_length);return 0 ; }
@@ -681,7 +679,9 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) n = count >>BDRV_SECTOR_BITS ;assert (n >0 );if (ret ==1 ) { -bdrv_set_dirty_bitmap (s->dirty_bitmap, sector_num, n); +bdrv_set_dirty_bitmap (s->dirty_bitmap, + sector_num *BDRV_SECTOR_SIZE , + n *BDRV_SECTOR_SIZE ); } sector_num += n; }
migration/block.c +5 -2
@@ -329,7 +329,8 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) blk->aiocb =blk_aio_preadv (bb, cur_sector *BDRV_SECTOR_SIZE , &blk->qiov,0 , blk_mig_read_cb, blk); -bdrv_reset_dirty_bitmap (bmds->dirty_bitmap, cur_sector, nr_sectors); +bdrv_reset_dirty_bitmap (bmds->dirty_bitmap, cur_sector *BDRV_SECTOR_SIZE , + nr_sectors *BDRV_SECTOR_SIZE );aio_context_release (blk_get_aio_context (bmds->blk));qemu_mutex_unlock_iothread ();
@@ -537,7 +538,9 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, }else { nr_sectors =BDRV_SECTORS_PER_DIRTY_CHUNK ; } -bdrv_reset_dirty_bitmap_locked (bmds->dirty_bitmap, sector, nr_sectors); +bdrv_reset_dirty_bitmap_locked (bmds->dirty_bitmap, + sector *BDRV_SECTOR_SIZE , + nr_sectors *BDRV_SECTOR_SIZE );bdrv_dirty_bitmap_unlock (bmds->dirty_bitmap); blk =g_new (BlkMigBlock,1 );
Signature · 1 line
2.13.5
PATCH v9 15/20 mirror: Switch mirror_dirty_init() to byte-based iteration
In reply to Eric BlakeNow that we have adjusted the majority of the calls this function makes to be byte-based, it is easier to read the code if it makes passes over the image using bytes rather than sectors.
---
v6: no change
v5: rebase to earlier changes
v2-v4: no change
--- block/mirror.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-)
block/mirror.c +14 -24
@@ -612,15 +612,13 @@ static void mirror_throttle(MirrorBlockJob *s) static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) { -int64_t sector_num, end; +int64_t offset;BlockDriverState *base = s->base;BlockDriverState *bs = s->source;BlockDriverState *target_bs =blk_bs (s->target); -int ret, n; +int ret;int64_t count; - end = s->bdev_length /BDRV_SECTOR_SIZE ; -if (base ==NULL && !bdrv_has_zero_init (target_bs)) {if (!bdrv_can_write_zeroes_with_unmap (target_bs)) {bdrv_set_dirty_bitmap (s->dirty_bitmap,0 , s->bdev_length);
@@ -628,9 +626,9 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) } s->initial_zeroing_ongoing = true; -for (sector_num =0 ; sector_num < end; ) { -int nb_sectors =MIN (end - sector_num, -QEMU_ALIGN_DOWN (INT_MAX , s->granularity) >>BDRV_SECTOR_BITS ); +for (offset =0 ; offset < s->bdev_length; ) { +int bytes =MIN (s->bdev_length - offset, +QEMU_ALIGN_DOWN (INT_MAX , s->granularity));mirror_throttle (s);
@@ -646,9 +644,8 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)continue ; } -mirror_do_zero_or_discard (s, sector_num *BDRV_SECTOR_SIZE , - nb_sectors *BDRV_SECTOR_SIZE , false); - sector_num += nb_sectors; +mirror_do_zero_or_discard (s, offset, bytes, false); + offset += bytes; }mirror_wait_for_all_io (s);
@@ -656,10 +653,10 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) }/* First part, loop on the sectors and initialize the dirty bitmap. */ -for (sector_num =0 ; sector_num < end; ) { +for (offset =0 ; offset < s->bdev_length; ) {/* Just to make sure we are not exceeding int limit. */ -int nb_sectors =MIN (INT_MAX >>BDRV_SECTOR_BITS , - end - sector_num); +int bytes =MIN (s->bdev_length - offset, +QEMU_ALIGN_DOWN (INT_MAX , s->granularity));mirror_throttle (s);
@@ -667,23 +664,16 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)return 0 ; } - ret =bdrv_is_allocated_above (bs, base, sector_num *BDRV_SECTOR_SIZE , - nb_sectors *BDRV_SECTOR_SIZE , &count); + ret =bdrv_is_allocated_above (bs, base, offset, bytes, &count);if (ret <0 ) {return ret; } -/* TODO: Relax this once bdrv_is_allocated_above and dirty -* bitmaps no longer require sector alignment. */ -assert (QEMU_IS_ALIGNED (count,BDRV_SECTOR_SIZE )); - n = count >>BDRV_SECTOR_BITS ; -assert (n >0 ); +assert (count);if (ret ==1 ) { -bdrv_set_dirty_bitmap (s->dirty_bitmap, - sector_num *BDRV_SECTOR_SIZE , - n *BDRV_SECTOR_SIZE ); +bdrv_set_dirty_bitmap (s->dirty_bitmap, offset, count); } - sector_num += n; + offset += count; }return 0 ; }
Signature · 1 line
2.13.5
PATCH v9 16/20 qcow2: Switch qcow2_measure() to byte-based iteration
In reply to Eric BlakeThis is new code, but it is easier to read if it makes passes over the image using bytes rather than sectors (and will get easier in the future when bdrv_get_block_status is converted to byte-based).
---
v7: tweak constant given to MIN (no semantic change, R-b kept) [Kevin]
v6: separate bug fix to earlier patch
v5: new patch
--- block/qcow2.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-)
block/qcow2.c +10 -12
@@ -3647,20 +3647,19 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,*/ required = virtual_size; }else { -int cluster_sectors = cluster_size /BDRV_SECTOR_SIZE ; -int64_t sector_num; +int64_t offset;int pnum =0 ; -for (sector_num =0 ; - sector_num < ssize /BDRV_SECTOR_SIZE ; - sector_num += pnum) { -int nb_sectors =MIN (ssize /BDRV_SECTOR_SIZE - sector_num, -BDRV_REQUEST_MAX_SECTORS ); +for (offset =0 ; offset < ssize; + offset += pnum *BDRV_SECTOR_SIZE ) { +int nb_sectors =MIN (ssize - offset, +BDRV_REQUEST_MAX_BYTES ) /BDRV_SECTOR_SIZE ;BlockDriverState *file;int64_t ret; ret =bdrv_get_block_status_above (in_bs,NULL , - sector_num, nb_sectors, + offset >>BDRV_SECTOR_BITS , + nb_sectors, &pnum, &file);if (ret <0 ) { error_setg_errno(&local_err, -ret,
@@ -3673,12 +3672,11 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, }else if ((ret & (BDRV_BLOCK_DATA |BDRV_BLOCK_ALLOCATED )) == (BDRV_BLOCK_DATA |BDRV_BLOCK_ALLOCATED )) {/* Extend pnum to end of cluster for next iteration */ - pnum =ROUND_UP (sector_num + pnum, cluster_sectors) - - sector_num; + pnum = (ROUND_UP (offset + pnum *BDRV_SECTOR_SIZE , + cluster_size) - offset) >>BDRV_SECTOR_BITS ;/* Count clusters we've seen */ - required += (sector_num % cluster_sectors + pnum) * -BDRV_SECTOR_SIZE ; + required += offset % cluster_size + pnum *BDRV_SECTOR_SIZE ; } } }
Signature · 1 line
2.13.5
PATCH v9 17/20 qcow2: Switch load_bitmap_data() to byte-based iteration
In reply to Eric BlakeNow that we have adjusted the majority of the calls this function makes to be byte-based, it is easier to read the code if it makes passes over the image using bytes rather than sectors.
---
v5: no change
v4: new patch
--- block/qcow2-bitmap.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-)
block/qcow2-bitmap.c +8 -14
@@ -291,9 +291,8 @@ static int load_bitmap_data(BlockDriverState *bs, {int ret =0 ;BDRVQcow2State *s = bs->opaque; -uint64_t sector, limit, sbc; +uint64_t offset, limit;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap); -uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );uint8_t *buf =NULL ;uint64_t i, tab_size = size_to_clusters(s,
@@ -305,32 +304,27 @@ static int load_bitmap_data(BlockDriverState *bs, buf =g_malloc (s->cluster_size); limit =bytes_covered_by_bitmap_cluster (s, bitmap); - sbc = limit >>BDRV_SECTOR_BITS ; -for (i =0 , sector =0 ; i < tab_size; ++i, sector += sbc) { -uint64_t count =MIN (bm_sectors - sector, sbc); +for (i =0 , offset =0 ; i < tab_size; ++i, offset += limit) { +uint64_t count =MIN (bm_size - offset, limit);uint64_t entry = bitmap_table[i]; -uint64_t offset = entry &BME_TABLE_ENTRY_OFFSET_MASK ; +uint64_t data_offset = entry &BME_TABLE_ENTRY_OFFSET_MASK ;assert (check_table_entry (entry, s->cluster_size) ==0 ); -if (offset ==0 ) { +if (data_offset ==0 ) {if (entry &BME_TABLE_ENTRY_FLAG_ALL_ONES ) { -bdrv_dirty_bitmap_deserialize_ones (bitmap, - sector *BDRV_SECTOR_SIZE , - count *BDRV_SECTOR_SIZE , +bdrv_dirty_bitmap_deserialize_ones (bitmap, offset, count, false); }else {/* No need to deserialize zeros because the dirty bitmap is * already cleared */ } }else { - ret =bdrv_pread (bs->file, offset, buf, s->cluster_size); + ret =bdrv_pread (bs->file, data_offset, buf, s->cluster_size);if (ret <0 ) { goto finish; } -bdrv_dirty_bitmap_deserialize_part (bitmap, buf, - sector *BDRV_SECTOR_SIZE , - count *BDRV_SECTOR_SIZE , +bdrv_dirty_bitmap_deserialize_part (bitmap, buf, offset, count, false); } }
Signature · 1 line
2.13.5
PATCH v9 18/20 qcow2: Switch store_bitmap_data() to byte-based iteration
In reply to Eric BlakeNow that we have adjusted the majority of the calls this function makes to be byte-based, it is easier to read the code if it makes passes over the image using bytes rather than sectors.
iotests 165 was rather weak - on a default 64k-cluster image, where bitmap granularity also defaults to 64k bytes, a single cluster of the bitmap table thus covers (64*1024*8) bits which each cover 64k bytes, or 32G of image space. But the test only uses a 1G image, so it cannot trigger any more than one loop of the code in store_bitmap_data(); and it was writing to the first cluster. In order to test that we are properly aligning which portions of the bitmap are being written to the file, we really want to test a case where the first dirty bit returned by bdrv_dirty_iter_next() is not aligned to the start of a cluster, which we can do by modifying the test to write data that doesn't happen to fall in the first cluster of the image.
---
v9: update iotests to show why aligning down is needed [Kevin], R-b dropped
v8: no change
v7: rebase to earlier change, make rounding of offset obvious (no semantic change, so R-b kept) [Kevin]
v5-v6: no change
v4: new patch
--- block/qcow2-bitmap.c | 31 ++++++++++++++++--------------- tests/qemu-iotests/165 | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-)
block/qcow2-bitmap.c +16 -15
@@ -1072,10 +1072,9 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, {int ret;BDRVQcow2State *s = bs->opaque; -int64_t sector; -uint64_t limit, sbc; +int64_t offset; +uint64_t limit;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap); -uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );const char *bm_name =bdrv_dirty_bitmap_name (bitmap);uint8_t *buf =NULL ;BdrvDirtyBitmapIter *dbi;
@@ -1100,18 +1099,22 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, dbi =bdrv_dirty_iter_new (bitmap); buf =g_malloc (s->cluster_size); limit =bytes_covered_by_bitmap_cluster (s, bitmap); - sbc = limit >>BDRV_SECTOR_BITS ;assert (DIV_ROUND_UP (bm_size, limit) == tb_size); -while ((sector =bdrv_dirty_iter_next (dbi) >>BDRV_SECTOR_BITS ) >=0 ) { -uint64_t cluster = sector / sbc; +while ((offset =bdrv_dirty_iter_next (dbi)) >=0 ) { +uint64_t cluster = offset / limit;uint64_t end, write_size;int64_t off; - sector = cluster * sbc; - end =MIN (bm_sectors, sector + sbc); - write_size =bdrv_dirty_bitmap_serialization_size (bitmap, - sector *BDRV_SECTOR_SIZE , (end - sector) *BDRV_SECTOR_SIZE ); +/* +* We found the first dirty offset, but want to write out the +* entire cluster of the bitmap that includes that offset, +* including any leading zero bits. +*/ + offset =QEMU_ALIGN_DOWN (offset, limit); + end =MIN (bm_size, offset + limit); + write_size =bdrv_dirty_bitmap_serialization_size (bitmap, offset, + end - offset);assert (write_size <= s->cluster_size); off =qcow2_alloc_clusters (bs, s->cluster_size);
@@ -1123,9 +1126,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, } tb[cluster] = off; -bdrv_dirty_bitmap_serialize_part (bitmap, buf, - sector *BDRV_SECTOR_SIZE , - (end - sector) *BDRV_SECTOR_SIZE ); +bdrv_dirty_bitmap_serialize_part (bitmap, buf, offset, end - offset);if (write_size < s->cluster_size) {memset (buf + write_size,0 , s->cluster_size - write_size); }
@@ -1143,11 +1144,11 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } -if (end >= bm_sectors) { +if (end >= bm_size) {break ; } -bdrv_set_dirty_iter (dbi, end *BDRV_SECTOR_SIZE ); +bdrv_set_dirty_iter (dbi, end); } *bitmap_table_size = tb_size;
tests/qemu-iotests/165 +1 -1
@@ -27,7 +27,7 @@ disk = os.path.join(iotests.test_dir, 'disk') disk_size = 0x40000000 # 1G # regions for qemu_io: (start, count) in bytes -regions1 = ((0, 0x100000), +regions1 = ((0x0fff00, 0x10000), (0x200000, 0x100000)) regions2 = ((0x10000000, 0x20000),
Signature · 1 line
2.13.5
Re: [Qemu-devel] [PATCH v9 18/20] qcow2: Switch store_bitmap_data() to byte-based iteration
Quoted 17 lines · Eric Blake
Now that we have adjusted the majority of the calls this function makes to be byte-based, it is easier to read the code if it makes passes over the image using bytes rather than sectors.
iotests 165 was rather weak - on a default 64k-cluster image, where bitmap granularity also defaults to 64k bytes, a single cluster of the bitmap table thus covers (64*1024*8) bits which each cover 64k bytes, or 32G of image space. But the test only uses a 1G image, so it cannot trigger any more than one loop of the code in
store_bitmap_data(); and it was writing to the first cluster. In order to test that we are properly aligning which portions of the bitmap are being written to the file, we really want to test a case where the first dirty bit returned bybdrv_dirty_iter_next()is not aligned to the start of a cluster, which we can do by modifying the test to write data that doesn't happen to fall in the first cluster of the image.
Quoted 1 line
Signed-off-by Eric Blake <[email protected]>
Quoted 98 lines
---
v9: update iotests to show why aligning down is needed [Kevin], R-b dropped
v8: no change
v7: rebase to earlier change, make rounding of offset obvious (no semantic change, so R-b kept) [Kevin]
v5-v6: no change
v4: new patch--- block/qcow2-bitmap.c | 31 ++++++++++++++++--------------- tests/qemu-iotests/165 | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-)block/qcow2-bitmap.c +16
-15@@ -1072,10 +1072,9 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, {int ret;BDRVQcow2State *s = bs->opaque; -int64_t sector; -uint64_t limit, sbc; +int64_t offset; +uint64_t limit;uint64_t bm_size =bdrv_dirty_bitmap_size (bitmap); -uint64_t bm_sectors =DIV_ROUND_UP (bm_size,BDRV_SECTOR_SIZE );const char *bm_name =bdrv_dirty_bitmap_name (bitmap);uint8_t *buf =NULL ;BdrvDirtyBitmapIter *dbi;@@ -1100,18 +1099,22 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, dbi =bdrv_dirty_iter_new (bitmap); buf =g_malloc (s->cluster_size); limit =bytes_covered_by_bitmap_cluster (s, bitmap); - sbc = limit >>BDRV_SECTOR_BITS ;assert (DIV_ROUND_UP (bm_size, limit) == tb_size); -while ((sector =bdrv_dirty_iter_next (dbi) >>BDRV_SECTOR_BITS ) >=0 ) { -uint64_t cluster = sector / sbc; +while ((offset =bdrv_dirty_iter_next (dbi)) >=0 ) { +uint64_t cluster = offset / limit;uint64_t end, write_size;int64_t off; - sector = cluster * sbc; - end =MIN (bm_sectors, sector + sbc); - write_size =bdrv_dirty_bitmap_serialization_size (bitmap, - sector *BDRV_SECTOR_SIZE , (end - sector) *BDRV_SECTOR_SIZE ); +/* +* We found the first dirty offset, but want to write out the +* entire cluster of the bitmap that includes that offset, +* including any leading zero bits. +*/ + offset =QEMU_ALIGN_DOWN (offset, limit); + end =MIN (bm_size, offset + limit); + write_size =bdrv_dirty_bitmap_serialization_size (bitmap, offset, + end - offset);assert (write_size <= s->cluster_size); off =qcow2_alloc_clusters (bs, s->cluster_size);@@ -1123,9 +1126,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, } tb[cluster] = off; -bdrv_dirty_bitmap_serialize_part (bitmap, buf, - sector *BDRV_SECTOR_SIZE , - (end - sector) *BDRV_SECTOR_SIZE ); +bdrv_dirty_bitmap_serialize_part (bitmap, buf, offset, end - offset);if (write_size < s->cluster_size) {memset (buf + write_size,0 , s->cluster_size - write_size); }@@ -1143,11 +1144,11 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } -if (end >= bm_sectors) { +if (end >= bm_size) {break ; } -bdrv_set_dirty_iter (dbi, end *BDRV_SECTOR_SIZE ); +bdrv_set_dirty_iter (dbi, end); } *bitmap_table_size = tb_size;tests/qemu-iotests/165 +1
-1@@ -27,7 +27,7 @@ disk = os.path.join(iotests.test_dir, 'disk') disk_size = 0x40000000 # 1G # regions for qemu_io: (start, count) in bytes -regions1 = ((0, 0x100000), +regions1 = ((0x0fff00, 0x10000), (0x200000, 0x100000)) regions2 = ((0x10000000, 0x20000),
Signature · 2 lines
Best regards, Vladimir
PATCH v9 19/20 dirty-bitmap: Switch bdrv_set_dirty() to bytes
In reply to Eric BlakeBoth callers already had bytes available, but were scaling to sectors. Move the scaling to internal code. In the case of bdrv_aligned_pwritev(), we are now passing the exact offset rather than a rounded sector-aligned value, but that's okay as long as dirty bitmap widens start/bytes to granularity boundaries.
---
v4: only context changes
v3: rebase to lock context changes, R-b kept
v2: no change
--- include/block/block_int.h | 2 +- block/io.c | 6 ++---- block/dirty-bitmap.c | 7 ++++--- 3 files changed, 7 insertions(+), 8 deletions(-)
include/block/block_int.h +1 -1
@@ -1021,7 +1021,7 @@ void blk_dev_eject_request(BlockBackend *blk, bool force);bool blk_dev_is_tray_open (BlockBackend *blk);bool blk_dev_is_medium_locked (BlockBackend *blk); -void bdrv_set_dirty (BlockDriverState *bs,int64_t cur_sector,int64_t nr_sect); +void bdrv_set_dirty (BlockDriverState *bs,int64_t offset,int64_t bytes);bool bdrv_requests_pending (BlockDriverState *bs);void bdrv_clear_dirty_bitmap (BdrvDirtyBitmap *bitmap,HBitmap **out);
block/io.c +2 -4
@@ -1334,7 +1334,6 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,bool waited;int ret; -int64_t start_sector = offset >>BDRV_SECTOR_BITS ;int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE );uint64_t bytes_remaining = bytes;int max_transfer;
@@ -1409,7 +1408,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,bdrv_debug_event (bs,BLKDBG_PWRITEV_DONE );atomic_inc (&bs->write_gen); -bdrv_set_dirty (bs, start_sector, end_sector - start_sector); +bdrv_set_dirty (bs, offset, bytes);stat64_max (&bs->wr_highest_offset, offset + bytes);
@@ -2438,8 +2437,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, ret =0 ; out:atomic_inc (&bs->write_gen); -bdrv_set_dirty (bs, req.offset >>BDRV_SECTOR_BITS , - req.bytes >>BDRV_SECTOR_BITS ); +bdrv_set_dirty (bs, req.offset, req.bytes);tracked_request_end (&req);bdrv_dec_in_flight (bs);return ret;
block/dirty-bitmap.c +4 -3
@@ -628,10 +628,10 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap)hbitmap_deserialize_finish (bitmap->bitmap); } -void bdrv_set_dirty (BlockDriverState *bs,int64_t cur_sector, -int64_t nr_sectors) +void bdrv_set_dirty (BlockDriverState *bs,int64_t offset,int64_t bytes) {BdrvDirtyBitmap *bitmap; +int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE );if (QLIST_EMPTY (&bs->dirty_bitmaps)) {return ;
@@ -643,7 +643,8 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,continue ; }assert (!bdrv_dirty_bitmap_readonly (bitmap)); -hbitmap_set (bitmap->bitmap, cur_sector, nr_sectors); +hbitmap_set (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , + end_sector - (offset >>BDRV_SECTOR_BITS )); }bdrv_dirty_bitmaps_unlock (bs); }
Signature · 1 line
2.13.5
PATCH v9 20/20 dirty-bitmap: Convert internal hbitmap size/granularity
In reply to Eric BlakeNow that all callers are using byte-based interfaces, there's no reason for our internal hbitmap to remain with sector-based granularity. It also simplifies our internal scaling, since we already know that hbitmap widens requests out to granularity boundaries.
---
v9: no change
v8: rebase to earlier truncate changes (R-b kept)
v7: rebase to dirty_iter_next cleanup (no semantic change, R-b kept)
v6: no change
v5: fix bdrv_dirty_bitmap_truncate [John]
v4: rebase to earlier changes, include serialization, R-b dropped
v3: no change
v2: no change
--- block/dirty-bitmap.c | 62 +++++++++++++++------------------------------------- 1 file changed, 18 insertions(+), 44 deletions(-)
block/dirty-bitmap.c +18 -44
@@ -38,7 +38,7 @@*/ struct BdrvDirtyBitmap {QemuMutex *mutex; -HBitmap *bitmap;/* Dirty sector bitmap implementation */ +HBitmap *bitmap;/* Dirty bitmap implementation */ HBitmap *meta;/* Meta dirty bitmap */ BdrvDirtyBitmap *successor;/* Anonymous child; implies frozen status */ char *name;/* Optional non-empty unique ID */
@@ -130,12 +130,7 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, } bitmap =g_new0 (BdrvDirtyBitmap,1 ); bitmap->mutex = &bs->dirty_bitmap_mutex; -/* -* TODO - let hbitmap track full granularity. For now, it is tracking -* only sector granularity, as a shortcut for our iterators. -*/ - bitmap->bitmap =hbitmap_alloc (DIV_ROUND_UP (bitmap_size,BDRV_SECTOR_SIZE ), -ctz32 (granularity) -BDRV_SECTOR_BITS ); + bitmap->bitmap =hbitmap_alloc (bitmap_size,ctz32 (granularity)); bitmap->size = bitmap_size; bitmap->name =g_strdup (name); bitmap->disabled = false;
@@ -312,7 +307,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {assert (!bdrv_dirty_bitmap_frozen (bitmap));assert (!bitmap->active_iterators); -hbitmap_truncate (bitmap->bitmap,DIV_ROUND_UP (bytes,BDRV_SECTOR_SIZE )); +hbitmap_truncate (bitmap->bitmap, bytes); bitmap->size = bytes; }bdrv_dirty_bitmaps_unlock (bs);
@@ -442,7 +437,7 @@ bool bdrv_get_dirty_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t offset) {if (bitmap) { -return hbitmap_get (bitmap->bitmap, offset >>BDRV_SECTOR_BITS ); +return hbitmap_get (bitmap->bitmap, offset); }else {return false; }
@@ -470,7 +465,7 @@ uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs)uint32_t bdrv_dirty_bitmap_granularity (const BdrvDirtyBitmap *bitmap) { -return BDRV_SECTOR_SIZE <<hbitmap_granularity (bitmap->bitmap); +return 1U <<hbitmap_granularity (bitmap->bitmap); }BdrvDirtyBitmapIter *bdrv_dirty_iter_new (BdrvDirtyBitmap *bitmap)
@@ -503,20 +498,16 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)int64_t bdrv_dirty_iter_next (BdrvDirtyBitmapIter *iter) { -int64_t ret =hbitmap_iter_next (&iter->hbi); -return ret <0 ?-1 : ret *BDRV_SECTOR_SIZE ; +return hbitmap_iter_next (&iter->hbi); }/* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_set_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap,int64_t offset,int64_t bytes) { -int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE ); -assert (bdrv_dirty_bitmap_enabled (bitmap));assert (!bdrv_dirty_bitmap_readonly (bitmap)); -hbitmap_set (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , - end_sector - (offset >>BDRV_SECTOR_BITS )); +hbitmap_set (bitmap->bitmap, offset, bytes); } void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,
@@ -531,12 +522,9 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap,void bdrv_reset_dirty_bitmap_locked (BdrvDirtyBitmap *bitmap,int64_t offset,int64_t bytes) { -int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE ); -assert (bdrv_dirty_bitmap_enabled (bitmap));assert (!bdrv_dirty_bitmap_readonly (bitmap)); -hbitmap_reset (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , - end_sector - (offset >>BDRV_SECTOR_BITS )); +hbitmap_reset (bitmap->bitmap, offset, bytes); } void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
@@ -556,8 +544,7 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)hbitmap_reset_all (bitmap->bitmap); }else {HBitmap *backup = bitmap->bitmap; - bitmap->bitmap =hbitmap_alloc (DIV_ROUND_UP (bitmap->size, -BDRV_SECTOR_SIZE ), + bitmap->bitmap =hbitmap_alloc (bitmap->size,hbitmap_granularity (backup)); *out = backup; }
@@ -576,51 +563,40 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)uint64_t bdrv_dirty_bitmap_serialization_size (const BdrvDirtyBitmap *bitmap,uint64_t offset,uint64_t bytes) { -assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); -return hbitmap_serialization_size (bitmap->bitmap, - offset >>BDRV_SECTOR_BITS , - bytes >>BDRV_SECTOR_BITS ); +return hbitmap_serialization_size (bitmap->bitmap, offset, bytes); }uint64_t bdrv_dirty_bitmap_serialization_align (const BdrvDirtyBitmap *bitmap) { -return hbitmap_serialization_align (bitmap->bitmap) *BDRV_SECTOR_SIZE ; +return hbitmap_serialization_align (bitmap->bitmap); }void bdrv_dirty_bitmap_serialize_part (const BdrvDirtyBitmap *bitmap,uint8_t *buf,uint64_t offset,uint64_t bytes) { -assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); -hbitmap_serialize_part (bitmap->bitmap, buf, offset >>BDRV_SECTOR_BITS , - bytes >>BDRV_SECTOR_BITS ); +hbitmap_serialize_part (bitmap->bitmap, buf, offset, bytes); }void bdrv_dirty_bitmap_deserialize_part (BdrvDirtyBitmap *bitmap,uint8_t *buf,uint64_t offset,uint64_t bytes,bool finish) { -assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); -hbitmap_deserialize_part (bitmap->bitmap, buf, offset >>BDRV_SECTOR_BITS , - bytes >>BDRV_SECTOR_BITS , finish); +hbitmap_deserialize_part (bitmap->bitmap, buf, offset, bytes, finish); }void bdrv_dirty_bitmap_deserialize_zeroes (BdrvDirtyBitmap *bitmap,uint64_t offset,uint64_t bytes,bool finish) { -assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); -hbitmap_deserialize_zeroes (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , - bytes >>BDRV_SECTOR_BITS , finish); +hbitmap_deserialize_zeroes (bitmap->bitmap, offset, bytes, finish); }void bdrv_dirty_bitmap_deserialize_ones (BdrvDirtyBitmap *bitmap,uint64_t offset,uint64_t bytes,bool finish) { -assert (QEMU_IS_ALIGNED (offset | bytes,BDRV_SECTOR_SIZE )); -hbitmap_deserialize_ones (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , - bytes >>BDRV_SECTOR_BITS , finish); +hbitmap_deserialize_ones (bitmap->bitmap, offset, bytes, finish); }void bdrv_dirty_bitmap_deserialize_finish (BdrvDirtyBitmap *bitmap)
@@ -631,7 +607,6 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap)void bdrv_set_dirty (BlockDriverState *bs,int64_t offset,int64_t bytes) {BdrvDirtyBitmap *bitmap; -int64_t end_sector =DIV_ROUND_UP (offset + bytes,BDRV_SECTOR_SIZE );if (QLIST_EMPTY (&bs->dirty_bitmaps)) {return ;
@@ -643,8 +618,7 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes)continue ; }assert (!bdrv_dirty_bitmap_readonly (bitmap)); -hbitmap_set (bitmap->bitmap, offset >>BDRV_SECTOR_BITS , - end_sector - (offset >>BDRV_SECTOR_BITS )); +hbitmap_set (bitmap->bitmap, offset, bytes); }bdrv_dirty_bitmaps_unlock (bs); }
@@ -654,12 +628,12 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes)*/ void bdrv_set_dirty_iter (BdrvDirtyBitmapIter *iter,int64_t offset) { -hbitmap_iter_init (&iter->hbi, iter->hbi.hb, offset >>BDRV_SECTOR_BITS ); +hbitmap_iter_init (&iter->hbi, iter->hbi.hb, offset); }int64_t bdrv_get_dirty_count (BdrvDirtyBitmap *bitmap) { -return hbitmap_count (bitmap->bitmap) <<BDRV_SECTOR_BITS ; +return hbitmap_count (bitmap->bitmap); }int64_t bdrv_get_meta_dirty_count (BdrvDirtyBitmap *bitmap)
Signature · 1 line
2.13.5