[Libguestfs] [PATCH nbdkit 1/6] server: Add new plugin/filter .block_size callback

Richard W.M. Jones rjones at redhat.com
Wed Feb 16 16:20:36 UTC 2022


This commit implements backend_block_size() and wires it up to a new
.block_size callback from the plugin through any filters on top.

This callback will allow plugins to pass their minimum, preferred and
maximum block size through the NBD protocol NBD_INFO_BLOCK_SIZE info
option.

The new function is not called from anywhere yet, so for the moment
this commit does nothing.
---
 include/nbdkit-filter.h |  4 ++++
 include/nbdkit-plugin.h |  3 +++
 server/internal.h       |  9 ++++++++
 server/backend.c        | 29 +++++++++++++++++++++++
 server/filters.c        | 17 ++++++++++++++
 server/plugins.c        | 51 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 113 insertions(+)

diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 8b587d70..4c620b4f 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -84,6 +84,8 @@ struct nbdkit_next_ops {
   /* These callbacks are the same as normal plugin operations. */
   int64_t (*get_size) (nbdkit_next *nxdata);
   const char * (*export_description) (nbdkit_next *nxdata);
+  int (*block_size) (nbdkit_next *nxdata,
+                     uint32_t *minimum, uint32_t *preferred, uint32_t *maximum);
 
   int (*can_write) (nbdkit_next *nxdata);
   int (*can_flush) (nbdkit_next *nxdata);
@@ -219,6 +221,8 @@ struct nbdkit_filter {
   int64_t (*get_size) (nbdkit_next *next,
                        void *handle);
   const char * (*export_description) (nbdkit_next *next, void *handle);
+  int (*block_size) (nbdkit_next *next, void *handle,
+                     uint32_t *minimum, uint32_t *preferred, uint32_t *maximum);
 
   int (*can_write) (nbdkit_next *next,
                     void *handle);
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 59ec11b6..50ff93df 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -146,6 +146,9 @@ struct nbdkit_plugin {
   const char * (*export_description) (void *handle);
 
   void (*cleanup) (void);
+
+  int (*block_size) (void *handle,
+                     uint32_t *minimum, uint32_t *preferred, uint32_t *maximum);
 };
 
 NBDKIT_EXTERN_DECL (void, nbdkit_set_error, (int err));
diff --git a/server/internal.h b/server/internal.h
index f4843164..c91d196e 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -216,6 +216,9 @@ struct context {
   unsigned char state;  /* Bitmask of HANDLE_* values */
 
   uint64_t exportsize;
+  uint32_t minimum_block_size;
+  uint32_t preferred_block_size;
+  uint32_t maximum_block_size;
   int can_write;
   int can_flush;
   int is_rotational;
@@ -368,6 +371,8 @@ struct backend {
 
   const char *(*export_description) (struct context *);
   int64_t (*get_size) (struct context *);
+  int (*block_size) (struct context *,
+                     uint32_t *minimum, uint32_t *preferred, uint32_t *maximum);
   int (*can_write) (struct context *);
   int (*can_flush) (struct context *);
   int (*is_rotational) (struct context *);
@@ -433,6 +438,10 @@ extern const char *backend_export_description (struct context *c)
   __attribute__((__nonnull__ (1)));
 extern int64_t backend_get_size (struct context *c)
   __attribute__((__nonnull__ (1)));
+extern int backend_block_size (struct context *c,
+                               uint32_t *minimum, uint32_t *preferred,
+                               uint32_t *maximum)
+  __attribute__((__nonnull__ (1, 2, 3, 4)));
 extern int backend_can_write (struct context *c)
   __attribute__((__nonnull__ (1)));
 extern int backend_can_flush (struct context *c)
diff --git a/server/backend.c b/server/backend.c
index 1bb3dac9..7d46b99d 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -214,6 +214,7 @@ static struct nbdkit_next_ops next_ops = {
   .finalize = backend_finalize,
   .export_description = backend_export_description,
   .get_size = backend_get_size,
+  .block_size = backend_block_size,
   .can_write = backend_can_write,
   .can_flush = backend_can_flush,
   .is_rotational = backend_is_rotational,
@@ -265,6 +266,7 @@ backend_open (struct backend *b, int readonly, const char *exportname,
   c->conn = shared ? NULL : conn;
   c->state = 0;
   c->exportsize = -1;
+  c->minimum_block_size = c->preferred_block_size = c->maximum_block_size = -1;
   c->can_write = readonly ? 0 : -1;
   c->can_flush = -1;
   c->is_rotational = -1;
@@ -418,6 +420,33 @@ backend_get_size (struct context *c)
   return c->exportsize;
 }
 
+int
+backend_block_size (struct context *c,
+                    uint32_t *minimum, uint32_t *preferred, uint32_t *maximum)
+{
+  PUSH_CONTEXT_FOR_SCOPE (c);
+  struct backend *b = c->b;
+  int r;
+
+  assert (c->handle && (c->state & HANDLE_CONNECTED));
+  if (c->minimum_block_size != -1) {
+    *minimum = c->minimum_block_size;
+    *preferred = c->preferred_block_size;
+    *maximum = c->maximum_block_size;
+    return 0;
+  }
+  else {
+    controlpath_debug ("%s: block_size", b->name);
+    r = b->block_size (c, minimum, preferred, maximum);
+    if (r == 0) {
+      c->minimum_block_size = *minimum;
+      c->preferred_block_size = *preferred;
+      c->maximum_block_size = *maximum;
+    }
+    return r;
+  }
+}
+
 int
 backend_can_write (struct context *c)
 {
diff --git a/server/filters.c b/server/filters.c
index 93ee55f0..ae7e1a56 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -358,6 +358,22 @@ filter_get_size (struct context *c)
     return backend_get_size (c_next);
 }
 
+static int
+filter_block_size (struct context *c,
+                   uint32_t *minimum, uint32_t *preferred, uint32_t *maximum)
+{
+  struct backend *b = c->b;
+  struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = c->c_next;
+
+  if (f->filter.block_size)
+    return f->filter.block_size (c_next, c->handle,
+                                 minimum, preferred, maximum);
+  else
+    return backend_block_size (c_next,
+                               minimum, preferred, maximum);
+}
+
 static int
 filter_can_write (struct context *c)
 {
@@ -621,6 +637,7 @@ static struct backend filter_functions = {
   .close = filter_close,
   .export_description = filter_export_description,
   .get_size = filter_get_size,
+  .block_size = filter_block_size,
   .can_write = filter_can_write,
   .can_flush = filter_can_flush,
   .is_rotational = filter_is_rotational,
diff --git a/server/plugins.c b/server/plugins.c
index b633c107..34d7a10c 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -46,6 +46,7 @@
 
 #include "internal.h"
 #include "minmax.h"
+#include "ispowerof2.h"
 
 /* We extend the generic backend struct with extra fields relating
  * to this plugin.
@@ -173,6 +174,7 @@ plugin_dump_fields (struct backend *b)
   HAS (close);
   HAS (export_description);
   HAS (get_size);
+  HAS (block_size);
   HAS (can_write);
   HAS (can_flush);
   HAS (is_rotational);
@@ -408,6 +410,54 @@ plugin_get_size (struct context *c)
   return p->plugin.get_size (c->handle);
 }
 
+static int
+plugin_block_size (struct context *c,
+                   uint32_t *minimum, uint32_t *preferred, uint32_t *maximum)
+{
+  struct backend *b = c->b;
+  struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+  int r;
+
+  if (p->plugin.block_size) {
+    r = p->plugin.block_size (c->handle, minimum, preferred, maximum);
+    if (r == 0) {
+      /* To make scripting easier, it's permitted to set
+       * minimum = preferred = maximum = 0 and return 0.
+       * That means "no information", and works the same
+       * way as the else clause below.
+       */
+      if (*minimum == 0 && *preferred == 0 && *maximum == 0)
+        return 0;
+
+      if (*minimum < 1) {
+        nbdkit_error ("plugin must set minimum block size >= 1");
+        r = -1;
+      }
+      if (! is_power_of_2 (*minimum)) {
+        nbdkit_error ("plugin must set minimum block size to a power of 2");
+        r = -1;
+      }
+      if (*minimum > *preferred || *preferred > *maximum) {
+        nbdkit_error ("plugin must set minimum block size "
+                      "<= preferred <= maximum");
+        r = -1;
+      }
+      /* XXX Other checks should be performed here:
+       * preferred and maximum should be multiples of the minimum.
+       */
+    }
+    return r;
+  }
+  else {
+    /* If there is no .block_size call then return minimum = preferred
+     * = maximum = 0, which is a sentinel meaning don't send the
+     * NBD_INFO_BLOCK_SIZE message.
+     */
+    *minimum = *preferred = *maximum = 0;
+    return 0;
+  }
+}
+
 static int
 normalize_bool (int value)
 {
@@ -824,6 +874,7 @@ static struct backend plugin_functions = {
   .close = plugin_close,
   .export_description = plugin_export_description,
   .get_size = plugin_get_size,
+  .block_size = plugin_block_size,
   .can_write = plugin_can_write,
   .can_flush = plugin_can_flush,
   .is_rotational = plugin_is_rotational,
-- 
2.35.1




More information about the Libguestfs mailing list