diff mbox series

[v2,2/3] qapi: nbd-export: allow select bitmaps by node/name pair

Message ID 20220314213226.362217-3-v.sementsov-og@mail.ru (mailing list archive)
State New, archived
Headers show
Series qapi: nbd-export: select bitmap by node/name pair | expand

Commit Message

Vladimir Sementsov-Ogievskiy March 14, 2022, 9:32 p.m. UTC
From: Vladimir Sementsov-Ogievskiy <v.sementsov-og@ya.ru>

Hi all! Current logic of relying on search through backing chain is not
safe neither convenient.

Sometimes it leads to necessity of extra bitmap copying. Also, we are
going to add "snapshot-access" driver, to access some snapshot state
through NBD. And this driver is not formally a filter, and of course
it's not a COW format driver. So, searching through backing chain will
not work. Instead of widening the workaround of bitmap searching, let's
extend the interface so that user can select bitmap precisely.

Note, that checking for bitmap active status is not copied to the new
API, I don't see a reason for it, user should understand the risks. And
anyway, bitmap from other node is unrelated to this export being
read-only or read-write.

Signed-off-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@ya.ru>
---
 blockdev-nbd.c         |  8 +++++-
 nbd/server.c           | 63 +++++++++++++++++++++++++++---------------
 qapi/block-export.json |  5 +++-
 qemu-nbd.c             | 11 ++++++--
 4 files changed, 61 insertions(+), 26 deletions(-)

Comments

Eric Blake March 16, 2022, 9:28 p.m. UTC | #1
On Tue, Mar 15, 2022 at 12:32:25AM +0300, Vladimir Sementsov-Ogievskiy wrote:
> From: Vladimir Sementsov-Ogievskiy <v.sementsov-og@ya.ru>
> 
> Hi all! Current logic of relying on search through backing chain is not
> safe neither convenient.
> 
> Sometimes it leads to necessity of extra bitmap copying. Also, we are
> going to add "snapshot-access" driver, to access some snapshot state
> through NBD. And this driver is not formally a filter, and of course
> it's not a COW format driver. So, searching through backing chain will
> not work. Instead of widening the workaround of bitmap searching, let's
> extend the interface so that user can select bitmap precisely.
> 
> Note, that checking for bitmap active status is not copied to the new
> API, I don't see a reason for it, user should understand the risks. And
> anyway, bitmap from other node is unrelated to this export being
> read-only or read-write.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@ya.ru>
> ---
>  blockdev-nbd.c         |  8 +++++-
>  nbd/server.c           | 63 +++++++++++++++++++++++++++---------------
>  qapi/block-export.json |  5 +++-
>  qemu-nbd.c             | 11 ++++++--
>  4 files changed, 61 insertions(+), 26 deletions(-)
> 

> @@ -1709,37 +1709,56 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
>      }
>      exp->export_bitmaps = g_new0(BdrvDirtyBitmap *, exp->nr_export_bitmaps);
>      for (i = 0, bitmaps = arg->bitmaps; bitmaps;
> -         i++, bitmaps = bitmaps->next) {
> -        const char *bitmap = bitmaps->value;
> +         i++, bitmaps = bitmaps->next)
> +    {
> +        const char *bitmap;

I'm not sure if our prevailing style splits { to its own line on a
multi-line 'for'.  But this is a cosmetic question, not one of
correctness.

> +        case QTYPE_QDICT:
> +            bitmap = bitmaps->value->u.external.name;
> +            bm = block_dirty_bitmap_lookup(bitmaps->value->u.external.node,
> +                                           bitmap, NULL, errp);
> +            if (!bm) {
> +                ret = -ENOENT;
> +                goto fail;
> +            }
> +            break;
> +        default:
> +            abort();

Not sure if g_assert_not_reached() or __builtin_unreachable() would be
any better here.  I'm fine with the abort() for now.

> +++ b/qapi/block-export.json
> @@ -6,6 +6,7 @@
>  ##
>  
>  { 'include': 'sockets.json' }
> +{ 'include': 'block-core.json' }

Hmm.  Does this extra inclusion negatively impact qemu-storage-daemon,
since that is why we created block-export.json in the first place (to
minimize the stuff that qsd pulled in without needing all of
block-core.json)?  In other words, would it be better to move
BlockDirtyBitmapOrStr to this file?

Everything else looks okay with this patch.
Vladimir Sementsov-Ogievskiy March 21, 2022, 11:50 a.m. UTC | #2
17.03.2022 00:28, Eric Blake wrote:
> On Tue, Mar 15, 2022 at 12:32:25AM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> From: Vladimir Sementsov-Ogievskiy <v.sementsov-og@ya.ru>
>>
>> Hi all! Current logic of relying on search through backing chain is not
>> safe neither convenient.
>>
>> Sometimes it leads to necessity of extra bitmap copying. Also, we are
>> going to add "snapshot-access" driver, to access some snapshot state
>> through NBD. And this driver is not formally a filter, and of course
>> it's not a COW format driver. So, searching through backing chain will
>> not work. Instead of widening the workaround of bitmap searching, let's
>> extend the interface so that user can select bitmap precisely.
>>
>> Note, that checking for bitmap active status is not copied to the new
>> API, I don't see a reason for it, user should understand the risks. And
>> anyway, bitmap from other node is unrelated to this export being
>> read-only or read-write.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <v.sementsov-og@ya.ru>
>> ---
>>   blockdev-nbd.c         |  8 +++++-
>>   nbd/server.c           | 63 +++++++++++++++++++++++++++---------------
>>   qapi/block-export.json |  5 +++-
>>   qemu-nbd.c             | 11 ++++++--
>>   4 files changed, 61 insertions(+), 26 deletions(-)
>>
> 
>> @@ -1709,37 +1709,56 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
>>       }
>>       exp->export_bitmaps = g_new0(BdrvDirtyBitmap *, exp->nr_export_bitmaps);
>>       for (i = 0, bitmaps = arg->bitmaps; bitmaps;
>> -         i++, bitmaps = bitmaps->next) {
>> -        const char *bitmap = bitmaps->value;
>> +         i++, bitmaps = bitmaps->next)
>> +    {
>> +        const char *bitmap;
> 
> I'm not sure if our prevailing style splits { to its own line on a
> multi-line 'for'.  But this is a cosmetic question, not one of
> correctness.
> 
>> +        case QTYPE_QDICT:
>> +            bitmap = bitmaps->value->u.external.name;
>> +            bm = block_dirty_bitmap_lookup(bitmaps->value->u.external.node,
>> +                                           bitmap, NULL, errp);
>> +            if (!bm) {
>> +                ret = -ENOENT;
>> +                goto fail;
>> +            }
>> +            break;
>> +        default:
>> +            abort();
> 
> Not sure if g_assert_not_reached() or __builtin_unreachable() would be
> any better here.  I'm fine with the abort() for now.
> 
>> +++ b/qapi/block-export.json
>> @@ -6,6 +6,7 @@
>>   ##
>>   
>>   { 'include': 'sockets.json' }
>> +{ 'include': 'block-core.json' }
> 
> Hmm.  Does this extra inclusion negatively impact qemu-storage-daemon,
> since that is why we created block-export.json in the first place (to
> minimize the stuff that qsd pulled in without needing all of
> block-core.json)?  In other words, would it be better to move
> BlockDirtyBitmapOrStr to this file?

And include block-export in block-core?

Another alternative is to move BlockDirtyBitmapOrStr to a separate file included from both block-export and block-core but that seems to be too much.

> 
> Everything else looks okay with this patch.
>
Eric Blake March 21, 2022, 1:30 p.m. UTC | #3
On Mon, Mar 21, 2022 at 02:50:25PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > +++ b/qapi/block-export.json
> > > @@ -6,6 +6,7 @@
> > >   ##
> > >   { 'include': 'sockets.json' }
> > > +{ 'include': 'block-core.json' }
> > 
> > Hmm.  Does this extra inclusion negatively impact qemu-storage-daemon,
> > since that is why we created block-export.json in the first place (to
> > minimize the stuff that qsd pulled in without needing all of
> > block-core.json)?  In other words, would it be better to move
> > BlockDirtyBitmapOrStr to this file?
> 
> And include block-export in block-core?

Right now, we have:

qapi/block-core.json "Block core (VM unrelated)" - includes
{common,crypto,job,sockets}.json

qapi/block-export.json "Block device exports" - includes sockets.json

qapi/block.json "Additional block stuff (VM related)" - includes block-core.json

Kevin, you forked off qapi/block-export.json.  What do you propose here?

> 
> Another alternative is to move BlockDirtyBitmapOrStr to a separate file included from both block-export and block-core but that seems to be too much.

Indeed, that feels like a step too far; we already have confusion on
which file to stick new stuff in, and adding another file won't help
that.
Vladimir Sementsov-Ogievskiy April 8, 2022, 8:27 p.m. UTC | #4
17.03.2022 00:28, Eric Blake wrote:
>> +++ b/qapi/block-export.json
>> @@ -6,6 +6,7 @@
>>   ##
>>   
>>   { 'include': 'sockets.json' }
>> +{ 'include': 'block-core.json' }
> Hmm.  Does this extra inclusion negatively impact qemu-storage-daemon,
> since that is why we created block-export.json in the first place (to
> minimize the stuff that qsd pulled in without needing all of
> block-core.json)?  In other words, would it be better to move
> BlockDirtyBitmapOrStr to this file?

Actually, looking at storage-daemon/qapi/qapi-schema.json I see block-cores.json.

That's block.json which is not mentioned in storage-daemon/qapi/qapi-schema.json.

So, I think it's OK to keep simple include for now.
Eric Blake April 22, 2022, 8:04 p.m. UTC | #5
On Fri, Apr 08, 2022 at 11:27:42PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> 17.03.2022 00:28, Eric Blake wrote:
> > > +++ b/qapi/block-export.json
> > > @@ -6,6 +6,7 @@
> > >   ##
> > >   { 'include': 'sockets.json' }
> > > +{ 'include': 'block-core.json' }
> > Hmm.  Does this extra inclusion negatively impact qemu-storage-daemon,
> > since that is why we created block-export.json in the first place (to
> > minimize the stuff that qsd pulled in without needing all of
> > block-core.json)?  In other words, would it be better to move
> > BlockDirtyBitmapOrStr to this file?
> 
> Actually, looking at storage-daemon/qapi/qapi-schema.json I see block-cores.json.
> 
> That's block.json which is not mentioned in storage-daemon/qapi/qapi-schema.json.
> 
> So, I think it's OK to keep simple include for now.

We're early enough in the 7.1 cycle that if someone proposes a reason
why this would need to change, then we can adjust it.

So for now, I'm adding

Reviewed-by: Eric Blake <eblake@redhat.com>

and queuing this series through my NBD tree.
diff mbox series

Patch

diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 9840d25a82..7f6531cba0 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -211,8 +211,14 @@  void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp)
     QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd,
                        qapi_NbdServerAddOptions_base(arg));
     if (arg->has_bitmap) {
+        BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
+
+        *el = (BlockDirtyBitmapOrStr) {
+            .type = QTYPE_QSTRING,
+            .u.local = g_strdup(arg->bitmap),
+        };
         export_opts->u.nbd.has_bitmaps = true;
-        QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, g_strdup(arg->bitmap));
+        QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el);
     }
 
     /*
diff --git a/nbd/server.c b/nbd/server.c
index 5da884c2fc..3956602c0a 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1643,7 +1643,7 @@  static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
     uint64_t perm, shared_perm;
     bool readonly = !exp_args->writable;
     bool shared = !exp_args->writable;
-    strList *bitmaps;
+    BlockDirtyBitmapOrStrList *bitmaps;
     size_t i;
     int ret;
 
@@ -1709,37 +1709,56 @@  static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
     }
     exp->export_bitmaps = g_new0(BdrvDirtyBitmap *, exp->nr_export_bitmaps);
     for (i = 0, bitmaps = arg->bitmaps; bitmaps;
-         i++, bitmaps = bitmaps->next) {
-        const char *bitmap = bitmaps->value;
+         i++, bitmaps = bitmaps->next)
+    {
+        const char *bitmap;
         BlockDriverState *bs = blk_bs(blk);
         BdrvDirtyBitmap *bm = NULL;
 
-        while (bs) {
-            bm = bdrv_find_dirty_bitmap(bs, bitmap);
-            if (bm != NULL) {
-                break;
+        switch (bitmaps->value->type) {
+        case QTYPE_QSTRING:
+            bitmap = bitmaps->value->u.local;
+            while (bs) {
+                bm = bdrv_find_dirty_bitmap(bs, bitmap);
+                if (bm != NULL) {
+                    break;
+                }
+
+                bs = bdrv_filter_or_cow_bs(bs);
             }
 
-            bs = bdrv_filter_or_cow_bs(bs);
-        }
+            if (bm == NULL) {
+                ret = -ENOENT;
+                error_setg(errp, "Bitmap '%s' is not found",
+                           bitmaps->value->u.local);
+                goto fail;
+            }
 
-        if (bm == NULL) {
-            ret = -ENOENT;
-            error_setg(errp, "Bitmap '%s' is not found", bitmap);
-            goto fail;
+            if (readonly && bdrv_is_writable(bs) &&
+                bdrv_dirty_bitmap_enabled(bm)) {
+                ret = -EINVAL;
+                error_setg(errp, "Enabled bitmap '%s' incompatible with "
+                           "readonly export", bitmap);
+                goto fail;
+            }
+            break;
+        case QTYPE_QDICT:
+            bitmap = bitmaps->value->u.external.name;
+            bm = block_dirty_bitmap_lookup(bitmaps->value->u.external.node,
+                                           bitmap, NULL, errp);
+            if (!bm) {
+                ret = -ENOENT;
+                goto fail;
+            }
+            break;
+        default:
+            abort();
         }
 
-        if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) {
-            ret = -EINVAL;
-            goto fail;
-        }
+        assert(bm);
 
-        if (readonly && bdrv_is_writable(bs) &&
-            bdrv_dirty_bitmap_enabled(bm)) {
+        if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) {
             ret = -EINVAL;
-            error_setg(errp,
-                       "Enabled bitmap '%s' incompatible with readonly export",
-                       bitmap);
             goto fail;
         }
 
diff --git a/qapi/block-export.json b/qapi/block-export.json
index f183522d0d..6afed472ff 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -6,6 +6,7 @@ 
 ##
 
 { 'include': 'sockets.json' }
+{ 'include': 'block-core.json' }
 
 ##
 # @NbdServerOptions:
@@ -89,6 +90,7 @@ 
 #           @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with
 #           the metadata context name "qemu:dirty-bitmap:BITMAP" to inspect
 #           each bitmap.
+#           Since 7.1 bitmap may be specified by node/name pair.
 #
 # @allocation-depth: Also export the allocation depth map for @device, so
 #                    the NBD client can use NBD_OPT_SET_META_CONTEXT with
@@ -99,7 +101,8 @@ 
 ##
 { 'struct': 'BlockExportOptionsNbd',
   'base': 'BlockExportOptionsNbdBase',
-  'data': { '*bitmaps': ['str'], '*allocation-depth': 'bool' } }
+  'data': { '*bitmaps': ['BlockDirtyBitmapOrStr'],
+            '*allocation-depth': 'bool' } }
 
 ##
 # @BlockExportOptionsVhostUserBlk:
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 713e7557a9..72138f703e 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -567,7 +567,7 @@  int main(int argc, char **argv)
     QDict *options = NULL;
     const char *export_name = NULL; /* defaults to "" later for server mode */
     const char *export_description = NULL;
-    strList *bitmaps = NULL;
+    BlockDirtyBitmapOrStrList *bitmaps = NULL;
     bool alloc_depth = false;
     const char *tlscredsid = NULL;
     const char *tlshostname = NULL;
@@ -687,7 +687,14 @@  int main(int argc, char **argv)
             alloc_depth = true;
             break;
         case 'B':
-            QAPI_LIST_PREPEND(bitmaps, g_strdup(optarg));
+            {
+                BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
+                *el = (BlockDirtyBitmapOrStr) {
+                    .type = QTYPE_QSTRING,
+                    .u.local = g_strdup(optarg),
+                };
+                QAPI_LIST_PREPEND(bitmaps, el);
+            }
             break;
         case 'k':
             sockpath = optarg;