diff mbox series

[v3,1/8] dirty-bitmap: improve bdrv_dirty_bitmap_next_zero

Message ID 20180814121443.33114-2-vsementsov@virtuozzo.com
State New
Headers show
Series dirty-bitmap: rewrite bdrv_dirty_iter_next_area | expand

Commit Message

Vladimir Sementsov-Ogievskiy Aug. 14, 2018, 12:14 p.m. UTC
Add bytes parameter to the function, to limit searched range.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 include/block/dirty-bitmap.h |  3 ++-
 include/qemu/hbitmap.h       | 10 ++++++++--
 block/backup.c               |  2 +-
 block/dirty-bitmap.c         |  5 +++--
 nbd/server.c                 |  2 +-
 tests/test-hbitmap.c         |  2 +-
 util/hbitmap.c               | 25 ++++++++++++++++++++-----
 7 files changed, 36 insertions(+), 13 deletions(-)

Comments

John Snow Sept. 7, 2018, 9:49 p.m. UTC | #1
On 08/14/2018 08:14 AM, Vladimir Sementsov-Ogievskiy wrote:
> Add bytes parameter to the function, to limit searched range.
> 

I'm going to assume that Eric Blake has been through here and commented
on the interface itself.

> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>  include/block/dirty-bitmap.h |  3 ++-
>  include/qemu/hbitmap.h       | 10 ++++++++--
>  block/backup.c               |  2 +-
>  block/dirty-bitmap.c         |  5 +++--
>  nbd/server.c                 |  2 +-
>  tests/test-hbitmap.c         |  2 +-
>  util/hbitmap.c               | 25 ++++++++++++++++++++-----
>  7 files changed, 36 insertions(+), 13 deletions(-)
> 
> diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
> index 259bd27c40..27f5299c4e 100644
> --- a/include/block/dirty-bitmap.h
> +++ b/include/block/dirty-bitmap.h
> @@ -98,7 +98,8 @@ bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
>  BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
>                                          BdrvDirtyBitmap *bitmap);
>  char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
> -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start);
> +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start,
> +                                    int64_t end);
>  BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
>                                                    BdrvDirtyBitmap *bitmap,
>                                                    Error **errp);
> diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h
> index ddca52c48e..fe4dfde27a 100644
> --- a/include/qemu/hbitmap.h
> +++ b/include/qemu/hbitmap.h
> @@ -295,10 +295,16 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi);
>  /* hbitmap_next_zero:
>   * @hb: The HBitmap to operate on
>   * @start: The bit to start from.
> + * @end: End of range to search in. If @end is -1, search up to the bitmap
> + *       end.
>   *
> - * Find next not dirty bit.
> + * Find next not dirty bit within range [@start, @end), or from
> + * @start to the bitmap end if @end is -1. If not found, return -1.
> + *
> + * @end may be greater than original bitmap size, in this case, search up to

"original" bitmap size? I think that's just an implementation detail, we
can drop 'original' here, yes?

> + * the bitmap end.
>   */
> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end);
>  

The interface looks weird because we can define a 'start' that's beyond
the 'end'.

I realize that you need a signed integer for 'end' to signify EOF...
should we do a 'bytes' parameter instead? (Did you already do that in an
earlier version and we changed it?)

Well, it's not a big deal to me personally.

>  /* hbitmap_create_meta:
>   * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap.
> diff --git a/block/backup.c b/block/backup.c
> index 8630d32926..9bfd3f7189 100644
> --- a/block/backup.c
> +++ b/block/backup.c
> @@ -458,7 +458,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
>              break;
>          }
>  
> -        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
> +        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, -1);
>          if (offset == -1) {
>              hbitmap_set(job->copy_bitmap, cluster, end - cluster);
>              break;
> diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
> index c9b8a6fd52..037ae62726 100644
> --- a/block/dirty-bitmap.c
> +++ b/block/dirty-bitmap.c
> @@ -785,9 +785,10 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **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_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
> +                                    int64_t end)
>  {
> -    return hbitmap_next_zero(bitmap->bitmap, offset);
> +    return hbitmap_next_zero(bitmap->bitmap, offset, end);
>  }
>  
>  void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
> diff --git a/nbd/server.c b/nbd/server.c
> index ea5fe0eb33..07920d123b 100644
> --- a/nbd/server.c
> +++ b/nbd/server.c
> @@ -1952,7 +1952,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
>      assert(begin < overall_end && nb_extents);
>      while (begin < overall_end && i < nb_extents) {
>          if (dirty) {
> -            end = bdrv_dirty_bitmap_next_zero(bitmap, begin);
> +            end = bdrv_dirty_bitmap_next_zero(bitmap, begin, -1);
>          } else {
>              bdrv_set_dirty_iter(it, begin);
>              end = bdrv_dirty_iter_next(it);
> diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c
> index 5e67ac1d3a..6b6a40bddd 100644
> --- a/tests/test-hbitmap.c
> +++ b/tests/test-hbitmap.c
> @@ -939,7 +939,7 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data,
>  
>  static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start)
>  {
> -    int64_t ret1 = hbitmap_next_zero(data->hb, start);
> +    int64_t ret1 = hbitmap_next_zero(data->hb, start, -1);
>      int64_t ret2 = start;
>      for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) {
>          ;
> diff --git a/util/hbitmap.c b/util/hbitmap.c
> index bcd304041a..1687372504 100644
> --- a/util/hbitmap.c
> +++ b/util/hbitmap.c
> @@ -53,6 +53,9 @@
>   */
>  
>  struct HBitmap {
> +    /* Size of the bitmap, as requested in hbitmap_alloc. */
> +    uint64_t orig_size;
> +
>      /* Number of total bits in the bottom level.  */
>      uint64_t size;
>  
> @@ -192,16 +195,26 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first)
>      }
>  }
>  
> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start)
> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end)
>  {
>      size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL;
>      unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1];
> -    uint64_t sz = hb->sizes[HBITMAP_LEVELS - 1];
>      unsigned long cur = last_lev[pos];
> -    unsigned start_bit_offset =
> -            (start >> hb->granularity) & (BITS_PER_LONG - 1);
> +    unsigned start_bit_offset;
> +    uint64_t end_bit, sz;
>      int64_t res;
>  
> +    if (start >= hb->orig_size || (end != -1 && end <= start)) {
> +        return -1;
> +    }
> +
> +    end_bit = end == -1 ? hb->size : ((end - 1) >> hb->granularity) + 1;
> +    sz = (end_bit + BITS_PER_LONG - 1) >> BITS_PER_LEVEL;
> +
> +    /* There may be some zero bits in @cur before @start. We are not interested
> +     * in them, let's set them.
> +     */
> +    start_bit_offset = (start >> hb->granularity) & (BITS_PER_LONG - 1);
>      cur |= (1UL << start_bit_offset) - 1;
>      assert((start >> hb->granularity) < hb->size);
>  
> @@ -218,7 +231,7 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start)
>      }
>  
>      res = (pos << BITS_PER_LEVEL) + ctol(cur);
> -    if (res >= hb->size) {
> +    if (res >= end_bit) {
>          return -1;
>      }
>  
> @@ -652,6 +665,8 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity)
>      HBitmap *hb = g_new0(struct HBitmap, 1);
>      unsigned i;
>  
> +    hb->orig_size = size;
> +
>      assert(granularity >= 0 && granularity < 64);
>      size = (size + (1ULL << granularity) - 1) >> granularity;
>      assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE));
> 

Despite my comments, I'm OK with or without changes.

Reviewed-by: John Snow <jsnow@redhat.com>
Eric Blake Sept. 10, 2018, 2:59 p.m. UTC | #2
On 9/7/18 4:49 PM, John Snow wrote:
> 
> 
> On 08/14/2018 08:14 AM, Vladimir Sementsov-Ogievskiy wrote:
>> Add bytes parameter to the function, to limit searched range.
>>
> 
> I'm going to assume that Eric Blake has been through here and commented
> on the interface itself.

Actually, I haven't had time to look at this series in depth. Do you 
need me to?

> 
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   include/block/dirty-bitmap.h |  3 ++-
>>   include/qemu/hbitmap.h       | 10 ++++++++--
>>   block/backup.c               |  2 +-
>>   block/dirty-bitmap.c         |  5 +++--
>>   nbd/server.c                 |  2 +-
>>   tests/test-hbitmap.c         |  2 +-
>>   util/hbitmap.c               | 25 ++++++++++++++++++++-----
>>   7 files changed, 36 insertions(+), 13 deletions(-)
>>
>> diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
>> index 259bd27c40..27f5299c4e 100644
>> --- a/include/block/dirty-bitmap.h
>> +++ b/include/block/dirty-bitmap.h
>> @@ -98,7 +98,8 @@ bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
>>   BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
>>                                           BdrvDirtyBitmap *bitmap);
>>   char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
>> -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start);
>> +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start,
>> +                                    int64_t end);

It's already seeming a bit odd to mix uint64_t AND int64_t for the two 
parameters.  Is the intent to allow -1 to mean "end of the bitmap 
instead of a specific end range"? But you can get that with UINT64_MAX 
just as easily, and still get away with spelling it -1 in the source.


>> + * the bitmap end.
>>    */
>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end);
>>   
> 
> The interface looks weird because we can define a 'start' that's beyond
> the 'end'.
> 
> I realize that you need a signed integer for 'end' to signify EOF...
> should we do a 'bytes' parameter instead? (Did you already do that in an
> earlier version and we changed it?)
> 
> Well, it's not a big deal to me personally.

It should always be possible to convert in either direction between 
[start,end) and [start,start+bytes); it boils down to a question of 
convenience (which form is easier for the majority of callers) and 
consistency (which form do we use more frequently in the block layer). I 
haven't checked closely, but I think start+bytes is more common than end 
in our public block layer APIs.
John Snow Sept. 10, 2018, 3:49 p.m. UTC | #3
On 09/10/2018 10:59 AM, Eric Blake wrote:
> On 9/7/18 4:49 PM, John Snow wrote:
>>
>>
>> On 08/14/2018 08:14 AM, Vladimir Sementsov-Ogievskiy wrote:
>>> Add bytes parameter to the function, to limit searched range.
>>>
>>
>> I'm going to assume that Eric Blake has been through here and commented
>> on the interface itself.
> 
> Actually, I haven't had time to look at this series in depth. Do you
> need me to?
> 

Not necessarily, it's just that I didn't read v1 or v2 so I was just
being cautious against recommending changes that maybe we already
recommended against in a different direction.

Historically you've cared the most about start/end/offset/bytes naming
and conventions, so I just made an assumption.

--js

>>
>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>>> ---
>>>   include/block/dirty-bitmap.h |  3 ++-
>>>   include/qemu/hbitmap.h       | 10 ++++++++--
>>>   block/backup.c               |  2 +-
>>>   block/dirty-bitmap.c         |  5 +++--
>>>   nbd/server.c                 |  2 +-
>>>   tests/test-hbitmap.c         |  2 +-
>>>   util/hbitmap.c               | 25 ++++++++++++++++++++-----
>>>   7 files changed, 36 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
>>> index 259bd27c40..27f5299c4e 100644
>>> --- a/include/block/dirty-bitmap.h
>>> +++ b/include/block/dirty-bitmap.h
>>> @@ -98,7 +98,8 @@ bool
>>> bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
>>>   BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
>>>                                           BdrvDirtyBitmap *bitmap);
>>>   char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error
>>> **errp);
>>> -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap,
>>> uint64_t start);
>>> +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap,
>>> uint64_t start,
>>> +                                    int64_t end);
> 
> It's already seeming a bit odd to mix uint64_t AND int64_t for the two
> parameters.  Is the intent to allow -1 to mean "end of the bitmap
> instead of a specific end range"? But you can get that with UINT64_MAX
> just as easily, and still get away with spelling it -1 in the source.
> 
> 
>>> + * the bitmap end.
>>>    */
>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t
>>> end);
>>>   
>>
>> The interface looks weird because we can define a 'start' that's beyond
>> the 'end'.
>>
>> I realize that you need a signed integer for 'end' to signify EOF...
>> should we do a 'bytes' parameter instead? (Did you already do that in an
>> earlier version and we changed it?)
>>
>> Well, it's not a big deal to me personally.
> 
> It should always be possible to convert in either direction between
> [start,end) and [start,start+bytes); it boils down to a question of
> convenience (which form is easier for the majority of callers) and
> consistency (which form do we use more frequently in the block layer). I
> haven't checked closely, but I think start+bytes is more common than end
> in our public block layer APIs.
>
Vladimir Sementsov-Ogievskiy Sept. 10, 2018, 4:49 p.m. UTC | #4
08.09.2018 00:49, John Snow wrote:
>
> On 08/14/2018 08:14 AM, Vladimir Sementsov-Ogievskiy wrote:
>> Add bytes parameter to the function, to limit searched range.
>>
> I'm going to assume that Eric Blake has been through here and commented
> on the interface itself.
>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   include/block/dirty-bitmap.h |  3 ++-
>>   include/qemu/hbitmap.h       | 10 ++++++++--
>>   block/backup.c               |  2 +-
>>   block/dirty-bitmap.c         |  5 +++--
>>   nbd/server.c                 |  2 +-
>>   tests/test-hbitmap.c         |  2 +-
>>   util/hbitmap.c               | 25 ++++++++++++++++++++-----
>>   7 files changed, 36 insertions(+), 13 deletions(-)
>>
>> diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
>> index 259bd27c40..27f5299c4e 100644
>> --- a/include/block/dirty-bitmap.h
>> +++ b/include/block/dirty-bitmap.h
>> @@ -98,7 +98,8 @@ bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
>>   BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
>>                                           BdrvDirtyBitmap *bitmap);
>>   char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
>> -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start);
>> +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start,
>> +                                    int64_t end);
>>   BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
>>                                                     BdrvDirtyBitmap *bitmap,
>>                                                     Error **errp);
>> diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h
>> index ddca52c48e..fe4dfde27a 100644
>> --- a/include/qemu/hbitmap.h
>> +++ b/include/qemu/hbitmap.h
>> @@ -295,10 +295,16 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi);
>>   /* hbitmap_next_zero:
>>    * @hb: The HBitmap to operate on
>>    * @start: The bit to start from.
>> + * @end: End of range to search in. If @end is -1, search up to the bitmap
>> + *       end.
>>    *
>> - * Find next not dirty bit.
>> + * Find next not dirty bit within range [@start, @end), or from
>> + * @start to the bitmap end if @end is -1. If not found, return -1.
>> + *
>> + * @end may be greater than original bitmap size, in this case, search up to
> "original" bitmap size? I think that's just an implementation detail, we
> can drop 'original' here, yes?

hm, no. we have field "size", which is not "original". But it all means 
that this place needs a bit more refactoring.

>
>> + * the bitmap end.
>>    */
>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end);
>>   
> The interface looks weird because we can define a 'start' that's beyond
> the 'end'.
>
> I realize that you need a signed integer for 'end' to signify EOF...
> should we do a 'bytes' parameter instead? (Did you already do that in an
> earlier version and we changed it?)
>
> Well, it's not a big deal to me personally.

interface with constant end parameter is more comfortable for loop: we 
don't need to update 'bytes' parameter on each iteration

>
>>   /* hbitmap_create_meta:
>>    * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap.
>> diff --git a/block/backup.c b/block/backup.c
>> index 8630d32926..9bfd3f7189 100644
>> --- a/block/backup.c
>> +++ b/block/backup.c
>> @@ -458,7 +458,7 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
>>               break;
>>           }
>>   
>> -        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
>> +        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, -1);
>>           if (offset == -1) {
>>               hbitmap_set(job->copy_bitmap, cluster, end - cluster);
>>               break;
>> diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
>> index c9b8a6fd52..037ae62726 100644
>> --- a/block/dirty-bitmap.c
>> +++ b/block/dirty-bitmap.c
>> @@ -785,9 +785,10 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **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_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
>> +                                    int64_t end)
>>   {
>> -    return hbitmap_next_zero(bitmap->bitmap, offset);
>> +    return hbitmap_next_zero(bitmap->bitmap, offset, end);
>>   }
>>   
>>   void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
>> diff --git a/nbd/server.c b/nbd/server.c
>> index ea5fe0eb33..07920d123b 100644
>> --- a/nbd/server.c
>> +++ b/nbd/server.c
>> @@ -1952,7 +1952,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
>>       assert(begin < overall_end && nb_extents);
>>       while (begin < overall_end && i < nb_extents) {
>>           if (dirty) {
>> -            end = bdrv_dirty_bitmap_next_zero(bitmap, begin);
>> +            end = bdrv_dirty_bitmap_next_zero(bitmap, begin, -1);
>>           } else {
>>               bdrv_set_dirty_iter(it, begin);
>>               end = bdrv_dirty_iter_next(it);
>> diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c
>> index 5e67ac1d3a..6b6a40bddd 100644
>> --- a/tests/test-hbitmap.c
>> +++ b/tests/test-hbitmap.c
>> @@ -939,7 +939,7 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data,
>>   
>>   static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start)
>>   {
>> -    int64_t ret1 = hbitmap_next_zero(data->hb, start);
>> +    int64_t ret1 = hbitmap_next_zero(data->hb, start, -1);
>>       int64_t ret2 = start;
>>       for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) {
>>           ;
>> diff --git a/util/hbitmap.c b/util/hbitmap.c
>> index bcd304041a..1687372504 100644
>> --- a/util/hbitmap.c
>> +++ b/util/hbitmap.c
>> @@ -53,6 +53,9 @@
>>    */
>>   
>>   struct HBitmap {
>> +    /* Size of the bitmap, as requested in hbitmap_alloc. */
>> +    uint64_t orig_size;
>> +
>>       /* Number of total bits in the bottom level.  */
>>       uint64_t size;
>>   
>> @@ -192,16 +195,26 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first)
>>       }
>>   }
>>   
>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start)
>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end)
>>   {
>>       size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL;
>>       unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1];
>> -    uint64_t sz = hb->sizes[HBITMAP_LEVELS - 1];
>>       unsigned long cur = last_lev[pos];
>> -    unsigned start_bit_offset =
>> -            (start >> hb->granularity) & (BITS_PER_LONG - 1);
>> +    unsigned start_bit_offset;
>> +    uint64_t end_bit, sz;
>>       int64_t res;
>>   
>> +    if (start >= hb->orig_size || (end != -1 && end <= start)) {
>> +        return -1;
>> +    }
>> +
>> +    end_bit = end == -1 ? hb->size : ((end - 1) >> hb->granularity) + 1;
>> +    sz = (end_bit + BITS_PER_LONG - 1) >> BITS_PER_LEVEL;
>> +
>> +    /* There may be some zero bits in @cur before @start. We are not interested
>> +     * in them, let's set them.
>> +     */
>> +    start_bit_offset = (start >> hb->granularity) & (BITS_PER_LONG - 1);
>>       cur |= (1UL << start_bit_offset) - 1;
>>       assert((start >> hb->granularity) < hb->size);
>>   
>> @@ -218,7 +231,7 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start)
>>       }
>>   
>>       res = (pos << BITS_PER_LEVEL) + ctol(cur);
>> -    if (res >= hb->size) {
>> +    if (res >= end_bit) {
>>           return -1;
>>       }
>>   
>> @@ -652,6 +665,8 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity)
>>       HBitmap *hb = g_new0(struct HBitmap, 1);
>>       unsigned i;
>>   
>> +    hb->orig_size = size;
>> +
>>       assert(granularity >= 0 && granularity < 64);
>>       size = (size + (1ULL << granularity) - 1) >> granularity;
>>       assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE));
>>
> Despite my comments, I'm OK with or without changes.
>
> Reviewed-by: John Snow <jsnow@redhat.com>
Eric Blake Sept. 10, 2018, 4:55 p.m. UTC | #5
On 9/10/18 11:49 AM, Vladimir Sementsov-Ogievskiy wrote:

>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t 
>>> end);
>> The interface looks weird because we can define a 'start' that's beyond
>> the 'end'.
>>
>> I realize that you need a signed integer for 'end' to signify EOF...
>> should we do a 'bytes' parameter instead? (Did you already do that in an
>> earlier version and we changed it?)
>>
>> Well, it's not a big deal to me personally.
> 
> interface with constant end parameter is more comfortable for loop: we 
> don't need to update 'bytes' parameter on each iteration

But there's still the question of WHO should be calculating end. Your 
interface argues for the caller:

hbitmap_next_zero(start, start + bytes)

int64_t hbitmap_next_zero(...)
{
     while (offset != end) ...
}

while we're asking about a consistent interface for the caller (if most 
callers already have a 'bytes' rather than an 'end' computed):

hbitmap_next_zero(start, bytes)

int64_t hbitmap_next_zero(...)
{
     int64_t end = start + bytes;
     while (offset != end) ...
}
Vladimir Sementsov-Ogievskiy Sept. 10, 2018, 5 p.m. UTC | #6
10.09.2018 19:55, Eric Blake wrote:
> On 9/10/18 11:49 AM, Vladimir Sementsov-Ogievskiy wrote:
>
>>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, 
>>>> int64_t end);
>>> The interface looks weird because we can define a 'start' that's beyond
>>> the 'end'.
>>>
>>> I realize that you need a signed integer for 'end' to signify EOF...
>>> should we do a 'bytes' parameter instead? (Did you already do that 
>>> in an
>>> earlier version and we changed it?)
>>>
>>> Well, it's not a big deal to me personally.
>>
>> interface with constant end parameter is more comfortable for loop: 
>> we don't need to update 'bytes' parameter on each iteration
>
> But there's still the question of WHO should be calculating end. Your 
> interface argues for the caller:
>
> hbitmap_next_zero(start, start + bytes)
>
> int64_t hbitmap_next_zero(...)
> {
>     while (offset != end) ...
> }
>
> while we're asking about a consistent interface for the caller (if 
> most callers already have a 'bytes' rather than an 'end' computed):
>
> hbitmap_next_zero(start, bytes)
>
> int64_t hbitmap_next_zero(...)
> {
>     int64_t end = start + bytes;
>     while (offset != end) ...
> }
>

Yes, that's an issue. Ok, if you are not comfortable with start,end, I 
can switch to start,bytes.
John Snow Sept. 14, 2018, 5:39 p.m. UTC | #7
On 09/10/2018 01:00 PM, Vladimir Sementsov-Ogievskiy wrote:
> 10.09.2018 19:55, Eric Blake wrote:
>> On 9/10/18 11:49 AM, Vladimir Sementsov-Ogievskiy wrote:
>>
>>>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start,
>>>>> int64_t end);
>>>> The interface looks weird because we can define a 'start' that's beyond
>>>> the 'end'.
>>>>
>>>> I realize that you need a signed integer for 'end' to signify EOF...
>>>> should we do a 'bytes' parameter instead? (Did you already do that
>>>> in an
>>>> earlier version and we changed it?)
>>>>
>>>> Well, it's not a big deal to me personally.
>>>
>>> interface with constant end parameter is more comfortable for loop:
>>> we don't need to update 'bytes' parameter on each iteration
>>
>> But there's still the question of WHO should be calculating end. Your
>> interface argues for the caller:
>>
>> hbitmap_next_zero(start, start + bytes)
>>
>> int64_t hbitmap_next_zero(...)
>> {
>>     while (offset != end) ...
>> }
>>
>> while we're asking about a consistent interface for the caller (if
>> most callers already have a 'bytes' rather than an 'end' computed):
>>
>> hbitmap_next_zero(start, bytes)
>>
>> int64_t hbitmap_next_zero(...)
>> {
>>     int64_t end = start + bytes;
>>     while (offset != end) ...
>> }
>>
> 
> Yes, that's an issue. Ok, if you are not comfortable with start,end, I
> can switch to start,bytes.
> 

The series looks pretty close, I can merge the next version if you think
it's worth changing the interface.

--js
Vladimir Sementsov-Ogievskiy Sept. 14, 2018, 5:51 p.m. UTC | #8
14.09.2018 20:39, John Snow wrote:
>
> On 09/10/2018 01:00 PM, Vladimir Sementsov-Ogievskiy wrote:
>> 10.09.2018 19:55, Eric Blake wrote:
>>> On 9/10/18 11:49 AM, Vladimir Sementsov-Ogievskiy wrote:
>>>
>>>>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>>>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start,
>>>>>> int64_t end);
>>>>> The interface looks weird because we can define a 'start' that's beyond
>>>>> the 'end'.
>>>>>
>>>>> I realize that you need a signed integer for 'end' to signify EOF...
>>>>> should we do a 'bytes' parameter instead? (Did you already do that
>>>>> in an
>>>>> earlier version and we changed it?)
>>>>>
>>>>> Well, it's not a big deal to me personally.
>>>> interface with constant end parameter is more comfortable for loop:
>>>> we don't need to update 'bytes' parameter on each iteration
>>> But there's still the question of WHO should be calculating end. Your
>>> interface argues for the caller:
>>>
>>> hbitmap_next_zero(start, start + bytes)
>>>
>>> int64_t hbitmap_next_zero(...)
>>> {
>>>      while (offset != end) ...
>>> }
>>>
>>> while we're asking about a consistent interface for the caller (if
>>> most callers already have a 'bytes' rather than an 'end' computed):
>>>
>>> hbitmap_next_zero(start, bytes)
>>>
>>> int64_t hbitmap_next_zero(...)
>>> {
>>>      int64_t end = start + bytes;
>>>      while (offset != end) ...
>>> }
>>>
>> Yes, that's an issue. Ok, if you are not comfortable with start,end, I
>> can switch to start,bytes.
>>
> The series looks pretty close, I can merge the next version if you think
> it's worth changing the interface.
>
> --js

I've started to change interface and found a bug in bitmap_to_extents 
(patch sent 
https://lists.gnu.org/archive/html/qemu-devel/2018-09/msg01804.html). 
So, next version will be based on this patch, which will go through 
Eric's tree..
John Snow Sept. 14, 2018, 5:52 p.m. UTC | #9
On 09/14/2018 01:51 PM, Vladimir Sementsov-Ogievskiy wrote:
> 14.09.2018 20:39, John Snow wrote:
>>
>> On 09/10/2018 01:00 PM, Vladimir Sementsov-Ogievskiy wrote:
>>> 10.09.2018 19:55, Eric Blake wrote:
>>>> On 9/10/18 11:49 AM, Vladimir Sementsov-Ogievskiy wrote:
>>>>
>>>>>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>>>>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start,
>>>>>>> int64_t end);
>>>>>> The interface looks weird because we can define a 'start' that's
>>>>>> beyond
>>>>>> the 'end'.
>>>>>>
>>>>>> I realize that you need a signed integer for 'end' to signify EOF...
>>>>>> should we do a 'bytes' parameter instead? (Did you already do that
>>>>>> in an
>>>>>> earlier version and we changed it?)
>>>>>>
>>>>>> Well, it's not a big deal to me personally.
>>>>> interface with constant end parameter is more comfortable for loop:
>>>>> we don't need to update 'bytes' parameter on each iteration
>>>> But there's still the question of WHO should be calculating end. Your
>>>> interface argues for the caller:
>>>>
>>>> hbitmap_next_zero(start, start + bytes)
>>>>
>>>> int64_t hbitmap_next_zero(...)
>>>> {
>>>>      while (offset != end) ...
>>>> }
>>>>
>>>> while we're asking about a consistent interface for the caller (if
>>>> most callers already have a 'bytes' rather than an 'end' computed):
>>>>
>>>> hbitmap_next_zero(start, bytes)
>>>>
>>>> int64_t hbitmap_next_zero(...)
>>>> {
>>>>      int64_t end = start + bytes;
>>>>      while (offset != end) ...
>>>> }
>>>>
>>> Yes, that's an issue. Ok, if you are not comfortable with start,end, I
>>> can switch to start,bytes.
>>>
>> The series looks pretty close, I can merge the next version if you think
>> it's worth changing the interface.
>>
>> --js
> 
> I've started to change interface and found a bug in bitmap_to_extents
> (patch sent
> https://lists.gnu.org/archive/html/qemu-devel/2018-09/msg01804.html).
> So, next version will be based on this patch, which will go through
> Eric's tree..
> 

ah, I see. you can send it to the list anyway with the requires: header
and I can have Eric stage it to make the eventual merge easier for Peter.

--js
John Snow Sept. 14, 2018, 8:03 p.m. UTC | #10
On 09/14/2018 01:51 PM, Vladimir Sementsov-Ogievskiy wrote:
> 14.09.2018 20:39, John Snow wrote:
>>
>> On 09/10/2018 01:00 PM, Vladimir Sementsov-Ogievskiy wrote:
>>> 10.09.2018 19:55, Eric Blake wrote:
>>>> On 9/10/18 11:49 AM, Vladimir Sementsov-Ogievskiy wrote:
>>>>
>>>>>>> -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
>>>>>>> +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start,
>>>>>>> int64_t end);
>>>>>> The interface looks weird because we can define a 'start' that's
>>>>>> beyond
>>>>>> the 'end'.
>>>>>>
>>>>>> I realize that you need a signed integer for 'end' to signify EOF...
>>>>>> should we do a 'bytes' parameter instead? (Did you already do that
>>>>>> in an
>>>>>> earlier version and we changed it?)
>>>>>>
>>>>>> Well, it's not a big deal to me personally.
>>>>> interface with constant end parameter is more comfortable for loop:
>>>>> we don't need to update 'bytes' parameter on each iteration
>>>> But there's still the question of WHO should be calculating end. Your
>>>> interface argues for the caller:
>>>>
>>>> hbitmap_next_zero(start, start + bytes)
>>>>
>>>> int64_t hbitmap_next_zero(...)
>>>> {
>>>>      while (offset != end) ...
>>>> }
>>>>
>>>> while we're asking about a consistent interface for the caller (if
>>>> most callers already have a 'bytes' rather than an 'end' computed):
>>>>
>>>> hbitmap_next_zero(start, bytes)
>>>>
>>>> int64_t hbitmap_next_zero(...)
>>>> {
>>>>      int64_t end = start + bytes;
>>>>      while (offset != end) ...
>>>> }
>>>>
>>> Yes, that's an issue. Ok, if you are not comfortable with start,end, I
>>> can switch to start,bytes.
>>>
>> The series looks pretty close, I can merge the next version if you think
>> it's worth changing the interface.
>>
>> --js
> 
> I've started to change interface and found a bug in bitmap_to_extents
> (patch sent
> https://lists.gnu.org/archive/html/qemu-devel/2018-09/msg01804.html).
> So, next version will be based on this patch, which will go through
> Eric's tree..
> 

OK, I spoke with Eric and if you resend and I R-B & ACK the patches,
he'll stage them through NBD.

Thanks,
--js
diff mbox series

Patch

diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
index 259bd27c40..27f5299c4e 100644
--- a/include/block/dirty-bitmap.h
+++ b/include/block/dirty-bitmap.h
@@ -98,7 +98,8 @@  bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
 BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
                                         BdrvDirtyBitmap *bitmap);
 char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
-int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start);
+int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start,
+                                    int64_t end);
 BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
                                                   BdrvDirtyBitmap *bitmap,
                                                   Error **errp);
diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h
index ddca52c48e..fe4dfde27a 100644
--- a/include/qemu/hbitmap.h
+++ b/include/qemu/hbitmap.h
@@ -295,10 +295,16 @@  unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi);
 /* hbitmap_next_zero:
  * @hb: The HBitmap to operate on
  * @start: The bit to start from.
+ * @end: End of range to search in. If @end is -1, search up to the bitmap
+ *       end.
  *
- * Find next not dirty bit.
+ * Find next not dirty bit within range [@start, @end), or from
+ * @start to the bitmap end if @end is -1. If not found, return -1.
+ *
+ * @end may be greater than original bitmap size, in this case, search up to
+ * the bitmap end.
  */
-int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start);
+int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end);
 
 /* hbitmap_create_meta:
  * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap.
diff --git a/block/backup.c b/block/backup.c
index 8630d32926..9bfd3f7189 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -458,7 +458,7 @@  static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
             break;
         }
 
-        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
+        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, -1);
         if (offset == -1) {
             hbitmap_set(job->copy_bitmap, cluster, end - cluster);
             break;
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index c9b8a6fd52..037ae62726 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -785,9 +785,10 @@  char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **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_zero(BdrvDirtyBitmap *bitmap, uint64_t offset,
+                                    int64_t end)
 {
-    return hbitmap_next_zero(bitmap->bitmap, offset);
+    return hbitmap_next_zero(bitmap->bitmap, offset, end);
 }
 
 void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
diff --git a/nbd/server.c b/nbd/server.c
index ea5fe0eb33..07920d123b 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1952,7 +1952,7 @@  static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
     assert(begin < overall_end && nb_extents);
     while (begin < overall_end && i < nb_extents) {
         if (dirty) {
-            end = bdrv_dirty_bitmap_next_zero(bitmap, begin);
+            end = bdrv_dirty_bitmap_next_zero(bitmap, begin, -1);
         } else {
             bdrv_set_dirty_iter(it, begin);
             end = bdrv_dirty_iter_next(it);
diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c
index 5e67ac1d3a..6b6a40bddd 100644
--- a/tests/test-hbitmap.c
+++ b/tests/test-hbitmap.c
@@ -939,7 +939,7 @@  static void test_hbitmap_iter_and_reset(TestHBitmapData *data,
 
 static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start)
 {
-    int64_t ret1 = hbitmap_next_zero(data->hb, start);
+    int64_t ret1 = hbitmap_next_zero(data->hb, start, -1);
     int64_t ret2 = start;
     for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) {
         ;
diff --git a/util/hbitmap.c b/util/hbitmap.c
index bcd304041a..1687372504 100644
--- a/util/hbitmap.c
+++ b/util/hbitmap.c
@@ -53,6 +53,9 @@ 
  */
 
 struct HBitmap {
+    /* Size of the bitmap, as requested in hbitmap_alloc. */
+    uint64_t orig_size;
+
     /* Number of total bits in the bottom level.  */
     uint64_t size;
 
@@ -192,16 +195,26 @@  void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first)
     }
 }
 
-int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start)
+int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, int64_t end)
 {
     size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL;
     unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1];
-    uint64_t sz = hb->sizes[HBITMAP_LEVELS - 1];
     unsigned long cur = last_lev[pos];
-    unsigned start_bit_offset =
-            (start >> hb->granularity) & (BITS_PER_LONG - 1);
+    unsigned start_bit_offset;
+    uint64_t end_bit, sz;
     int64_t res;
 
+    if (start >= hb->orig_size || (end != -1 && end <= start)) {
+        return -1;
+    }
+
+    end_bit = end == -1 ? hb->size : ((end - 1) >> hb->granularity) + 1;
+    sz = (end_bit + BITS_PER_LONG - 1) >> BITS_PER_LEVEL;
+
+    /* There may be some zero bits in @cur before @start. We are not interested
+     * in them, let's set them.
+     */
+    start_bit_offset = (start >> hb->granularity) & (BITS_PER_LONG - 1);
     cur |= (1UL << start_bit_offset) - 1;
     assert((start >> hb->granularity) < hb->size);
 
@@ -218,7 +231,7 @@  int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start)
     }
 
     res = (pos << BITS_PER_LEVEL) + ctol(cur);
-    if (res >= hb->size) {
+    if (res >= end_bit) {
         return -1;
     }
 
@@ -652,6 +665,8 @@  HBitmap *hbitmap_alloc(uint64_t size, int granularity)
     HBitmap *hb = g_new0(struct HBitmap, 1);
     unsigned i;
 
+    hb->orig_size = size;
+
     assert(granularity >= 0 && granularity < 64);
     size = (size + (1ULL << granularity) - 1) >> granularity;
     assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE));