diff mbox series

[v2,12/15] nbd/server: Support inactive nodes

Message ID 20250131095051.49708-3-kwolf@redhat.com (mailing list archive)
State New
Headers show
Series block: Managing inactive nodes (QSD migration) | expand

Commit Message

Kevin Wolf Jan. 31, 2025, 9:50 a.m. UTC
In order to support running an NBD export on inactive nodes, we must
make sure to return errors for any operations that aren't allowed on
inactive nodes. Reads are the only operation we know we need for
inactive images, so to err on the side of caution, return errors for
everything else, even if some operations could possibly be okay.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 nbd/server.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

Comments

Eric Blake Feb. 3, 2025, 7:17 p.m. UTC | #1
On Fri, Jan 31, 2025 at 10:50:48AM +0100, Kevin Wolf wrote:
> In order to support running an NBD export on inactive nodes, we must
> make sure to return errors for any operations that aren't allowed on
> inactive nodes. Reads are the only operation we know we need for
> inactive images, so to err on the side of caution, return errors for
> everything else, even if some operations could possibly be okay.

We may still find a use case for block status on an inactive node
(especially if that helps us take more accurate snapshots, which is
the whole point of wanting to read pre-activation).  But I'm okay if
we defer that to a separate patch only if it actually proves to be
needed.

> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  nbd/server.c | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>

Reviewed-by: Eric Blake <eblake@redhat.com>
Stefan Hajnoczi Feb. 3, 2025, 7:19 p.m. UTC | #2
On Fri, Jan 31, 2025 at 10:50:48AM +0100, Kevin Wolf wrote:
> In order to support running an NBD export on inactive nodes, we must
> make sure to return errors for any operations that aren't allowed on
> inactive nodes. Reads are the only operation we know we need for
> inactive images, so to err on the side of caution, return errors for
> everything else, even if some operations could possibly be okay.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  nbd/server.c | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> diff --git a/nbd/server.c b/nbd/server.c
> index f64e47270c..2076fb2666 100644
> --- a/nbd/server.c
> +++ b/nbd/server.c
> @@ -2026,6 +2026,7 @@ static void nbd_export_delete(BlockExport *blk_exp)
>  const BlockExportDriver blk_exp_nbd = {
>      .type               = BLOCK_EXPORT_TYPE_NBD,
>      .instance_size      = sizeof(NBDExport),
> +    .supports_inactive  = true,
>      .create             = nbd_export_create,
>      .delete             = nbd_export_delete,
>      .request_shutdown   = nbd_export_request_shutdown,
> @@ -2920,6 +2921,22 @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
>      NBDExport *exp = client->exp;
>      char *msg;
>      size_t i;
> +    bool inactive;
> +
> +    WITH_GRAPH_RDLOCK_GUARD() {
> +        inactive = bdrv_is_inactive(blk_bs(exp->common.blk));
> +        if (inactive) {
> +            switch (request->type) {
> +            case NBD_CMD_READ:
> +                /* These commands are allowed on inactive nodes */
> +                break;
> +            default:
> +                /* Return an error for the rest */
> +                return nbd_send_generic_reply(client, request, -EPERM,
> +                                              "export is inactive", errp);
> +            }
> +        }
> +    }

Hmm...end of lock guard. What prevents the race where inactive changes
before the request is performed?

>  
>      switch (request->type) {
>      case NBD_CMD_CACHE:
> -- 
> 2.48.1
>
Kevin Wolf Feb. 4, 2025, 5:10 p.m. UTC | #3
Am 03.02.2025 um 20:19 hat Stefan Hajnoczi geschrieben:
> On Fri, Jan 31, 2025 at 10:50:48AM +0100, Kevin Wolf wrote:
> > In order to support running an NBD export on inactive nodes, we must
> > make sure to return errors for any operations that aren't allowed on
> > inactive nodes. Reads are the only operation we know we need for
> > inactive images, so to err on the side of caution, return errors for
> > everything else, even if some operations could possibly be okay.
> > 
> > Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> > ---
> >  nbd/server.c | 17 +++++++++++++++++
> >  1 file changed, 17 insertions(+)
> > 
> > diff --git a/nbd/server.c b/nbd/server.c
> > index f64e47270c..2076fb2666 100644
> > --- a/nbd/server.c
> > +++ b/nbd/server.c
> > @@ -2026,6 +2026,7 @@ static void nbd_export_delete(BlockExport *blk_exp)
> >  const BlockExportDriver blk_exp_nbd = {
> >      .type               = BLOCK_EXPORT_TYPE_NBD,
> >      .instance_size      = sizeof(NBDExport),
> > +    .supports_inactive  = true,
> >      .create             = nbd_export_create,
> >      .delete             = nbd_export_delete,
> >      .request_shutdown   = nbd_export_request_shutdown,
> > @@ -2920,6 +2921,22 @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
> >      NBDExport *exp = client->exp;
> >      char *msg;
> >      size_t i;
> > +    bool inactive;
> > +
> > +    WITH_GRAPH_RDLOCK_GUARD() {
> > +        inactive = bdrv_is_inactive(blk_bs(exp->common.blk));
> > +        if (inactive) {
> > +            switch (request->type) {
> > +            case NBD_CMD_READ:
> > +                /* These commands are allowed on inactive nodes */
> > +                break;
> > +            default:
> > +                /* Return an error for the rest */
> > +                return nbd_send_generic_reply(client, request, -EPERM,
> > +                                              "export is inactive", errp);
> > +            }
> > +        }
> > +    }
> 
> Hmm...end of lock guard. What prevents the race where inactive changes
> before the request is performed?

That's a good question. Probably nothing. Extending the lock guard to
cover the rest of the function wouldn't prevent it either because
inactivating doesn't change the structure of the graph and therefore
also doesn't take the writer lock.

We should probably drain nodes around setting BDRV_O_INACTIVE. Generally
the expectation has always been that the block node is idle when we try
to inactivate an image. With exports, this isn't automatically true any
more, but draining gives us the guarantee we need.

This seems to also fix a qed crash I noticed with the new test cases
where the timer still wants to write to the image after we set the
inactive flag. Draining cancels the timer.

Kevin

diff --git a/block.c b/block.c
index 7eeb8d076e..1601b25f66 100644
--- a/block.c
+++ b/block.c
@@ -7032,7 +7032,9 @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level)
         return -EPERM;
     }

+    bdrv_drained_begin(bs);
     bs->open_flags |= BDRV_O_INACTIVE;
+    bdrv_drained_end(bs);

     /*
      * Update permissions, they may differ for inactive nodes.
diff mbox series

Patch

diff --git a/nbd/server.c b/nbd/server.c
index f64e47270c..2076fb2666 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -2026,6 +2026,7 @@  static void nbd_export_delete(BlockExport *blk_exp)
 const BlockExportDriver blk_exp_nbd = {
     .type               = BLOCK_EXPORT_TYPE_NBD,
     .instance_size      = sizeof(NBDExport),
+    .supports_inactive  = true,
     .create             = nbd_export_create,
     .delete             = nbd_export_delete,
     .request_shutdown   = nbd_export_request_shutdown,
@@ -2920,6 +2921,22 @@  static coroutine_fn int nbd_handle_request(NBDClient *client,
     NBDExport *exp = client->exp;
     char *msg;
     size_t i;
+    bool inactive;
+
+    WITH_GRAPH_RDLOCK_GUARD() {
+        inactive = bdrv_is_inactive(blk_bs(exp->common.blk));
+        if (inactive) {
+            switch (request->type) {
+            case NBD_CMD_READ:
+                /* These commands are allowed on inactive nodes */
+                break;
+            default:
+                /* Return an error for the rest */
+                return nbd_send_generic_reply(client, request, -EPERM,
+                                              "export is inactive", errp);
+            }
+        }
+    }
 
     switch (request->type) {
     case NBD_CMD_CACHE: