Blob Blame History Raw
From be7252bada79ee542356dffaf5f3c568a5c7fec3 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 10 Aug 2021 08:39:15 +0100
Subject: [PATCH] delay: Fix delay-close

See comments in the code for how this has been fixed.

This only delays clients which use NBD_CMD_DISC (libnbd
nbd_shutdown(3)).  Clients which drop the connection obviously cannot
be delayed.  For example:

$ nbdkit --filter=delay null delay-close=3 \
    --run 'time nbdsh -u $uri -c "h.shutdown()"
           time nbdsh -u $uri -c "pass"'

real	0m3.061s     # Client used shutdown, was delayed
user	0m0.028s
sys	0m0.030s

real	0m0.058s     # Client disconnected, was not delayed
user	0m0.029s
sys	0m0.027s

Reported-by: Ming Xie
Fixes: commit de8dcd3a34a38b088a0f9a6f8ca754702ad1f598
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1991652
(cherry picked from commit 0cafebdb67d0d557ba1be8ea306b8acc5d9b2203)
---
 filters/delay/delay.c                 | 42 +++++++++++++++++++--------
 filters/delay/nbdkit-delay-filter.pod | 14 +++++++--
 2 files changed, 41 insertions(+), 15 deletions(-)

diff --git a/filters/delay/delay.c b/filters/delay/delay.c
index df3729a7..9252b855 100644
--- a/filters/delay/delay.c
+++ b/filters/delay/delay.c
@@ -39,6 +39,7 @@
 #include <stdbool.h>
 #include <errno.h>
 #include <limits.h>
+#include <time.h>
 
 #include <nbdkit-filter.h>
 
@@ -134,12 +135,6 @@ open_delay (int *err)
   return delay (delay_open_ms, err);
 }
 
-static int
-close_delay (int *err)
-{
-  return delay (delay_close_ms, err);
-}
-
 /* Called for each key=value passed on the command line. */
 static int
 delay_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
@@ -253,13 +248,36 @@ delay_open (nbdkit_next_open *next, nbdkit_context *nxdata,
   return NBDKIT_HANDLE_NOT_NEEDED;
 }
 
-/* Close connection. */
-static void
-delay_close (void *handle)
+/* Close connection.
+ *
+ * We cannot call nbdkit_nanosleep here because the socket may have
+ * been closed and that function will abort and return immediately.
+ * However we want to force a sleep (even if the server is shutting
+ * down) so use regular nanosleep instead.
+ *
+ * We cannot use the .close callback because that happens after the
+ * socket has closed, thus not delaying the client.  By using
+ * .finalize we can delay well-behaved clients (those that use
+ * NBD_CMD_DISC).  We cannot delay clients that drop the connection.
+ */
+static int
+delay_finalize (nbdkit_next *next, void *handle)
 {
-  int err;
+  const unsigned ms = delay_close_ms;
 
-  close_delay (&err);
+  if (ms > 0) {
+    struct timespec ts;
+
+    ts.tv_sec = ms / 1000;
+    ts.tv_nsec = (ms % 1000) * 1000000;
+    /* If nanosleep fails we don't really want to interrupt the chain
+     * of finalize calls through the other filters, so ignore any
+     * error here.
+     */
+    nanosleep (&ts, NULL);
+  }
+
+  return next->finalize (next);
 }
 
 /* Read data. */
@@ -340,7 +358,7 @@ static struct nbdkit_filter filter = {
   .config_help       = delay_config_help,
   .can_fast_zero     = delay_can_fast_zero,
   .open              = delay_open,
-  .close             = delay_close,
+  .finalize          = delay_finalize,
   .pread             = delay_pread,
   .pwrite            = delay_pwrite,
   .zero              = delay_zero,
diff --git a/filters/delay/nbdkit-delay-filter.pod b/filters/delay/nbdkit-delay-filter.pod
index 11ae544b..76614736 100644
--- a/filters/delay/nbdkit-delay-filter.pod
+++ b/filters/delay/nbdkit-delay-filter.pod
@@ -117,15 +117,23 @@ the plugin.
 
 =item B<delay-open=>NNB<ms>
 
+(nbdkit E<ge> 1.28)
+
+Delay open (client connection) by C<SECS> seconds or C<NN>
+milliseconds.
+
 =item B<delay-close=>SECS
 
 =item B<delay-close=>NNB<ms>
 
 (nbdkit E<ge> 1.28)
 
-Delay open and close operations by C<SECS> seconds or C<NN>
-milliseconds.  Open corresponds to client connection.  Close may not
-be visible to clients if they abruptly disconnect.
+Delay close (client disconnection) by C<SECS> seconds or C<NN>
+milliseconds.  This can also cause server shutdown to be delayed if
+clients are connected at the time.  This only affects clients that
+gracefully disconnect (using C<NBD_CMD_DISC> / libnbd function
+L<nbd_shutdown(3)>).  Clients that abruptly disconnect from the server
+cannot be delayed.
 
 =back
 
-- 
2.31.1