[Libguestfs] [libnbd PATCH 4/6] states: Prepare for aio notify callback

Eric Blake eblake at redhat.com
Tue Jul 2 14:48:12 UTC 2019


On 6/29/19 8:28 AM, Eric Blake wrote:
> Having the client polling thread perform an O(n) loop over all known
> in-flight commands after each time the poll woke up is somewhat
> inefficient, and in a multi-threaded setup requires additional locking
> beyond libnbd to track the set of known command handles.  Better is a
> way for aio commands to call a notify callback the moment a specific
> command is ready to complete, and then a separate thread can gather
> the final completion status using just libnbd's locking, making the
> polling loop more efficient.  This also provides an opportunity to
> clean up any opaque data and/or change the final command status (for
> example, writing a strict validator for nbd_aio_pread_structured can
> change the command from success to failure if the server violated
> protocol by not returning chunks to cover the entire read).
> 
> We also want the client to be aware of any issued/in-flight commands
> that failed because they were stranded when the state machine moved to
> CLOSED or DEAD.  Previously, nbd_aio_command_completed() would never
> locate such stranded commands, but adding a common point to fire the
> notifier for such commands makes it also possible to move those
> commands to the completion queue.
> 
> This patch sets up the framework, with observable effects for stranded
> commands per the testsuite changes, but nothing yet actually sets the
> notify callback; that will come in the next patch.
> ---

> +/* Forcefully fail any remaining in-flight commands in list */
> +void abort_commands (struct nbd_handle *h,
> +                     struct command_in_flight **list)
> +{
> +  struct command_in_flight *prev_cmd, *cmd;
> +
> +  for (cmd = *list, prev_cmd = NULL;
> +       cmd != NULL;
> +       prev_cmd = cmd, cmd = cmd->next) {
> +    if (cmd->cb.notify && cmd->type != NBD_CMD_DISC) {
> +      int error = cmd->error ? cmd->error : ENOTCONN;
> +
> +      if (cmd->cb.notify (cmd->cb.opaque, cmd->handle, &error) == -1 && error)
> +        cmd->error = error;
> +    }

Note that this special-cases NBD_CMD_DISC - since we did not return a
handle to the user, nor add an nbd_aio_disconnect_notify() variant, and
since the server (if compliant) does not reply to NBD_CMD_DISC, I
decided it did not make sense to call a notify callback for that command
(instead, you know the disconnect command completed when the state
machine moves to CLOSED or DEAD).

But my special-casing was incomplete; I'm squashing this in before pushing:

diff --git a/lib/aio.c b/lib/aio.c
index b29378b..748665e 100644
--- a/lib/aio.c
+++ b/lib/aio.c
@@ -69,7 +69,7 @@ nbd_unlocked_aio_command_completed (struct nbd_handle *h,
     if (cmd->handle == handle)
       break;
   }
-  if (!cmd)
+  if (!cmd || cmd->type == NBD_CMD_DISC)
     return 0;

   type = cmd->type;
@@ -103,6 +103,14 @@ nbd_unlocked_aio_command_completed (struct
nbd_handle *h,
 int64_t
 nbd_unlocked_aio_peek_command_completed (struct nbd_handle *h)
 {
+  /* Special case NBD_CMD_DISC, as it does not have a user-visible
handle */
+  if (h->cmds_done && h->cmds_done->type == NBD_CMD_DISC) {
+    struct command_in_flight *cmd = h->cmds_done;
+
+    h->cmds_done = cmd->next;
+    free (cmd);
+  }
+
   if (h->cmds_done != NULL)
     return h->cmds_done->handle;

diff --git a/lib/disconnect.c b/lib/disconnect.c
index 53de386..5bbc64b 100644
--- a/lib/disconnect.c
+++ b/lib/disconnect.c
@@ -60,10 +60,11 @@ nbd_unlocked_aio_disconnect (struct nbd_handle *h,
uint32_t flags)
     return -1;
   h->disconnect_request = true;

-  /* This will leave the command on the in-flight list.  Is this a
-   * problem?  Probably it isn't.  If it is, we could add a flag to
-   * the command struct to tell SEND_REQUEST not to add it to the
-   * in-flight list.
+  /* As the server does not reply to this command, it is left
+   * in-flight until the cleanup performed when moving to CLOSED or
+   * DEAD.  We don't return a handle to the user, and thus also
+   * special case things so that the user cannot request the status of
+   * this command during aio_[peek_]command_completed.
    */
   return 0;
 }
diff --git a/tests/server-death.c b/tests/server-death.c
index f8747e4..18ca5f8 100644
--- a/tests/server-death.c
+++ b/tests/server-death.c
@@ -145,6 +145,27 @@ main (int argc, char *argv[])
     goto fail;
   }

+  /* With all commands retired, no further command should be pending */
+  if (nbd_aio_in_flight (nbd) != 0) {
+    fprintf (stderr, "%s: test failed: nbd_aio_in_flight\n",
+             argv[0]);
+    goto fail;
+  }
+  if (nbd_aio_peek_command_completed (nbd) != -1) {
+    fprintf (stderr, "%s: test failed: nbd_aio_peek_command_completed\n",
+             argv[0]);
+    goto fail;
+  }
+  msg = nbd_get_error ();
+  err = nbd_get_errno ();
+  printf ("error: \"%s\"\n", msg);
+  printf ("errno: %d (%s)\n", err, strerror (err));
+  if (err != EINVAL) {
+    fprintf (stderr, "%s: test failed: unexpected errno %d (%s)\n",
argv[0],
+             err, strerror (err));
+    goto fail;
+  }
+
   close (fd);
   unlink (pidfile);
   nbd_close (nbd);
-- 
2.20.1



-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3226
Virtualization:  qemu.org | libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20190702/285d9e4a/attachment.sig>


More information about the Libguestfs mailing list