diff mbox series

[4/5] block/qcow2-bitmap: Count queued bitmaps towards nb_bitmaps

Message ID 20190606184159.979-5-jsnow@redhat.com (mailing list archive)
State New, archived
Headers show
Series block/dirty-bitmap: check number and size constraints against queued bitmaps | expand

Commit Message

John Snow June 6, 2019, 6:41 p.m. UTC
When we check to see if we can store a bitmap, we don't check how many
we've queued up. This can cause a problem saving bitmaps on close
instead of when we request them to be added. With the stricter add
interface, prohibit these bitmaps specifically.

To match, make the remove interface more strict as well; now rejecting
any requests to remove bitmaps that were never queued for storage.

We don't need to "find" the bitmap when the interface has been given the
bitmap explicitly, but this is done to make sure that the bitmap given
actually does belong to the bs we were passed as a paranoia check to
enforce consistency.

---

"What about directory size?" Please see the following patch.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 block/qcow2.h        |  1 +
 block/dirty-bitmap.c |  8 +++-----
 block/qcow2-bitmap.c | 31 ++++++++++++++++++++++++++-----
 3 files changed, 30 insertions(+), 10 deletions(-)

Comments

Eric Blake June 7, 2019, 2:27 a.m. UTC | #1
On 6/6/19 1:41 PM, John Snow wrote:
> When we check to see if we can store a bitmap, we don't check how many
> we've queued up. This can cause a problem saving bitmaps on close
> instead of when we request them to be added. With the stricter add
> interface, prohibit these bitmaps specifically.
> 
> To match, make the remove interface more strict as well; now rejecting
> any requests to remove bitmaps that were never queued for storage.
> 
> We don't need to "find" the bitmap when the interface has been given the
> bitmap explicitly, but this is done to make sure that the bitmap given
> actually does belong to the bs we were passed as a paranoia check to
> enforce consistency.
> 
> ---

Oops - that marker...

> 
> "What about directory size?" Please see the following patch.
> 
> Signed-off-by: John Snow <jsnow@redhat.com>

...renders the S-o-b invisible.

> +++ b/block/qcow2-bitmap.c
> @@ -1402,6 +1402,23 @@ static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list,
>      return NULL;
>  }
>  
> +static int qcow2_remove_queued_dirty_bitmap(
> +    BlockDriverState *bs, const char *name, Error **errp)
> +{
> +    BDRVQcow2State *s = bs->opaque;
> +    BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, name);
> +    if (!bitmap) {
> +        error_setg(errp, "Node '%s' has no stored or enqueued bitmap '%s'",
> +                   bdrv_get_node_name(bs), name);
> +        return -ENOENT;
> +    }
> +    assert(s->nb_queued_bitmaps > 0);
> +    assert(bdrv_dirty_bitmap_get_persistence(bitmap));
> +    s->nb_queued_bitmaps -= 1;

I tend to use -- over -= 1.

> @@ -1667,6 +1686,8 @@ int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
>          goto fail;
>      }
>  
> +    s->nb_queued_bitmaps += 1;

And again, for ++

Reviewed-by: Eric Blake <eblake@redhat.com>
Vladimir Sementsov-Ogievskiy June 7, 2019, 2:58 p.m. UTC | #2
06.06.2019 21:41, John Snow wrote:
> When we check to see if we can store a bitmap, we don't check how many
> we've queued up. This can cause a problem saving bitmaps on close
> instead of when we request them to be added. With the stricter add
> interface, prohibit these bitmaps specifically.
> 
> To match, make the remove interface more strict as well; now rejecting
> any requests to remove bitmaps that were never queued for storage.
> 
> We don't need to "find" the bitmap when the interface has been given the
> bitmap explicitly, but this is done to make sure that the bitmap given
> actually does belong to the bs we were passed as a paranoia check to
> enforce consistency.

If you want to check it, I'd really prefer to do it explictly,
by adding "bool bdrv_has_dirty_bitmap(bs, bitmap) handler, or bitmap.bs field",
instead of hiding it under inconvenient interface of helper, so we actually do

name = bdrv_dirty_bitmap_name(bitmap);
bitmap = bdrv_find_dirty_bitmap(bs, name);

it really looks strange.

Hmmm, when I read series cover letter and this commit message, I thought
that you'll just calculate current number of persistent bitmaps on bs..
Do we really need to introduce additional counters on qcow2 state?

So, you want to check nb_queued + s->nb_bitmaps instead of just s->nb_bitmaps.

I think we can just count persistent dirty bitmaps and take that number
(as, there should not be in-image bitmaps, for which we don't have in-ram
version, but we can check it too and return an error on mismatch (or count
in-image-not-in-ram bitmaps and this count to number of in-ram persistent
bitmaps it seems an extra thing)..

> 
> ---
> 
> "What about directory size?" Please see the following patch.
> 
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>   block/qcow2.h        |  1 +
>   block/dirty-bitmap.c |  8 +++-----
>   block/qcow2-bitmap.c | 31 ++++++++++++++++++++++++++-----
>   3 files changed, 30 insertions(+), 10 deletions(-)
> 
> diff --git a/block/qcow2.h b/block/qcow2.h
> index ce07f003f7..ebf60ac236 100644
> --- a/block/qcow2.h
> +++ b/block/qcow2.h
> @@ -317,6 +317,7 @@ typedef struct BDRVQcow2State {
>       QCowSnapshot *snapshots;
>   
>       uint32_t nb_bitmaps;
> +    uint32_t nb_queued_bitmaps;
>       uint64_t bitmap_directory_size;
>       uint64_t bitmap_directory_offset;
>   
> diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
> index 4667f9e65a..084c42af57 100644
> --- a/block/dirty-bitmap.c
> +++ b/block/dirty-bitmap.c
> @@ -450,11 +450,9 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
>   }
>   
>   /**
> - * Remove persistent dirty bitmap from the storage if it exists.
> - * Absence of bitmap is not an error, because we have the following scenario:
> - * BdrvDirtyBitmap can have .persistent = true but not yet saved and have no
> - * stored version. For such bitmap bdrv_remove_persistent_dirty_bitmap() should
> - * not fail.
> + * Remove a persistent dirty bitmap from storage,
> + * or dequeue it from being stored if it is enqueued.
> + *
>    * This function doesn't release the corresponding BdrvDirtyBitmap.
>    */
>   int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
> diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
> index 930a6c91ff..7193c66787 100644
> --- a/block/qcow2-bitmap.c
> +++ b/block/qcow2-bitmap.c
> @@ -1402,6 +1402,23 @@ static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list,
>       return NULL;
>   }
>   
> +static int qcow2_remove_queued_dirty_bitmap(
> +    BlockDriverState *bs, const char *name, Error **errp)
> +{
> +    BDRVQcow2State *s = bs->opaque;
> +    BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, name);
> +    if (!bitmap) {
> +        error_setg(errp, "Node '%s' has no stored or enqueued bitmap '%s'",
> +                   bdrv_get_node_name(bs), name);
> +        return -ENOENT;
> +    }
> +    assert(s->nb_queued_bitmaps > 0);
> +    assert(bdrv_dirty_bitmap_get_persistence(bitmap));
> +    s->nb_queued_bitmaps -= 1;
> +
> +    return 0;
> +}
> +
>   int qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
>                                            BdrvDirtyBitmap *bitmap,
>                                            Error **errp)
> @@ -1413,9 +1430,7 @@ int qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
>       const char *name = bdrv_dirty_bitmap_name(bitmap);
>   
>       if (s->nb_bitmaps == 0) {
> -        /* Absence of the bitmap is not an error: see explanation above
> -         * bdrv_remove_persistent_dirty_bitmap() definition. */
> -        return 0;
> +        return qcow2_remove_queued_dirty_bitmap(bs, name, errp);
>       }
>   
>       if ((ret = bitmap_list_load(bs, &bm_list, errp))) {
> @@ -1424,6 +1439,7 @@ int qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
>   
>       bm = find_bitmap_by_name(bm_list, name);
>       if (bm == NULL) {
> +        ret = qcow2_remove_queued_dirty_bitmap(bs, name, errp);
>           goto fail;
>       }
>   
> @@ -1544,6 +1560,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
>           error_setg_errno(errp, -ret, "Failed to update bitmap extension");
>           goto fail;
>       }
> +    s->nb_queued_bitmaps = 0;
>   
>       /* Bitmap directory was successfully updated, so, old data can be dropped.
>        * TODO it is better to reuse these clusters */
> @@ -1618,6 +1635,7 @@ int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
>       Qcow2BitmapList *bm_list;
>       const char *name = bdrv_dirty_bitmap_name(bitmap);
>       uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
> +    uint32_t nb_bitmaps;
>       int ret = 0;
>   
>       if (s->qcow_version < 3) {
> @@ -1636,11 +1654,12 @@ int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
>           goto fail;
>       }
>   
> -    if (s->nb_bitmaps == 0) {
> +    nb_bitmaps = s->nb_bitmaps + s->nb_queued_bitmaps;
> +    if (nb_bitmaps == 0) {
>           return 0;
>       }
>   
> -    if (s->nb_bitmaps >= QCOW2_MAX_BITMAPS) {
> +    if (nb_bitmaps >= QCOW2_MAX_BITMAPS) {
>           error_setg(errp,
>                      "Maximum number of persistent bitmaps is already reached");
>           ret = -EOVERFLOW;
> @@ -1667,6 +1686,8 @@ int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
>           goto fail;
>       }
>   
> +    s->nb_queued_bitmaps += 1;
> +
>       return 0;
>   fail:
>       error_prepend(errp, "Can't make bitmap '%s' persistent in '%s': ",
>
John Snow June 7, 2019, 6:04 p.m. UTC | #3
On 6/6/19 10:27 PM, Eric Blake wrote:
> On 6/6/19 1:41 PM, John Snow wrote:
>> When we check to see if we can store a bitmap, we don't check how many
>> we've queued up. This can cause a problem saving bitmaps on close
>> instead of when we request them to be added. With the stricter add
>> interface, prohibit these bitmaps specifically.
>>
>> To match, make the remove interface more strict as well; now rejecting
>> any requests to remove bitmaps that were never queued for storage.
>>
>> We don't need to "find" the bitmap when the interface has been given the
>> bitmap explicitly, but this is done to make sure that the bitmap given
>> actually does belong to the bs we were passed as a paranoia check to
>> enforce consistency.
>>
>> ---
> 
> Oops - that marker...
> 
>>
>> "What about directory size?" Please see the following patch.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
> 
> ...renders the S-o-b invisible.
> 
>> +++ b/block/qcow2-bitmap.c
>> @@ -1402,6 +1402,23 @@ static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list,
>>      return NULL;
>>  }
>>  
>> +static int qcow2_remove_queued_dirty_bitmap(
>> +    BlockDriverState *bs, const char *name, Error **errp)
>> +{
>> +    BDRVQcow2State *s = bs->opaque;
>> +    BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, name);
>> +    if (!bitmap) {
>> +        error_setg(errp, "Node '%s' has no stored or enqueued bitmap '%s'",
>> +                   bdrv_get_node_name(bs), name);
>> +        return -ENOENT;
>> +    }
>> +    assert(s->nb_queued_bitmaps > 0);
>> +    assert(bdrv_dirty_bitmap_get_persistence(bitmap));
>> +    s->nb_queued_bitmaps -= 1;
> 
> I tend to use -- over -= 1.
> 
>> @@ -1667,6 +1686,8 @@ int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
>>          goto fail;
>>      }
>>  
>> +    s->nb_queued_bitmaps += 1;
> 
> And again, for ++
> 
> Reviewed-by: Eric Blake <eblake@redhat.com>
> 

Wow, sorry, lots of Python lately!
John Snow June 7, 2019, 6:24 p.m. UTC | #4
On 6/7/19 10:58 AM, Vladimir Sementsov-Ogievskiy wrote:
> 06.06.2019 21:41, John Snow wrote:
>> When we check to see if we can store a bitmap, we don't check how many
>> we've queued up. This can cause a problem saving bitmaps on close
>> instead of when we request them to be added. With the stricter add
>> interface, prohibit these bitmaps specifically.
>>
>> To match, make the remove interface more strict as well; now rejecting
>> any requests to remove bitmaps that were never queued for storage.
>>
>> We don't need to "find" the bitmap when the interface has been given the
>> bitmap explicitly, but this is done to make sure that the bitmap given
>> actually does belong to the bs we were passed as a paranoia check to
>> enforce consistency.
> 
> If you want to check it, I'd really prefer to do it explictly,
> by adding "bool bdrv_has_dirty_bitmap(bs, bitmap) handler, or bitmap.bs field",
> instead of hiding it under inconvenient interface of helper, so we actually do
> 
> name = bdrv_dirty_bitmap_name(bitmap);
> bitmap = bdrv_find_dirty_bitmap(bs, name);
> 
> it really looks strange.
> 

You're right, it does look weird. It's not obvious when you read it what
the real purpose is. I'll try to rectify it with an explicit helper like
you suggest.

> Hmmm, when I read series cover letter and this commit message, I thought
> that you'll just calculate current number of persistent bitmaps on bs..
> Do we really need to introduce additional counters on qcow2 state?
> 

I suppose I could do that, too -- that seems a bit heavier than just
incrementing a counter, but it has less risk of getting out of sync.

> So, you want to check nb_queued + s->nb_bitmaps instead of just s->nb_bitmaps.
> 
> I think we can just count persistent dirty bitmaps and take that number
> (as, there should not be in-image bitmaps, for which we don't have in-ram
> version, but we can check it too and return an error on mismatch (or count
> in-image-not-in-ram bitmaps and this count to number of in-ram persistent
> bitmaps it seems an extra thing)..
> 

If I cache the bm_list as discussed in patch 2, we can actually avoid
re-enumerating all of the bitmaps and just go off of that data without
having to add any new counters.

--js
diff mbox series

Patch

diff --git a/block/qcow2.h b/block/qcow2.h
index ce07f003f7..ebf60ac236 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -317,6 +317,7 @@  typedef struct BDRVQcow2State {
     QCowSnapshot *snapshots;
 
     uint32_t nb_bitmaps;
+    uint32_t nb_queued_bitmaps;
     uint64_t bitmap_directory_size;
     uint64_t bitmap_directory_offset;
 
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index 4667f9e65a..084c42af57 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -450,11 +450,9 @@  void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
 }
 
 /**
- * Remove persistent dirty bitmap from the storage if it exists.
- * Absence of bitmap is not an error, because we have the following scenario:
- * BdrvDirtyBitmap can have .persistent = true but not yet saved and have no
- * stored version. For such bitmap bdrv_remove_persistent_dirty_bitmap() should
- * not fail.
+ * Remove a persistent dirty bitmap from storage,
+ * or dequeue it from being stored if it is enqueued.
+ *
  * This function doesn't release the corresponding BdrvDirtyBitmap.
  */
 int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 930a6c91ff..7193c66787 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -1402,6 +1402,23 @@  static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list,
     return NULL;
 }
 
+static int qcow2_remove_queued_dirty_bitmap(
+    BlockDriverState *bs, const char *name, Error **errp)
+{
+    BDRVQcow2State *s = bs->opaque;
+    BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, name);
+    if (!bitmap) {
+        error_setg(errp, "Node '%s' has no stored or enqueued bitmap '%s'",
+                   bdrv_get_node_name(bs), name);
+        return -ENOENT;
+    }
+    assert(s->nb_queued_bitmaps > 0);
+    assert(bdrv_dirty_bitmap_get_persistence(bitmap));
+    s->nb_queued_bitmaps -= 1;
+
+    return 0;
+}
+
 int qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
                                          BdrvDirtyBitmap *bitmap,
                                          Error **errp)
@@ -1413,9 +1430,7 @@  int qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
     const char *name = bdrv_dirty_bitmap_name(bitmap);
 
     if (s->nb_bitmaps == 0) {
-        /* Absence of the bitmap is not an error: see explanation above
-         * bdrv_remove_persistent_dirty_bitmap() definition. */
-        return 0;
+        return qcow2_remove_queued_dirty_bitmap(bs, name, errp);
     }
 
     if ((ret = bitmap_list_load(bs, &bm_list, errp))) {
@@ -1424,6 +1439,7 @@  int qcow2_remove_persistent_dirty_bitmap(BlockDriverState *bs,
 
     bm = find_bitmap_by_name(bm_list, name);
     if (bm == NULL) {
+        ret = qcow2_remove_queued_dirty_bitmap(bs, name, errp);
         goto fail;
     }
 
@@ -1544,6 +1560,7 @@  void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
         error_setg_errno(errp, -ret, "Failed to update bitmap extension");
         goto fail;
     }
+    s->nb_queued_bitmaps = 0;
 
     /* Bitmap directory was successfully updated, so, old data can be dropped.
      * TODO it is better to reuse these clusters */
@@ -1618,6 +1635,7 @@  int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
     Qcow2BitmapList *bm_list;
     const char *name = bdrv_dirty_bitmap_name(bitmap);
     uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
+    uint32_t nb_bitmaps;
     int ret = 0;
 
     if (s->qcow_version < 3) {
@@ -1636,11 +1654,12 @@  int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
         goto fail;
     }
 
-    if (s->nb_bitmaps == 0) {
+    nb_bitmaps = s->nb_bitmaps + s->nb_queued_bitmaps;
+    if (nb_bitmaps == 0) {
         return 0;
     }
 
-    if (s->nb_bitmaps >= QCOW2_MAX_BITMAPS) {
+    if (nb_bitmaps >= QCOW2_MAX_BITMAPS) {
         error_setg(errp,
                    "Maximum number of persistent bitmaps is already reached");
         ret = -EOVERFLOW;
@@ -1667,6 +1686,8 @@  int qcow2_add_persistent_dirty_bitmap(BlockDriverState *bs,
         goto fail;
     }
 
+    s->nb_queued_bitmaps += 1;
+
     return 0;
 fail:
     error_prepend(errp, "Can't make bitmap '%s' persistent in '%s': ",