Pull request
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAl5yg0AACgkQfe+BBqr8 OQ7uWBAArnxhM0kej5NAQiGsC3sHDOGUszKhsz8qwSOYQ0NkOH9495ksklOuw8T9 v/7CAp4rqvma00nL5D+6Srjc9Pe+9BdIz3aa0xvi+tkXvRvFKcWoZlLyNpK+SCbB FwT5sAzoZ004cijG3xsV8U/1i/jEWiRk0AV8USuGymTkQh0M9XS/MHVlrv0HJU9k 2tLz7FGHBmEGhXFXKjviNsrQfvHl2AzIKIYnOcK6OhF6wevJzRz8ShsYcmhyN7V3 rBm0IDiJxJ54a6oZ8FyukkqL5gj+mZDEoGXnspvWkmixqjmNw0iAWgyWNk8vnBue ROphtzZ7xvATu560vIXnlj9q+new5h1ZEb2Q1Nl6WL0PFHJ0vEmRHnpSj+lUTHFD aEwLL72qk12yZFPwEO9V+tO15mE+9vYbbsfe8SXVIlH52ec+OHGQ3/ytulL2zEDz vVJYy37wOhp8f5TFpdmFXi8ovHV7a8g3Vr6Hh1XmcEW0o/GjEgunjW7V+JHSJlDe XBp5IXsz0+zWn2hCxtjUzX3qBDIswyR89qR/NRJKpuu1D5VSaqVwM3vd5dCgDLHA lnCCi8kcq9rsnRNyeBb0t8d7c6e1HSW8OIHL4OC515VUFdrKSEyxprzDyIMPifDg 9B9ImLXg3CgOQVOXPU/0mbh4beTIeI7uJrmgzvzs3vZc+dLG+H8= =xYlZ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jnsnow/tags/bitmaps-pull-request' into staging Pull request # gpg: Signature made Wed 18 Mar 2020 20:23:28 GMT # gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full] # Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB # Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E * remotes/jnsnow/tags/bitmaps-pull-request: block/qcow2-bitmap: use bdrv_dirty_bitmap_next_dirty nbd/server: use bdrv_dirty_bitmap_next_dirty_area nbd/server: introduce NBDExtentArray block/dirty-bitmap: improve _next_dirty_area API block/dirty-bitmap: add _next_dirty API block/dirty-bitmap: switch _next_dirty_area and _next_zero to int64_t hbitmap: drop meta bitmaps as they are unused hbitmap: unpublish hbitmap_iter_skip_words hbitmap: move hbitmap_iter_next_word to hbitmap.c hbitmap: assert that we don't create bitmap larger than INT64_MAX build: Silence clang warning on older glib autoptr usage Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
e6d567db23
|
@ -860,16 +860,24 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
||||||
return hbitmap_sha256(bitmap->bitmap, errp);
|
return hbitmap_sha256(bitmap->bitmap, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||||
uint64_t bytes)
|
int64_t bytes)
|
||||||
|
{
|
||||||
|
return hbitmap_next_dirty(bitmap->bitmap, offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||||
|
int64_t bytes)
|
||||||
{
|
{
|
||||||
return hbitmap_next_zero(bitmap->bitmap, offset, bytes);
|
return hbitmap_next_zero(bitmap->bitmap, offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
||||||
uint64_t *offset, uint64_t *bytes)
|
int64_t start, int64_t end, int64_t max_dirty_count,
|
||||||
|
int64_t *dirty_start, int64_t *dirty_count)
|
||||||
{
|
{
|
||||||
return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes);
|
return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count,
|
||||||
|
dirty_start, dirty_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1288,7 +1288,6 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
||||||
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
|
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
|
||||||
const char *bm_name = bdrv_dirty_bitmap_name(bitmap);
|
const char *bm_name = bdrv_dirty_bitmap_name(bitmap);
|
||||||
uint8_t *buf = NULL;
|
uint8_t *buf = NULL;
|
||||||
BdrvDirtyBitmapIter *dbi;
|
|
||||||
uint64_t *tb;
|
uint64_t *tb;
|
||||||
uint64_t tb_size =
|
uint64_t tb_size =
|
||||||
size_to_clusters(s,
|
size_to_clusters(s,
|
||||||
|
@ -1307,12 +1306,14 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbi = bdrv_dirty_iter_new(bitmap);
|
|
||||||
buf = g_malloc(s->cluster_size);
|
buf = g_malloc(s->cluster_size);
|
||||||
limit = bytes_covered_by_bitmap_cluster(s, bitmap);
|
limit = bytes_covered_by_bitmap_cluster(s, bitmap);
|
||||||
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
|
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
|
||||||
|
|
||||||
while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
|
offset = 0;
|
||||||
|
while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX))
|
||||||
|
>= 0)
|
||||||
|
{
|
||||||
uint64_t cluster = offset / limit;
|
uint64_t cluster = offset / limit;
|
||||||
uint64_t end, write_size;
|
uint64_t end, write_size;
|
||||||
int64_t off;
|
int64_t off;
|
||||||
|
@ -1355,23 +1356,17 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end >= bm_size) {
|
offset = end;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bdrv_set_dirty_iter(dbi, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*bitmap_table_size = tb_size;
|
*bitmap_table_size = tb_size;
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
bdrv_dirty_iter_free(dbi);
|
|
||||||
|
|
||||||
return tb;
|
return tb;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
clear_bitmap_table(bs, tb, tb_size);
|
clear_bitmap_table(bs, tb, tb_size);
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
bdrv_dirty_iter_free(dbi);
|
|
||||||
g_free(tb);
|
g_free(tb);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
20
configure
vendored
20
configure
vendored
|
@ -3855,6 +3855,26 @@ if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Silence clang warnings triggered by glib < 2.57.2
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
#include <glib.h>
|
||||||
|
typedef struct Foo {
|
||||||
|
int i;
|
||||||
|
} Foo;
|
||||||
|
static void foo_free(Foo *f)
|
||||||
|
{
|
||||||
|
g_free(f);
|
||||||
|
}
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free);
|
||||||
|
int main(void) { return 0; }
|
||||||
|
EOF
|
||||||
|
if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
|
||||||
|
if cc_has_warning_flag "-Wno-unused-function"; then
|
||||||
|
glib_cflags="$glib_cflags -Wno-unused-function"
|
||||||
|
CFLAGS="$CFLAGS -Wno-unused-function"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# zlib check
|
# zlib check
|
||||||
|
|
||||||
|
|
|
@ -105,10 +105,13 @@ for (bitmap = bdrv_dirty_bitmap_first(bs); bitmap; \
|
||||||
bitmap = bdrv_dirty_bitmap_next(bitmap))
|
bitmap = bdrv_dirty_bitmap_next(bitmap))
|
||||||
|
|
||||||
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
|
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
|
||||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||||
uint64_t bytes);
|
int64_t bytes);
|
||||||
|
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||||
|
int64_t bytes);
|
||||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
||||||
uint64_t *offset, uint64_t *bytes);
|
int64_t start, int64_t end, int64_t max_dirty_count,
|
||||||
|
int64_t *dirty_start, int64_t *dirty_count);
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
|
|
@ -297,12 +297,18 @@ void hbitmap_free(HBitmap *hb);
|
||||||
*/
|
*/
|
||||||
void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first);
|
void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first);
|
||||||
|
|
||||||
/* hbitmap_iter_skip_words:
|
/*
|
||||||
* @hbi: HBitmapIter to operate on.
|
* hbitmap_next_dirty:
|
||||||
*
|
*
|
||||||
* Internal function used by hbitmap_iter_next and hbitmap_iter_next_word.
|
* Find next dirty bit within selected range. If not found, return -1.
|
||||||
|
*
|
||||||
|
* @hb: The HBitmap to operate on
|
||||||
|
* @start: The bit to start from.
|
||||||
|
* @count: Number of bits to proceed. If @start+@count > bitmap size, the whole
|
||||||
|
* bitmap is looked through. You can use INT64_MAX as @count to search up to
|
||||||
|
* the bitmap end.
|
||||||
*/
|
*/
|
||||||
unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi);
|
int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count);
|
||||||
|
|
||||||
/* hbitmap_next_zero:
|
/* hbitmap_next_zero:
|
||||||
*
|
*
|
||||||
|
@ -311,47 +317,28 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi);
|
||||||
* @hb: The HBitmap to operate on
|
* @hb: The HBitmap to operate on
|
||||||
* @start: The bit to start from.
|
* @start: The bit to start from.
|
||||||
* @count: Number of bits to proceed. If @start+@count > bitmap size, the whole
|
* @count: Number of bits to proceed. If @start+@count > bitmap size, the whole
|
||||||
* bitmap is looked through. You can use UINT64_MAX as @count to search up to
|
* bitmap is looked through. You can use INT64_MAX as @count to search up to
|
||||||
* the bitmap end.
|
* the bitmap end.
|
||||||
*/
|
*/
|
||||||
int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count);
|
int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count);
|
||||||
|
|
||||||
/* hbitmap_next_dirty_area:
|
/* hbitmap_next_dirty_area:
|
||||||
* @hb: The HBitmap to operate on
|
* @hb: The HBitmap to operate on
|
||||||
* @start: in-out parameter.
|
* @start: the offset to start from
|
||||||
* in: the offset to start from
|
* @end: end of requested area
|
||||||
* out: (if area found) start of found area
|
* @max_dirty_count: limit for out parameter dirty_count
|
||||||
* @count: in-out parameter.
|
* @dirty_start: on success: start of found area
|
||||||
* in: length of requested region
|
* @dirty_count: on success: length of found area
|
||||||
* out: length of found area
|
|
||||||
*
|
*
|
||||||
* If dirty area found within [@start, @start + @count), returns true and sets
|
* If dirty area found within [@start, @end), returns true and sets
|
||||||
* @offset and @bytes appropriately. Otherwise returns false and leaves @offset
|
* @dirty_start and @dirty_count appropriately. @dirty_count will not exceed
|
||||||
* and @bytes unchanged.
|
* @max_dirty_count.
|
||||||
|
* If dirty area was not found, returns false and leaves @dirty_start and
|
||||||
|
* @dirty_count unchanged.
|
||||||
*/
|
*/
|
||||||
bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start,
|
bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end,
|
||||||
uint64_t *count);
|
int64_t max_dirty_count,
|
||||||
|
int64_t *dirty_start, int64_t *dirty_count);
|
||||||
/* hbitmap_create_meta:
|
|
||||||
* Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap.
|
|
||||||
* The caller owns the created bitmap and must call hbitmap_free_meta(hb) to
|
|
||||||
* free it.
|
|
||||||
*
|
|
||||||
* Currently, we only guarantee that if a bit in the hbitmap is changed it
|
|
||||||
* will be reflected in the meta bitmap, but we do not yet guarantee the
|
|
||||||
* opposite.
|
|
||||||
*
|
|
||||||
* @hb: The HBitmap to operate on.
|
|
||||||
* @chunk_size: How many bits in @hb does one bit in the meta track.
|
|
||||||
*/
|
|
||||||
HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size);
|
|
||||||
|
|
||||||
/* hbitmap_free_meta:
|
|
||||||
* Free the meta bitmap of @hb.
|
|
||||||
*
|
|
||||||
* @hb: The HBitmap whose meta bitmap should be freed.
|
|
||||||
*/
|
|
||||||
void hbitmap_free_meta(HBitmap *hb);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hbitmap_iter_next:
|
* hbitmap_iter_next:
|
||||||
|
@ -362,34 +349,4 @@ void hbitmap_free_meta(HBitmap *hb);
|
||||||
*/
|
*/
|
||||||
int64_t hbitmap_iter_next(HBitmapIter *hbi);
|
int64_t hbitmap_iter_next(HBitmapIter *hbi);
|
||||||
|
|
||||||
/**
|
|
||||||
* hbitmap_iter_next_word:
|
|
||||||
* @hbi: HBitmapIter to operate on.
|
|
||||||
* @p_cur: Location where to store the next non-zero word.
|
|
||||||
*
|
|
||||||
* Return the index of the next nonzero word that is set in @hbi's
|
|
||||||
* associated HBitmap, and set *p_cur to the content of that word
|
|
||||||
* (bits before the index that was passed to hbitmap_iter_init are
|
|
||||||
* trimmed on the first call). Return -1, and set *p_cur to zero,
|
|
||||||
* if all remaining words are zero.
|
|
||||||
*/
|
|
||||||
static inline size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur)
|
|
||||||
{
|
|
||||||
unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1];
|
|
||||||
|
|
||||||
if (cur == 0) {
|
|
||||||
cur = hbitmap_iter_skip_words(hbi);
|
|
||||||
if (cur == 0) {
|
|
||||||
*p_cur = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The next call will resume work from the next word. */
|
|
||||||
hbi->cur[HBITMAP_LEVELS - 1] = 0;
|
|
||||||
*p_cur = cur;
|
|
||||||
return hbi->pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
257
nbd/server.c
257
nbd/server.c
|
@ -1909,27 +1909,98 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
typedef struct NBDExtentArray {
|
||||||
* Populate @extents from block status. Update @bytes to be the actual
|
NBDExtent *extents;
|
||||||
* length encoded (which may be smaller than the original), and update
|
unsigned int nb_alloc;
|
||||||
* @nb_extents to the number of extents used.
|
unsigned int count;
|
||||||
*
|
uint64_t total_length;
|
||||||
* Returns zero on success and -errno on bdrv_block_status_above failure.
|
bool can_add;
|
||||||
*/
|
bool converted_to_be;
|
||||||
static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
} NBDExtentArray;
|
||||||
uint64_t *bytes, NBDExtent *extents,
|
|
||||||
unsigned int *nb_extents)
|
|
||||||
{
|
|
||||||
uint64_t remaining_bytes = *bytes;
|
|
||||||
NBDExtent *extent = extents, *extents_end = extents + *nb_extents;
|
|
||||||
bool first_extent = true;
|
|
||||||
|
|
||||||
assert(*nb_extents);
|
static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc)
|
||||||
while (remaining_bytes) {
|
{
|
||||||
|
NBDExtentArray *ea = g_new0(NBDExtentArray, 1);
|
||||||
|
|
||||||
|
ea->nb_alloc = nb_alloc;
|
||||||
|
ea->extents = g_new(NBDExtent, nb_alloc);
|
||||||
|
ea->can_add = true;
|
||||||
|
|
||||||
|
return ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbd_extent_array_free(NBDExtentArray *ea)
|
||||||
|
{
|
||||||
|
g_free(ea->extents);
|
||||||
|
g_free(ea);
|
||||||
|
}
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free);
|
||||||
|
|
||||||
|
/* Further modifications of the array after conversion are abandoned */
|
||||||
|
static void nbd_extent_array_convert_to_be(NBDExtentArray *ea)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(!ea->converted_to_be);
|
||||||
|
ea->can_add = false;
|
||||||
|
ea->converted_to_be = true;
|
||||||
|
|
||||||
|
for (i = 0; i < ea->count; i++) {
|
||||||
|
ea->extents[i].flags = cpu_to_be32(ea->extents[i].flags);
|
||||||
|
ea->extents[i].length = cpu_to_be32(ea->extents[i].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add extent to NBDExtentArray. If extent can't be added (no available space),
|
||||||
|
* return -1.
|
||||||
|
* For safety, when returning -1 for the first time, .can_add is set to false,
|
||||||
|
* further call to nbd_extent_array_add() will crash.
|
||||||
|
* (to avoid the situation, when after failing to add an extent (returned -1),
|
||||||
|
* user miss this failure and add another extent, which is successfully added
|
||||||
|
* (array is full, but new extent may be squashed into the last one), then we
|
||||||
|
* have invalid array with skipped extent)
|
||||||
|
*/
|
||||||
|
static int nbd_extent_array_add(NBDExtentArray *ea,
|
||||||
|
uint32_t length, uint32_t flags)
|
||||||
|
{
|
||||||
|
assert(ea->can_add);
|
||||||
|
|
||||||
|
if (!length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extend previous extent if flags are the same */
|
||||||
|
if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) {
|
||||||
|
uint64_t sum = (uint64_t)length + ea->extents[ea->count - 1].length;
|
||||||
|
|
||||||
|
if (sum <= UINT32_MAX) {
|
||||||
|
ea->extents[ea->count - 1].length = sum;
|
||||||
|
ea->total_length += length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ea->count >= ea->nb_alloc) {
|
||||||
|
ea->can_add = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ea->total_length += length;
|
||||||
|
ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags};
|
||||||
|
ea->count++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
||||||
|
uint64_t bytes, NBDExtentArray *ea)
|
||||||
|
{
|
||||||
|
while (bytes) {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
int64_t num;
|
int64_t num;
|
||||||
int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
|
int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num,
|
||||||
&num, NULL, NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1938,60 +2009,37 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
||||||
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
|
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
|
||||||
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
||||||
|
|
||||||
if (first_extent) {
|
if (nbd_extent_array_add(ea, num, flags) < 0) {
|
||||||
extent->flags = flags;
|
return 0;
|
||||||
extent->length = num;
|
|
||||||
first_extent = false;
|
|
||||||
} else if (flags == extent->flags) {
|
|
||||||
/* extend current extent */
|
|
||||||
extent->length += num;
|
|
||||||
} else {
|
|
||||||
if (extent + 1 == extents_end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* start new extent */
|
|
||||||
extent++;
|
|
||||||
extent->flags = flags;
|
|
||||||
extent->length = num;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += num;
|
offset += num;
|
||||||
remaining_bytes -= num;
|
bytes -= num;
|
||||||
}
|
}
|
||||||
|
|
||||||
extents_end = extent + 1;
|
|
||||||
|
|
||||||
for (extent = extents; extent < extents_end; extent++) {
|
|
||||||
extent->flags = cpu_to_be32(extent->flags);
|
|
||||||
extent->length = cpu_to_be32(extent->length);
|
|
||||||
}
|
|
||||||
|
|
||||||
*bytes -= remaining_bytes;
|
|
||||||
*nb_extents = extents_end - extents;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nbd_co_send_extents
|
/*
|
||||||
|
* nbd_co_send_extents
|
||||||
*
|
*
|
||||||
* @length is only for tracing purposes (and may be smaller or larger
|
* @ea is converted to BE by the function
|
||||||
* than the client's original request). @last controls whether
|
* @last controls whether NBD_REPLY_FLAG_DONE is sent.
|
||||||
* NBD_REPLY_FLAG_DONE is sent. @extents should already be in
|
|
||||||
* big-endian format.
|
|
||||||
*/
|
*/
|
||||||
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
||||||
NBDExtent *extents, unsigned int nb_extents,
|
NBDExtentArray *ea,
|
||||||
uint64_t length, bool last,
|
bool last, uint32_t context_id, Error **errp)
|
||||||
uint32_t context_id, Error **errp)
|
|
||||||
{
|
{
|
||||||
NBDStructuredMeta chunk;
|
NBDStructuredMeta chunk;
|
||||||
|
|
||||||
struct iovec iov[] = {
|
struct iovec iov[] = {
|
||||||
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
||||||
{.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
|
{.iov_base = ea->extents, .iov_len = ea->count * sizeof(ea->extents[0])}
|
||||||
};
|
};
|
||||||
|
|
||||||
trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last);
|
nbd_extent_array_convert_to_be(ea);
|
||||||
|
|
||||||
|
trace_nbd_co_send_extents(handle, ea->count, context_id, ea->total_length,
|
||||||
|
last);
|
||||||
set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0,
|
set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0,
|
||||||
NBD_REPLY_TYPE_BLOCK_STATUS,
|
NBD_REPLY_TYPE_BLOCK_STATUS,
|
||||||
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
||||||
|
@ -2009,82 +2057,47 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
||||||
NBDExtent *extents = g_new(NBDExtent, nb_extents);
|
g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
|
||||||
uint64_t final_length = length;
|
|
||||||
|
|
||||||
ret = blockstatus_to_extents(bs, offset, &final_length, extents,
|
ret = blockstatus_to_extents(bs, offset, length, ea);
|
||||||
&nb_extents);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_free(extents);
|
|
||||||
return nbd_co_send_structured_error(
|
return nbd_co_send_structured_error(
|
||||||
client, handle, -ret, "can't get block status", errp);
|
client, handle, -ret, "can't get block status", errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nbd_co_send_extents(client, handle, extents, nb_extents,
|
return nbd_co_send_extents(client, handle, ea, last, context_id, errp);
|
||||||
final_length, last, context_id, errp);
|
|
||||||
|
|
||||||
g_free(extents);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Populate @ea from a dirty bitmap. */
|
||||||
* Populate @extents from a dirty bitmap. Unless @dont_fragment, the
|
static void bitmap_to_extents(BdrvDirtyBitmap *bitmap,
|
||||||
* final extent may exceed the original @length. Store in @length the
|
uint64_t offset, uint64_t length,
|
||||||
* byte length encoded (which may be smaller or larger than the
|
NBDExtentArray *es)
|
||||||
* original), and return the number of extents used.
|
|
||||||
*/
|
|
||||||
static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
|
||||||
uint64_t *length, NBDExtent *extents,
|
|
||||||
unsigned int nb_extents,
|
|
||||||
bool dont_fragment)
|
|
||||||
{
|
{
|
||||||
uint64_t begin = offset, end = offset;
|
int64_t start, dirty_start, dirty_count;
|
||||||
uint64_t overall_end = offset + *length;
|
int64_t end = offset + length;
|
||||||
unsigned int i = 0;
|
bool full = false;
|
||||||
BdrvDirtyBitmapIter *it;
|
|
||||||
bool dirty;
|
|
||||||
|
|
||||||
bdrv_dirty_bitmap_lock(bitmap);
|
bdrv_dirty_bitmap_lock(bitmap);
|
||||||
|
|
||||||
it = bdrv_dirty_iter_new(bitmap);
|
for (start = offset;
|
||||||
dirty = bdrv_dirty_bitmap_get_locked(bitmap, offset);
|
bdrv_dirty_bitmap_next_dirty_area(bitmap, start, end, INT32_MAX,
|
||||||
|
&dirty_start, &dirty_count);
|
||||||
assert(begin < overall_end && nb_extents);
|
start = dirty_start + dirty_count)
|
||||||
while (begin < overall_end && i < nb_extents) {
|
{
|
||||||
bool next_dirty = !dirty;
|
if ((nbd_extent_array_add(es, dirty_start - start, 0) < 0) ||
|
||||||
|
(nbd_extent_array_add(es, dirty_count, NBD_STATE_DIRTY) < 0))
|
||||||
if (dirty) {
|
{
|
||||||
end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX);
|
full = true;
|
||||||
} else {
|
break;
|
||||||
bdrv_set_dirty_iter(it, begin);
|
|
||||||
end = bdrv_dirty_iter_next(it);
|
|
||||||
}
|
}
|
||||||
if (end == -1 || end - begin > UINT32_MAX) {
|
|
||||||
/* Cap to an aligned value < 4G beyond begin. */
|
|
||||||
end = MIN(bdrv_dirty_bitmap_size(bitmap),
|
|
||||||
begin + UINT32_MAX + 1 -
|
|
||||||
bdrv_dirty_bitmap_granularity(bitmap));
|
|
||||||
next_dirty = dirty;
|
|
||||||
}
|
|
||||||
if (dont_fragment && end > overall_end) {
|
|
||||||
end = overall_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
extents[i].length = cpu_to_be32(end - begin);
|
|
||||||
extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0);
|
|
||||||
i++;
|
|
||||||
begin = end;
|
|
||||||
dirty = next_dirty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_dirty_iter_free(it);
|
if (!full) {
|
||||||
|
/* last non dirty extent */
|
||||||
|
nbd_extent_array_add(es, end - start, 0);
|
||||||
|
}
|
||||||
|
|
||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
|
|
||||||
assert(offset < end);
|
|
||||||
*length = end - offset;
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
||||||
|
@ -2092,20 +2105,12 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
||||||
uint32_t length, bool dont_fragment, bool last,
|
uint32_t length, bool dont_fragment, bool last,
|
||||||
uint32_t context_id, Error **errp)
|
uint32_t context_id, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
||||||
NBDExtent *extents = g_new(NBDExtent, nb_extents);
|
g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
|
||||||
uint64_t final_length = length;
|
|
||||||
|
|
||||||
nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents,
|
bitmap_to_extents(bitmap, offset, length, ea);
|
||||||
nb_extents, dont_fragment);
|
|
||||||
|
|
||||||
ret = nbd_co_send_extents(client, handle, extents, nb_extents,
|
return nbd_co_send_extents(client, handle, ea, last, context_id, errp);
|
||||||
final_length, last, context_id, errp);
|
|
||||||
|
|
||||||
g_free(extents);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nbd_co_receive_request
|
/* nbd_co_receive_request
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
|
|
||||||
typedef struct TestHBitmapData {
|
typedef struct TestHBitmapData {
|
||||||
HBitmap *hb;
|
HBitmap *hb;
|
||||||
HBitmap *meta;
|
|
||||||
unsigned long *bits;
|
unsigned long *bits;
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t old_size;
|
size_t old_size;
|
||||||
|
@ -94,14 +93,6 @@ static void hbitmap_test_init(TestHBitmapData *data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hbitmap_test_init_meta(TestHBitmapData *data,
|
|
||||||
uint64_t size, int granularity,
|
|
||||||
int meta_chunk)
|
|
||||||
{
|
|
||||||
hbitmap_test_init(data, size, granularity);
|
|
||||||
data->meta = hbitmap_create_meta(data->hb, meta_chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t hbitmap_test_array_size(size_t bits)
|
static inline size_t hbitmap_test_array_size(size_t bits)
|
||||||
{
|
{
|
||||||
size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG);
|
size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG);
|
||||||
|
@ -144,9 +135,6 @@ static void hbitmap_test_teardown(TestHBitmapData *data,
|
||||||
const void *unused)
|
const void *unused)
|
||||||
{
|
{
|
||||||
if (data->hb) {
|
if (data->hb) {
|
||||||
if (data->meta) {
|
|
||||||
hbitmap_free_meta(data->hb);
|
|
||||||
}
|
|
||||||
hbitmap_free(data->hb);
|
hbitmap_free(data->hb);
|
||||||
data->hb = NULL;
|
data->hb = NULL;
|
||||||
}
|
}
|
||||||
|
@ -648,96 +636,6 @@ static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data,
|
||||||
hbitmap_test_truncate(data, size, -diff, 0);
|
hbitmap_test_truncate(data, size, -diff, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hbitmap_check_meta(TestHBitmapData *data,
|
|
||||||
int64_t start, int count)
|
|
||||||
{
|
|
||||||
int64_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < data->size; i++) {
|
|
||||||
if (i >= start && i < start + count) {
|
|
||||||
g_assert(hbitmap_get(data->meta, i));
|
|
||||||
} else {
|
|
||||||
g_assert(!hbitmap_get(data->meta, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hbitmap_test_meta(TestHBitmapData *data,
|
|
||||||
int64_t start, int count,
|
|
||||||
int64_t check_start, int check_count)
|
|
||||||
{
|
|
||||||
hbitmap_reset_all(data->hb);
|
|
||||||
hbitmap_reset_all(data->meta);
|
|
||||||
|
|
||||||
/* Test "unset" -> "unset" will not update meta. */
|
|
||||||
hbitmap_reset(data->hb, start, count);
|
|
||||||
hbitmap_check_meta(data, 0, 0);
|
|
||||||
|
|
||||||
/* Test "unset" -> "set" will update meta */
|
|
||||||
hbitmap_set(data->hb, start, count);
|
|
||||||
hbitmap_check_meta(data, check_start, check_count);
|
|
||||||
|
|
||||||
/* Test "set" -> "set" will not update meta */
|
|
||||||
hbitmap_reset_all(data->meta);
|
|
||||||
hbitmap_set(data->hb, start, count);
|
|
||||||
hbitmap_check_meta(data, 0, 0);
|
|
||||||
|
|
||||||
/* Test "set" -> "unset" will update meta */
|
|
||||||
hbitmap_reset_all(data->meta);
|
|
||||||
hbitmap_reset(data->hb, start, count);
|
|
||||||
hbitmap_check_meta(data, check_start, check_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hbitmap_test_meta_do(TestHBitmapData *data, int chunk_size)
|
|
||||||
{
|
|
||||||
uint64_t size = chunk_size * 100;
|
|
||||||
hbitmap_test_init_meta(data, size, 0, chunk_size);
|
|
||||||
|
|
||||||
hbitmap_test_meta(data, 0, 1, 0, chunk_size);
|
|
||||||
hbitmap_test_meta(data, 0, chunk_size, 0, chunk_size);
|
|
||||||
hbitmap_test_meta(data, chunk_size - 1, 1, 0, chunk_size);
|
|
||||||
hbitmap_test_meta(data, chunk_size - 1, 2, 0, chunk_size * 2);
|
|
||||||
hbitmap_test_meta(data, chunk_size - 1, chunk_size + 1, 0, chunk_size * 2);
|
|
||||||
hbitmap_test_meta(data, chunk_size - 1, chunk_size + 2, 0, chunk_size * 3);
|
|
||||||
hbitmap_test_meta(data, 7 * chunk_size - 1, chunk_size + 2,
|
|
||||||
6 * chunk_size, chunk_size * 3);
|
|
||||||
hbitmap_test_meta(data, size - 1, 1, size - chunk_size, chunk_size);
|
|
||||||
hbitmap_test_meta(data, 0, size, 0, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_hbitmap_meta_byte(TestHBitmapData *data, const void *unused)
|
|
||||||
{
|
|
||||||
hbitmap_test_meta_do(data, BITS_PER_BYTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_hbitmap_meta_word(TestHBitmapData *data, const void *unused)
|
|
||||||
{
|
|
||||||
hbitmap_test_meta_do(data, BITS_PER_LONG);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_hbitmap_meta_sector(TestHBitmapData *data, const void *unused)
|
|
||||||
{
|
|
||||||
hbitmap_test_meta_do(data, BDRV_SECTOR_SIZE * BITS_PER_BYTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an HBitmap and test set/unset.
|
|
||||||
*/
|
|
||||||
static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int64_t offsets[] = {
|
|
||||||
0, 1, L1 - 1, L1, L1 + 1, L2 - 1, L2, L2 + 1, L3 - 1, L3, L3 + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
hbitmap_test_init_meta(data, L3 * 2, 0, 1);
|
|
||||||
for (i = 0; i < ARRAY_SIZE(offsets); i++) {
|
|
||||||
hbitmap_test_meta(data, offsets[i], 1, offsets[i], 1);
|
|
||||||
hbitmap_test_meta(data, offsets[i], L1, offsets[i], L1);
|
|
||||||
hbitmap_test_meta(data, offsets[i], L2, offsets[i], L2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_hbitmap_serialize_align(TestHBitmapData *data,
|
static void test_hbitmap_serialize_align(TestHBitmapData *data,
|
||||||
const void *unused)
|
const void *unused)
|
||||||
{
|
{
|
||||||
|
@ -750,13 +648,6 @@ static void test_hbitmap_serialize_align(TestHBitmapData *data,
|
||||||
g_assert_cmpint(r, ==, 64 << 3);
|
g_assert_cmpint(r, ==, 64 << 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_meta_zero(TestHBitmapData *data, const void *unused)
|
|
||||||
{
|
|
||||||
hbitmap_test_init_meta(data, 0, 0, 1);
|
|
||||||
|
|
||||||
hbitmap_check_meta(data, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hbitmap_test_serialize_range(TestHBitmapData *data,
|
static void hbitmap_test_serialize_range(TestHBitmapData *data,
|
||||||
uint8_t *buf, size_t buf_size,
|
uint8_t *buf, size_t buf_size,
|
||||||
uint64_t pos, uint64_t count)
|
uint64_t pos, uint64_t count)
|
||||||
|
@ -925,106 +816,123 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data,
|
||||||
hbitmap_iter_next(&hbi);
|
hbitmap_iter_next(&hbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_zero_check_range(TestHBitmapData *data,
|
static void test_hbitmap_next_x_check_range(TestHBitmapData *data,
|
||||||
uint64_t start,
|
int64_t start,
|
||||||
uint64_t count)
|
int64_t count)
|
||||||
{
|
{
|
||||||
int64_t ret1 = hbitmap_next_zero(data->hb, start, count);
|
int64_t next_zero = hbitmap_next_zero(data->hb, start, count);
|
||||||
int64_t ret2 = start;
|
int64_t next_dirty = hbitmap_next_dirty(data->hb, start, count);
|
||||||
|
int64_t next;
|
||||||
int64_t end = start >= data->size || data->size - start < count ?
|
int64_t end = start >= data->size || data->size - start < count ?
|
||||||
data->size : start + count;
|
data->size : start + count;
|
||||||
|
bool first_bit = hbitmap_get(data->hb, start);
|
||||||
|
|
||||||
for ( ; ret2 < end && hbitmap_get(data->hb, ret2); ret2++) {
|
for (next = start;
|
||||||
|
next < end && hbitmap_get(data->hb, next) == first_bit;
|
||||||
|
next++)
|
||||||
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
if (ret2 == end) {
|
|
||||||
ret2 = -1;
|
if (next == end) {
|
||||||
|
next = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_assert_cmpint(ret1, ==, ret2);
|
g_assert_cmpint(next_dirty, ==, first_bit ? start : next);
|
||||||
|
g_assert_cmpint(next_zero, ==, first_bit ? next : start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start)
|
static void test_hbitmap_next_x_check(TestHBitmapData *data, int64_t start)
|
||||||
{
|
{
|
||||||
test_hbitmap_next_zero_check_range(data, start, UINT64_MAX);
|
test_hbitmap_next_x_check_range(data, start, INT64_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity)
|
static void test_hbitmap_next_x_do(TestHBitmapData *data, int granularity)
|
||||||
{
|
{
|
||||||
hbitmap_test_init(data, L3, granularity);
|
hbitmap_test_init(data, L3, granularity);
|
||||||
test_hbitmap_next_zero_check(data, 0);
|
test_hbitmap_next_x_check(data, 0);
|
||||||
test_hbitmap_next_zero_check(data, L3 - 1);
|
test_hbitmap_next_x_check(data, L3 - 1);
|
||||||
test_hbitmap_next_zero_check_range(data, 0, 1);
|
test_hbitmap_next_x_check_range(data, 0, 1);
|
||||||
test_hbitmap_next_zero_check_range(data, L3 - 1, 1);
|
test_hbitmap_next_x_check_range(data, L3 - 1, 1);
|
||||||
|
|
||||||
hbitmap_set(data->hb, L2, 1);
|
hbitmap_set(data->hb, L2, 1);
|
||||||
test_hbitmap_next_zero_check(data, 0);
|
test_hbitmap_next_x_check(data, 0);
|
||||||
test_hbitmap_next_zero_check(data, L2 - 1);
|
test_hbitmap_next_x_check(data, L2 - 1);
|
||||||
test_hbitmap_next_zero_check(data, L2);
|
test_hbitmap_next_x_check(data, L2);
|
||||||
test_hbitmap_next_zero_check(data, L2 + 1);
|
test_hbitmap_next_x_check(data, L2 + 1);
|
||||||
test_hbitmap_next_zero_check_range(data, 0, 1);
|
test_hbitmap_next_x_check_range(data, 0, 1);
|
||||||
test_hbitmap_next_zero_check_range(data, 0, L2);
|
test_hbitmap_next_x_check_range(data, 0, L2);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 - 1, 1);
|
test_hbitmap_next_x_check_range(data, L2 - 1, 1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 - 1, 2);
|
test_hbitmap_next_x_check_range(data, L2 - 1, 2);
|
||||||
test_hbitmap_next_zero_check_range(data, L2, 1);
|
test_hbitmap_next_x_check_range(data, L2, 1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 + 1, 1);
|
test_hbitmap_next_x_check_range(data, L2 + 1, 1);
|
||||||
|
|
||||||
hbitmap_set(data->hb, L2 + 5, L1);
|
hbitmap_set(data->hb, L2 + 5, L1);
|
||||||
test_hbitmap_next_zero_check(data, 0);
|
test_hbitmap_next_x_check(data, 0);
|
||||||
test_hbitmap_next_zero_check(data, L2 + 1);
|
test_hbitmap_next_x_check(data, L2 - L1);
|
||||||
test_hbitmap_next_zero_check(data, L2 + 2);
|
test_hbitmap_next_x_check(data, L2 + 1);
|
||||||
test_hbitmap_next_zero_check(data, L2 + 5);
|
test_hbitmap_next_x_check(data, L2 + 2);
|
||||||
test_hbitmap_next_zero_check(data, L2 + L1 - 1);
|
test_hbitmap_next_x_check(data, L2 + 5);
|
||||||
test_hbitmap_next_zero_check(data, L2 + L1);
|
test_hbitmap_next_x_check(data, L2 + L1 - 1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2, 6);
|
test_hbitmap_next_x_check(data, L2 + L1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 + 1, 3);
|
test_hbitmap_next_x_check(data, L2 + L1 + 1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 + 4, L1);
|
test_hbitmap_next_x_check_range(data, L2 - 2, L1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 + 5, L1);
|
test_hbitmap_next_x_check_range(data, L2, 4);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2, 6);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 + 1, 3);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 + 4, L1);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 + 5, L1);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 + 5 + L1 - 1, 1);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 + 5 + L1, 1);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 + 5 + L1 + 1, 1);
|
||||||
|
|
||||||
hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2);
|
hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2);
|
||||||
test_hbitmap_next_zero_check(data, L2 * 2 - L1);
|
test_hbitmap_next_x_check(data, L2 * 2 - L1);
|
||||||
test_hbitmap_next_zero_check(data, L2 * 2 - 2);
|
test_hbitmap_next_x_check(data, L2 * 2 - 2);
|
||||||
test_hbitmap_next_zero_check(data, L2 * 2 - 1);
|
test_hbitmap_next_x_check(data, L2 * 2 - 1);
|
||||||
test_hbitmap_next_zero_check(data, L2 * 2);
|
test_hbitmap_next_x_check(data, L2 * 2);
|
||||||
test_hbitmap_next_zero_check(data, L3 - 1);
|
test_hbitmap_next_x_check(data, L2 * 2 + 1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 * 2 - L1, L1 + 1);
|
test_hbitmap_next_x_check(data, L2 * 2 + L1);
|
||||||
test_hbitmap_next_zero_check_range(data, L2 * 2, L2);
|
test_hbitmap_next_x_check(data, L3 - 1);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 * 2 - L1, L1 + 1);
|
||||||
|
test_hbitmap_next_x_check_range(data, L2 * 2, L2);
|
||||||
|
|
||||||
hbitmap_set(data->hb, 0, L3);
|
hbitmap_set(data->hb, 0, L3);
|
||||||
test_hbitmap_next_zero_check(data, 0);
|
test_hbitmap_next_x_check(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_zero_0(TestHBitmapData *data, const void *unused)
|
static void test_hbitmap_next_x_0(TestHBitmapData *data, const void *unused)
|
||||||
{
|
{
|
||||||
test_hbitmap_next_zero_do(data, 0);
|
test_hbitmap_next_x_do(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_zero_4(TestHBitmapData *data, const void *unused)
|
static void test_hbitmap_next_x_4(TestHBitmapData *data, const void *unused)
|
||||||
{
|
{
|
||||||
test_hbitmap_next_zero_do(data, 4);
|
test_hbitmap_next_x_do(data, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_zero_after_truncate(TestHBitmapData *data,
|
static void test_hbitmap_next_x_after_truncate(TestHBitmapData *data,
|
||||||
const void *unused)
|
const void *unused)
|
||||||
{
|
{
|
||||||
hbitmap_test_init(data, L1, 0);
|
hbitmap_test_init(data, L1, 0);
|
||||||
hbitmap_test_truncate_impl(data, L1 * 2);
|
hbitmap_test_truncate_impl(data, L1 * 2);
|
||||||
hbitmap_set(data->hb, 0, L1);
|
hbitmap_set(data->hb, 0, L1);
|
||||||
test_hbitmap_next_zero_check(data, 0);
|
test_hbitmap_next_x_check(data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data,
|
static void test_hbitmap_next_dirty_area_check_limited(TestHBitmapData *data,
|
||||||
uint64_t offset,
|
int64_t offset,
|
||||||
uint64_t count)
|
int64_t count,
|
||||||
|
int64_t max_dirty)
|
||||||
{
|
{
|
||||||
uint64_t off1, off2;
|
int64_t off1, off2;
|
||||||
uint64_t len1 = 0, len2;
|
int64_t len1 = 0, len2;
|
||||||
bool ret1, ret2;
|
bool ret1, ret2;
|
||||||
int64_t end;
|
int64_t end;
|
||||||
|
|
||||||
off1 = offset;
|
ret1 = hbitmap_next_dirty_area(data->hb,
|
||||||
len1 = count;
|
offset, count == INT64_MAX ? INT64_MAX : offset + count, max_dirty,
|
||||||
ret1 = hbitmap_next_dirty_area(data->hb, &off1, &len1);
|
&off1, &len1);
|
||||||
|
|
||||||
end = offset > data->size || data->size - offset < count ? data->size :
|
end = offset > data->size || data->size - offset < count ? data->size :
|
||||||
offset + count;
|
offset + count;
|
||||||
|
@ -1033,45 +941,52 @@ static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data,
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (len2 = 1; off2 + len2 < end && hbitmap_get(data->hb, off2 + len2);
|
for (len2 = 1; (off2 + len2 < end && len2 < max_dirty &&
|
||||||
len2++) {
|
hbitmap_get(data->hb, off2 + len2)); len2++)
|
||||||
|
{
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret2 = off2 < end;
|
ret2 = off2 < end;
|
||||||
if (!ret2) {
|
|
||||||
/* leave unchanged */
|
|
||||||
off2 = offset;
|
|
||||||
len2 = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_assert_cmpint(ret1, ==, ret2);
|
g_assert_cmpint(ret1, ==, ret2);
|
||||||
g_assert_cmpint(off1, ==, off2);
|
|
||||||
g_assert_cmpint(len1, ==, len2);
|
if (ret2) {
|
||||||
|
g_assert_cmpint(off1, ==, off2);
|
||||||
|
g_assert_cmpint(len1, ==, len2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data,
|
||||||
|
int64_t offset, int64_t count)
|
||||||
|
{
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, offset, count, INT64_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data,
|
static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data,
|
||||||
int granularity)
|
int granularity)
|
||||||
{
|
{
|
||||||
hbitmap_test_init(data, L3, granularity);
|
hbitmap_test_init(data, L3, granularity);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, 1);
|
test_hbitmap_next_dirty_area_check(data, 0, 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, L3 - 1, 1);
|
test_hbitmap_next_dirty_area_check(data, L3 - 1, 1);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1);
|
||||||
|
|
||||||
hbitmap_set(data->hb, L2, 1);
|
hbitmap_set(data->hb, L2, 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, 1);
|
test_hbitmap_next_dirty_area_check(data, 0, 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, L2);
|
test_hbitmap_next_dirty_area_check(data, 0, L2);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 - 1, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, L2 - 1, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 - 1, 1);
|
test_hbitmap_next_dirty_area_check(data, L2 - 1, 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 - 1, 2);
|
test_hbitmap_next_dirty_area_check(data, L2 - 1, 2);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 - 1, 3);
|
test_hbitmap_next_dirty_area_check(data, L2 - 1, 3);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2, 1);
|
test_hbitmap_next_dirty_area_check(data, L2, 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 1, 1);
|
test_hbitmap_next_dirty_area_check(data, L2 + 1, 1);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, L2 - 1, 2, 1);
|
||||||
|
|
||||||
hbitmap_set(data->hb, L2 + 5, L1);
|
hbitmap_set(data->hb, L2 + 5, L1);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 - 2, 8);
|
test_hbitmap_next_dirty_area_check(data, L2 - 2, 8);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 1, 5);
|
test_hbitmap_next_dirty_area_check(data, L2 + 1, 5);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 1, 3);
|
test_hbitmap_next_dirty_area_check(data, L2 + 1, 3);
|
||||||
|
@ -1081,18 +996,23 @@ static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data,
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + L1, L1);
|
test_hbitmap_next_dirty_area_check(data, L2 + L1, L1);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2, 0);
|
test_hbitmap_next_dirty_area_check(data, L2, 0);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 1, 0);
|
test_hbitmap_next_dirty_area_check(data, L2 + 1, 0);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, INT64_MAX, 3);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, 7, 10);
|
||||||
|
|
||||||
hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2);
|
hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 1, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, L2 + 1, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, INT64_MAX);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5);
|
test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1);
|
test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, L2 * 2, L2);
|
test_hbitmap_next_dirty_area_check(data, L2 * 2, L2);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, INT64_MAX, 5);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 10, 5);
|
||||||
|
test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 2, 5);
|
||||||
|
|
||||||
hbitmap_set(data->hb, 0, L3);
|
hbitmap_set(data->hb, 0, L3);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data,
|
static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data,
|
||||||
|
@ -1119,7 +1039,7 @@ static void test_hbitmap_next_dirty_area_after_truncate(TestHBitmapData *data,
|
||||||
hbitmap_test_init(data, L1, 0);
|
hbitmap_test_init(data, L1, 0);
|
||||||
hbitmap_test_truncate_impl(data, L1 * 2);
|
hbitmap_test_truncate_impl(data, L1 * 2);
|
||||||
hbitmap_set(data->hb, L1 + 1, 1);
|
hbitmap_set(data->hb, L1 + 1, 1);
|
||||||
test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX);
|
test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
@ -1165,12 +1085,6 @@ int main(int argc, char **argv)
|
||||||
hbitmap_test_add("/hbitmap/truncate/shrink/large",
|
hbitmap_test_add("/hbitmap/truncate/shrink/large",
|
||||||
test_hbitmap_truncate_shrink_large);
|
test_hbitmap_truncate_shrink_large);
|
||||||
|
|
||||||
hbitmap_test_add("/hbitmap/meta/zero", test_hbitmap_meta_zero);
|
|
||||||
hbitmap_test_add("/hbitmap/meta/one", test_hbitmap_meta_one);
|
|
||||||
hbitmap_test_add("/hbitmap/meta/byte", test_hbitmap_meta_byte);
|
|
||||||
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/align",
|
hbitmap_test_add("/hbitmap/serialize/align",
|
||||||
test_hbitmap_serialize_align);
|
test_hbitmap_serialize_align);
|
||||||
hbitmap_test_add("/hbitmap/serialize/basic",
|
hbitmap_test_add("/hbitmap/serialize/basic",
|
||||||
|
@ -1183,12 +1097,12 @@ int main(int argc, char **argv)
|
||||||
hbitmap_test_add("/hbitmap/iter/iter_and_reset",
|
hbitmap_test_add("/hbitmap/iter/iter_and_reset",
|
||||||
test_hbitmap_iter_and_reset);
|
test_hbitmap_iter_and_reset);
|
||||||
|
|
||||||
hbitmap_test_add("/hbitmap/next_zero/next_zero_0",
|
hbitmap_test_add("/hbitmap/next_zero/next_x_0",
|
||||||
test_hbitmap_next_zero_0);
|
test_hbitmap_next_x_0);
|
||||||
hbitmap_test_add("/hbitmap/next_zero/next_zero_4",
|
hbitmap_test_add("/hbitmap/next_zero/next_x_4",
|
||||||
test_hbitmap_next_zero_4);
|
test_hbitmap_next_x_4);
|
||||||
hbitmap_test_add("/hbitmap/next_zero/next_zero_after_truncate",
|
hbitmap_test_add("/hbitmap/next_zero/next_x_after_truncate",
|
||||||
test_hbitmap_next_zero_after_truncate);
|
test_hbitmap_next_x_after_truncate);
|
||||||
|
|
||||||
hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0",
|
hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0",
|
||||||
test_hbitmap_next_dirty_area_0);
|
test_hbitmap_next_dirty_area_0);
|
||||||
|
|
134
util/hbitmap.c
134
util/hbitmap.c
|
@ -104,7 +104,7 @@ struct HBitmap {
|
||||||
/* Advance hbi to the next nonzero word and return it. hbi->pos
|
/* Advance hbi to the next nonzero word and return it. hbi->pos
|
||||||
* is updated. Returns zero if we reach the end of the bitmap.
|
* is updated. Returns zero if we reach the end of the bitmap.
|
||||||
*/
|
*/
|
||||||
unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi)
|
static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi)
|
||||||
{
|
{
|
||||||
size_t pos = hbi->pos;
|
size_t pos = hbi->pos;
|
||||||
const HBitmap *hb = hbi->hb;
|
const HBitmap *hb = hbi->hb;
|
||||||
|
@ -193,7 +193,31 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count)
|
int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count)
|
||||||
|
{
|
||||||
|
HBitmapIter hbi;
|
||||||
|
int64_t first_dirty_off;
|
||||||
|
uint64_t end;
|
||||||
|
|
||||||
|
assert(start >= 0 && count >= 0);
|
||||||
|
|
||||||
|
if (start >= hb->orig_size || count == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = count > hb->orig_size - start ? hb->orig_size : start + count;
|
||||||
|
|
||||||
|
hbitmap_iter_init(&hbi, hb, start);
|
||||||
|
first_dirty_off = hbitmap_iter_next(&hbi);
|
||||||
|
|
||||||
|
if (first_dirty_off < 0 || first_dirty_off >= end) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MAX(start, first_dirty_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count)
|
||||||
{
|
{
|
||||||
size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL;
|
size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL;
|
||||||
unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1];
|
unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1];
|
||||||
|
@ -202,6 +226,8 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count)
|
||||||
uint64_t end_bit, sz;
|
uint64_t end_bit, sz;
|
||||||
int64_t res;
|
int64_t res;
|
||||||
|
|
||||||
|
assert(start >= 0 && count >= 0);
|
||||||
|
|
||||||
if (start >= hb->orig_size || count == 0) {
|
if (start >= hb->orig_size || count == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -244,41 +270,33 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start,
|
bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end,
|
||||||
uint64_t *count)
|
int64_t max_dirty_count,
|
||||||
|
int64_t *dirty_start, int64_t *dirty_count)
|
||||||
{
|
{
|
||||||
HBitmapIter hbi;
|
int64_t next_zero;
|
||||||
int64_t firt_dirty_off, area_end;
|
|
||||||
uint32_t granularity = 1UL << hb->granularity;
|
|
||||||
uint64_t end;
|
|
||||||
|
|
||||||
if (*start >= hb->orig_size || *count == 0) {
|
assert(start >= 0 && end >= 0 && max_dirty_count > 0);
|
||||||
|
|
||||||
|
end = MIN(end, hb->orig_size);
|
||||||
|
if (start >= end) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
end = *count > hb->orig_size - *start ? hb->orig_size : *start + *count;
|
start = hbitmap_next_dirty(hb, start, end - start);
|
||||||
|
if (start < 0) {
|
||||||
hbitmap_iter_init(&hbi, hb, *start);
|
|
||||||
firt_dirty_off = hbitmap_iter_next(&hbi);
|
|
||||||
|
|
||||||
if (firt_dirty_off < 0 || firt_dirty_off >= end) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firt_dirty_off + granularity >= end) {
|
end = start + MIN(end - start, max_dirty_count);
|
||||||
area_end = end;
|
|
||||||
} else {
|
next_zero = hbitmap_next_zero(hb, start, end - start);
|
||||||
area_end = hbitmap_next_zero(hb, firt_dirty_off + granularity,
|
if (next_zero >= 0) {
|
||||||
end - firt_dirty_off - granularity);
|
end = next_zero;
|
||||||
if (area_end < 0) {
|
|
||||||
area_end = end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firt_dirty_off > *start) {
|
*dirty_start = start;
|
||||||
*start = firt_dirty_off;
|
*dirty_count = end - start;
|
||||||
}
|
|
||||||
*count = area_end - *start;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -298,6 +316,35 @@ uint64_t hbitmap_count(const HBitmap *hb)
|
||||||
return hb->count << hb->granularity;
|
return hb->count << hb->granularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hbitmap_iter_next_word:
|
||||||
|
* @hbi: HBitmapIter to operate on.
|
||||||
|
* @p_cur: Location where to store the next non-zero word.
|
||||||
|
*
|
||||||
|
* Return the index of the next nonzero word that is set in @hbi's
|
||||||
|
* associated HBitmap, and set *p_cur to the content of that word
|
||||||
|
* (bits before the index that was passed to hbitmap_iter_init are
|
||||||
|
* trimmed on the first call). Return -1, and set *p_cur to zero,
|
||||||
|
* if all remaining words are zero.
|
||||||
|
*/
|
||||||
|
static size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur)
|
||||||
|
{
|
||||||
|
unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1];
|
||||||
|
|
||||||
|
if (cur == 0) {
|
||||||
|
cur = hbitmap_iter_skip_words(hbi);
|
||||||
|
if (cur == 0) {
|
||||||
|
*p_cur = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The next call will resume work from the next word. */
|
||||||
|
hbi->cur[HBITMAP_LEVELS - 1] = 0;
|
||||||
|
*p_cur = cur;
|
||||||
|
return hbi->pos;
|
||||||
|
}
|
||||||
|
|
||||||
/* Count the number of set bits between start and end, not accounting for
|
/* Count the number of set bits between start and end, not accounting for
|
||||||
* the granularity. Also an example of how to use hbitmap_iter_next_word.
|
* the granularity. Also an example of how to use hbitmap_iter_next_word.
|
||||||
*/
|
*/
|
||||||
|
@ -716,6 +763,7 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity)
|
||||||
HBitmap *hb = g_new0(struct HBitmap, 1);
|
HBitmap *hb = g_new0(struct HBitmap, 1);
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
|
assert(size <= INT64_MAX);
|
||||||
hb->orig_size = size;
|
hb->orig_size = size;
|
||||||
|
|
||||||
assert(granularity >= 0 && granularity < 64);
|
assert(granularity >= 0 && granularity < 64);
|
||||||
|
@ -746,6 +794,7 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
|
||||||
uint64_t num_elements = size;
|
uint64_t num_elements = size;
|
||||||
uint64_t old;
|
uint64_t old;
|
||||||
|
|
||||||
|
assert(size <= INT64_MAX);
|
||||||
hb->orig_size = size;
|
hb->orig_size = size;
|
||||||
|
|
||||||
/* Size comes in as logical elements, adjust for granularity. */
|
/* Size comes in as logical elements, adjust for granularity. */
|
||||||
|
@ -803,16 +852,15 @@ bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b)
|
||||||
*/
|
*/
|
||||||
static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src)
|
static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src)
|
||||||
{
|
{
|
||||||
uint64_t offset = 0;
|
int64_t offset;
|
||||||
uint64_t count = src->orig_size;
|
int64_t count;
|
||||||
|
|
||||||
while (hbitmap_next_dirty_area(src, &offset, &count)) {
|
for (offset = 0;
|
||||||
|
hbitmap_next_dirty_area(src, offset, src->orig_size, INT64_MAX,
|
||||||
|
&offset, &count);
|
||||||
|
offset += count)
|
||||||
|
{
|
||||||
hbitmap_set(dst, offset, count);
|
hbitmap_set(dst, offset, count);
|
||||||
offset += count;
|
|
||||||
if (offset >= src->orig_size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
count = src->orig_size - offset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -874,22 +922,6 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size)
|
|
||||||
{
|
|
||||||
assert(!(chunk_size & (chunk_size - 1)));
|
|
||||||
assert(!hb->meta);
|
|
||||||
hb->meta = hbitmap_alloc(hb->size << hb->granularity,
|
|
||||||
hb->granularity + ctz32(chunk_size));
|
|
||||||
return hb->meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hbitmap_free_meta(HBitmap *hb)
|
|
||||||
{
|
|
||||||
assert(hb->meta);
|
|
||||||
hbitmap_free(hb->meta);
|
|
||||||
hb->meta = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *hbitmap_sha256(const HBitmap *bitmap, Error **errp)
|
char *hbitmap_sha256(const HBitmap *bitmap, Error **errp)
|
||||||
{
|
{
|
||||||
size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long);
|
size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long);
|
||||||
|
|
Loading…
Reference in a new issue