[Libguestfs] [PATCH nbdkit v5 FINAL 01/19] server: Implement extents/can_extents calls for plugins and filters.

Richard W.M. Jones rjones at redhat.com
Thu Mar 28 16:18:28 UTC 2019


This new pair of callbacks allows plugins to describe which extents in
the virtual disk are allocated, holes or zeroes.
---
 docs/nbdkit-filter.pod  |  83 ++++++++++++++++
 docs/nbdkit-plugin.pod  |  97 +++++++++++++++++++
 include/nbdkit-common.h |  10 +-
 include/nbdkit-filter.h |  22 ++++-
 include/nbdkit-plugin.h |   6 +-
 server/internal.h       |   4 +
 server/extents.c        | 210 ++++++++++++++++++++++++++++++++++++++++
 server/filters.c        |  59 +++++++++++
 server/plugins.c        |  54 ++++++++++-
 server/Makefile.am      |   1 +
 server/nbdkit.syms      |   5 +
 11 files changed, 547 insertions(+), 4 deletions(-)

diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index dc9a262..9e51c68 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -350,6 +350,8 @@ calls.
 
 =head2 C<.can_zero>
 
+=head2 C<.can_extents>
+
 =head2 C<.can_fua>
 
 =head2 C<.can_multi_conn>
@@ -365,6 +367,8 @@ calls.
                   void *handle);
  int (*can_zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
                   void *handle);
+ int (*can_extents) (struct nbdkit_next_ops *next_ops, void *nxdata,
+                     void *handle);
  int (*can_fua) (struct nbdkit_next_ops *next_ops, void *nxdata,
                  void *handle);
  int (*can_multi_conn) (struct nbdkit_next_ops *next_ops, void *nxdata,
@@ -513,6 +517,85 @@ value to return to the client.  The filter should never fail with
 C<EOPNOTSUPP> (while plugins have automatic fallback to C<.pwrite>,
 filters do not).
 
+=head2 C<.extents>
+
+ int (*extents) (struct nbdkit_next_ops *next_ops, void *nxdata,
+                 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+                 struct nbdkit_extents *extents,
+                 int *err);
+
+This intercepts the plugin C<.extents> method and can be used to
+modify extent requests.
+
+This function will not be called if C<.can_extents> returned false; in
+turn, the filter should not call C<next_ops-E<gt>extents> if
+C<next_ops-E<gt>can_extents> did not return true.
+
+It is possible for filters to transform the extents list received back
+from the layer below.  Without error checking it would look like this:
+
+ myfilter_extents (..., uint32_t count, uint64_t offset, ...)
+ {
+   size_t i;
+   struct nbdkit_extents *extents2;
+   struct nbdkit_extent e;
+   int64_t size;
+
+   size = next_ops->get_size (nxdata);
+   extents2 = nbdkit_extents_new (offset + shift, size - shift);
+   next_ops->extents (nxdata, count, offset + shift, flags, extents2, err);
+   for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
+     e = nbdkit_get_extent (extents2, i);
+     e.offset -= shift;
+     nbdkit_add_extent (extents, e.offset, e.length, e.type);
+   }
+   nbdkit_extents_free (extents2);
+ }
+
+If there is an error, C<.extents> should call C<nbdkit_error> with an
+error message B<and> return -1 with C<err> set to the positive errno
+value to return to the client.
+
+=head3 Allocating and freeing nbdkit_extents list
+
+Two functions are provided to filters only for allocating and freeing
+the map:
+
+ struct nbdkit_extents *nbdkit_extents_new (uint64_t start, uint64_t end);
+
+Allocates and returns a new, empty extents list.  The C<start>
+parameter is the start of the range described in the list, and the
+C<end> parameter is the offset of the byte beyond the end.  Normally
+you would pass in C<offset> as the start and the size of the plugin as
+the end, but for filters which adjust offsets, they should pass in the
+adjusted offset.
+
+On error this function can return C<NULL>.  In this case it calls
+C<nbdkit_error> and/or C<nbdkit_set_error> as required.
+
+ void nbdkit_extents_free (struct nbdkit_extents *);
+
+Frees an existing extents list.
+
+=head3 Iterating over nbdkit_extents list
+
+Two functions are provided to filters only to iterate over the extents
+in order:
+
+ size_t nbdkit_extents_count (const struct nbdkit_extents *);
+
+Returns the number of extents in the list.
+
+ struct nbdkit_extent {
+   uint64_t offset;
+   uint64_t length;
+   uint32_t type;
+ };
+ struct nbdkit_extent nbdkit_get_extent (const struct nbdkit_extents *,
+                                         size_t i);
+
+Returns a copy of the C<i>'th extent.
+
 =head1 ERROR HANDLING
 
 If there is an error in the filter itself, the filter should call
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 47545f3..272ec67 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -555,6 +555,20 @@ This callback is not required.  If omitted, then nbdkit always tries
 C<.zero> first if it is present, and gracefully falls back to
 C<.pwrite> if C<.zero> was absent or failed with C<EOPNOTSUPP>.
 
+=head2 C<.can_extents>
+
+ int can_extents (void *handle);
+
+This is called during the option negotiation phase to find out if the
+plugin supports detecting allocated (non-sparse) regions of the disk
+with the C<.extents> callback.
+
+If there is an error, C<.can_extents> should call C<nbdkit_error> with
+an error message and return C<-1>.
+
+This callback is not required.  If omitted, then we return true iff a
+C<.extents> callback has been defined.
+
 =head2 C<.can_fua>
 
  int can_fua (void *handle);
@@ -717,6 +731,89 @@ If there is an error, C<.zero> should call C<nbdkit_error> with an
 error message, and C<nbdkit_set_error> to record an appropriate error
 (unless C<errno> is sufficient), then return C<-1>.
 
+=head2 C<.extents>
+
+ int extents (void *handle, uint32_t count, uint64_t offset,
+              uint32_t flags, struct nbdkit_extents *extents);
+
+During the data serving phase, this callback is used to detect
+allocated, sparse and zeroed regions of the disk.
+
+This function will not be called if C<.can_extents> returned false.
+nbdkit's default behaviour in this case is to treat the whole virtual
+disk as if it was allocated.
+
+The callback should detect and return the list of extents overlapping
+the range C<[offset...offset+count-1]>.  The C<extents> parameter
+points to an opaque object which the callback should fill in by
+calling C<nbdkit_add_extent>.  See L</Extents list> below.
+
+If there is an error, C<.extents> should call C<nbdkit_error> with an
+error message, and C<nbdkit_set_error> to record an appropriate error
+(unless C<errno> is sufficient), then return C<-1>.
+
+=head3 Extents list
+
+The plugin C<extents> callback is passed an opaque pointer C<struct
+nbdkit_extents *extents>.  This structure represents a list of
+L<filesystem extents|https://en.wikipedia.org/wiki/Extent_(file_systems)>
+describing which areas of the disk are allocated, which are sparse
+(“holes”), and, if supported, which are zeroes.
+
+The C<extents> callback should scan the disk starting at C<offset> and
+call C<nbdkit_add_extent> for each extent found.
+
+Extents overlapping the range C<[offset...offset+count-1]> should be
+returned if possible.  However nbdkit ignores extents E<lt> offset so
+the plugin may, if it is easier to implement, return all extent
+information for the whole disk.  The plugin may return extents beyond
+the end of the range.  It may also return extent information for less
+than the whole range, but it must return at least one extent
+overlapping C<offset>.
+
+The extents B<must> be added in ascending order, and B<must> be
+contiguous.
+
+The C<flags> parameter of the C<.extents> callback may contain the
+flag C<NBDKIT_FLAG_REQ_ONE>.  This means that the client is only
+requesting information about the extent overlapping C<offset>.  The
+plugin may ignore this flag, or as an optimization it may return just
+a single extent for C<offset>.
+
+ int nbdkit_add_extent (struct nbdkit_extents *extents,
+                        uint64_t offset, uint64_t length, uint32_t type);
+
+Add an extent covering C<[offset...offset+length-1]> of one of
+the following four types:
+
+=over 4
+
+=item C<type = 0>
+
+A normal, allocated data extent.
+
+=item C<type = NBDKIT_EXTENT_HOLE|NBDKIT_EXTENT_ZERO>
+
+An unallocated extent, a.k.a. a “hole”, which reads back as zeroes.
+This is the normal type of hole applicable to most disks.
+
+=item C<type = NBDKIT_EXTENT_ZERO>
+
+An allocated extent which is known to contain only zeroes.
+
+=item C<type = NBDKIT_EXTENT_HOLE>
+
+An unallocated extent (hole) which does not read back as zeroes.  Note
+this should only be used in specialized circumstances such as when
+writing a plugin for (or to emulate) certain SCSI drives which do not
+guarantee that trimmed blocks read back as zeroes.
+
+=back
+
+C<nbdkit_extent_add> returns C<0> on success or C<-1> on failure.  On
+failure C<nbdkit_error> and/or C<nbdkit_set_error> has already been
+called.
+
 =head1 THREADS
 
 Each nbdkit plugin must declare its thread safety model by defining
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index cb9954e..7c69b14 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -60,11 +60,15 @@ extern "C" {
 
 #define NBDKIT_FLAG_MAY_TRIM (1<<0) /* Maps to !NBD_CMD_FLAG_NO_HOLE */
 #define NBDKIT_FLAG_FUA      (1<<1) /* Maps to NBD_CMD_FLAG_FUA */
+#define NBDKIT_FLAG_REQ_ONE  (1<<2) /* Maps to NBD_CMD_FLAG_REQ_ONE */
 
 #define NBDKIT_FUA_NONE       0
 #define NBDKIT_FUA_EMULATE    1
 #define NBDKIT_FUA_NATIVE     2
 
+#define NBDKIT_EXTENT_HOLE    (1<<0) /* Same as NBD_STATE_HOLE */
+#define NBDKIT_EXTENT_ZERO    (1<<1) /* Same as NBD_STATE_ZERO */
+
 extern void nbdkit_error (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
 extern void nbdkit_verror (const char *msg, va_list args);
 extern void nbdkit_debug (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
@@ -76,6 +80,10 @@ extern int nbdkit_parse_bool (const char *str);
 extern int nbdkit_read_password (const char *value, char **password);
 extern char *nbdkit_realpath (const char *path);
 
+struct nbdkit_extents;
+extern int nbdkit_add_extent (struct nbdkit_extents *,
+                              uint64_t offset, uint64_t length, uint32_t type);
+
 /* A static non-NULL pointer which can be used when you don't need a
  * per-connection handle.
  */
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 71c06c8..026145b 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,18 @@ extern "C" {
 
 #define NBDKIT_FILTER_API_VERSION 2
 
+struct nbdkit_extent {
+  uint64_t offset;
+  uint64_t length;
+  uint32_t type;
+};
+
+extern struct nbdkit_extents *nbdkit_extents_new (uint64_t start, uint64_t end);
+extern void nbdkit_extents_free (struct nbdkit_extents *);
+extern size_t nbdkit_extents_count (const struct nbdkit_extents *);
+extern struct nbdkit_extent nbdkit_get_extent (const struct nbdkit_extents *,
+                                               size_t);
+
 typedef int nbdkit_next_config (void *nxdata,
                                 const char *key, const char *value);
 typedef int nbdkit_next_config_complete (void *nxdata);
@@ -58,6 +70,7 @@ struct nbdkit_next_ops {
   int (*is_rotational) (void *nxdata);
   int (*can_trim) (void *nxdata);
   int (*can_zero) (void *nxdata);
+  int (*can_extents) (void *nxdata);
   int (*can_fua) (void *nxdata);
   int (*can_multi_conn) (void *nxdata);
 
@@ -71,6 +84,8 @@ struct nbdkit_next_ops {
                int *err);
   int (*zero) (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
                int *err);
+  int (*extents) (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
+                  struct nbdkit_extents *extents, int *err);
 };
 
 struct nbdkit_filter {
@@ -120,6 +135,8 @@ struct nbdkit_filter {
                    void *handle);
   int (*can_zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
                    void *handle);
+  int (*can_extents) (struct nbdkit_next_ops *next_ops, void *nxdata,
+                      void *handle);
   int (*can_fua) (struct nbdkit_next_ops *next_ops, void *nxdata,
                   void *handle);
   int (*can_multi_conn) (struct nbdkit_next_ops *next_ops, void *nxdata,
@@ -140,6 +157,9 @@ struct nbdkit_filter {
   int (*zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                int *err);
+  int (*extents) (struct nbdkit_next_ops *next_ops, void *nxdata,
+                  void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+                  struct nbdkit_extents *extents, int *err);
 };
 
 #define NBDKIT_REGISTER_FILTER(filter)                                  \
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index d43b2f5..20d193c 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -125,6 +125,10 @@ struct nbdkit_plugin {
   const char *magic_config_key;
 
   int (*can_multi_conn) (void *handle);
+
+  int (*can_extents) (void *handle);
+  int (*extents) (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+                  struct nbdkit_extents *extents);
 };
 
 extern void nbdkit_set_error (int err);
diff --git a/server/internal.h b/server/internal.h
index d40a82d..71d2815 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -274,6 +274,7 @@ struct backend {
   int (*is_rotational) (struct backend *, struct connection *conn);
   int (*can_trim) (struct backend *, struct connection *conn);
   int (*can_zero) (struct backend *, struct connection *conn);
+  int (*can_extents) (struct backend *, struct connection *conn);
   int (*can_fua) (struct backend *, struct connection *conn);
   int (*can_multi_conn) (struct backend *, struct connection *conn);
 
@@ -287,6 +288,9 @@ struct backend {
                uint64_t offset, uint32_t flags, int *err);
   int (*zero) (struct backend *, struct connection *conn, uint32_t count,
                uint64_t offset, uint32_t flags, int *err);
+  int (*extents) (struct backend *, struct connection *conn, uint32_t count,
+                  uint64_t offset, uint32_t flags,
+                  struct nbdkit_extents *extents, int *err);
 };
 
 /* plugins.c */
diff --git a/server/extents.c b/server/extents.c
new file mode 100644
index 0000000..549d39f
--- /dev/null
+++ b/server/extents.c
@@ -0,0 +1,210 @@
+/* nbdkit
+ * Copyright (C) 2019 Red Hat Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "minmax.h"
+
+#include "internal.h"
+
+struct nbdkit_extents {
+  struct nbdkit_extent *extents;
+  size_t nr_extents, allocated;
+  uint64_t start, end; /* end is one byte beyond the end of the range */
+};
+
+struct nbdkit_extents *
+nbdkit_extents_new (uint64_t start, uint64_t end)
+{
+  struct nbdkit_extents *r;
+
+  if (start >= INT64_MAX || end >= INT64_MAX) {
+    nbdkit_error ("nbdkit_extents_new: "
+                  "start (%" PRIu64 ") or end (%" PRIu64 ") >= INT64_MAX",
+                  start, end);
+    errno = ERANGE;
+    return NULL;
+  }
+
+  /* 0-length ranges are possible, so start == end is not an error. */
+  if (start > end) {
+    nbdkit_error ("nbdkit_extents_new: "
+                  "start (%" PRIu64 ") >= end (%" PRIu64 ")",
+                  start, end);
+    errno = ERANGE;
+    return NULL;
+  }
+
+  r = malloc (sizeof *r);
+  if (r == NULL) {
+    nbdkit_error ("nbdkit_extents_new: malloc: %m");
+    return NULL;
+  }
+  r->extents = NULL;
+  r->nr_extents = r->allocated = 0;
+  r->start = start;
+  r->end = end;
+  return r;
+}
+
+void
+nbdkit_extents_free (struct nbdkit_extents *exts)
+{
+  if (exts) {
+    free (exts->extents);
+    free (exts);
+  }
+}
+
+size_t
+nbdkit_extents_count (const struct nbdkit_extents *exts)
+{
+  return exts->nr_extents;
+}
+
+const struct nbdkit_extent
+nbdkit_get_extent (const struct nbdkit_extents *exts, size_t i)
+{
+  assert (i < exts->nr_extents);
+  return exts->extents[i];
+}
+
+/* Insert *e in the list at the end. */
+static int
+append_extent (struct nbdkit_extents *exts, const struct nbdkit_extent *e)
+{
+  if (exts->nr_extents >= exts->allocated) {
+    size_t new_allocated;
+    struct nbdkit_extent *new_extents;
+
+    new_allocated = exts->allocated;
+    if (new_allocated == 0)
+      new_allocated = 1;
+    new_allocated *= 2;
+    new_extents =
+      realloc (exts->extents, new_allocated * sizeof (struct nbdkit_extent));
+    if (new_extents == NULL) {
+      nbdkit_error ("nbdkit_add_extent: realloc: %m");
+      return -1;
+    }
+    exts->allocated = new_allocated;
+    exts->extents = new_extents;
+  }
+
+  exts->extents[exts->nr_extents] = *e;
+  exts->nr_extents++;
+  return 0;
+}
+
+int
+nbdkit_add_extent (struct nbdkit_extents *exts,
+                   uint64_t offset, uint64_t length, uint32_t type)
+{
+  uint64_t overlap;
+
+  if (length == 0)
+    return 0;
+
+  /* Ignore extents beyond the end of the range.  Unfortunately we
+   * lost the information about whether this is contiguous with a
+   * previously added extent so we can't check for API usage errors.
+   */
+  if (offset >= exts->end)
+    return 0;
+
+  /* Shorten extents that overlap the end of the range. */
+  if (offset + length >= exts->end) {
+    overlap = offset + length - exts->end;
+    length -= overlap;
+  }
+
+  /* If there are existing extents, the new extent must be contiguous. */
+  if (exts->nr_extents > 0) {
+    const struct nbdkit_extent *ep;
+
+    ep = &exts->extents[exts->nr_extents-1];
+    if (offset != ep->offset + ep->length) {
+      nbdkit_error ("nbdkit_add_extent: "
+                    "extents must be added in ascending order and "
+                    "must be contiguous");
+      return -1;
+    }
+  }
+  else {
+    /* If there are no existing extents, and the new extent is
+     * entirely before start, ignore it.
+     */
+    if (offset + length <= exts->start)
+      return 0;
+
+    /* If there are no existing extents, and the new extent is after
+     * start, then this is a bug in the plugin.
+     */
+    if (offset > exts->start) {
+      nbdkit_error ("nbdkit_add_extent: "
+                    "first extent must not be > start (%" PRIu64 ")",
+                    exts->start);
+      return -1;
+    }
+
+    /* If there are no existing extents, and the new extent overlaps
+     * start, truncate it so it starts at start.
+     */
+    overlap = exts->start - offset;
+    length -= overlap;
+    offset += overlap;
+  }
+
+  /* If we get here we are going to either add or extend. */
+  if (exts->nr_extents > 0 &&
+      exts->extents[exts->nr_extents-1].type == type) {
+    /* Coalesce with the last extent. */
+    exts->extents[exts->nr_extents-1].length += length;
+    return 0;
+  }
+  else {
+    /* Add a new extent. */
+    const struct nbdkit_extent e =
+      { .offset = offset, .length = length, .type = type };
+    return append_extent (exts, &e);
+  }
+}
diff --git a/server/filters.c b/server/filters.c
index 5b7abc4..5095188 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -309,6 +309,13 @@ next_can_zero (void *nxdata)
   return b_conn->b->can_zero (b_conn->b, b_conn->conn);
 }
 
+static int
+next_can_extents (void *nxdata)
+{
+  struct b_conn *b_conn = nxdata;
+  return b_conn->b->can_extents (b_conn->b, b_conn->conn);
+}
+
 static int
 next_can_fua (void *nxdata)
 {
@@ -364,6 +371,15 @@ next_zero (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
   return b_conn->b->zero (b_conn->b, b_conn->conn, count, offset, flags, err);
 }
 
+static int
+next_extents (void *nxdata, uint32_t count, uint64_t offset, uint32_t flags,
+              struct nbdkit_extents *extents, int *err)
+{
+  struct b_conn *b_conn = nxdata;
+  return b_conn->b->extents (b_conn->b, b_conn->conn, count, offset, flags,
+                             extents, err);
+}
+
 static struct nbdkit_next_ops next_ops = {
   .get_size = next_get_size,
   .can_write = next_can_write,
@@ -371,6 +387,7 @@ static struct nbdkit_next_ops next_ops = {
   .is_rotational = next_is_rotational,
   .can_trim = next_can_trim,
   .can_zero = next_can_zero,
+  .can_extents = next_can_extents,
   .can_fua = next_can_fua,
   .can_multi_conn = next_can_multi_conn,
   .pread = next_pread,
@@ -378,6 +395,7 @@ static struct nbdkit_next_ops next_ops = {
   .flush = next_flush,
   .trim = next_trim,
   .zero = next_zero,
+  .extents = next_extents,
 };
 
 static int
@@ -511,6 +529,21 @@ filter_can_zero (struct backend *b, struct connection *conn)
     return f->backend.next->can_zero (f->backend.next, conn);
 }
 
+static int
+filter_can_extents (struct backend *b, struct connection *conn)
+{
+  struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  void *handle = connection_get_handle (conn, f->backend.i);
+  struct b_conn nxdata = { .b = f->backend.next, .conn = conn };
+
+  debug ("%s: can_extents", f->name);
+
+  if (f->filter.can_extents)
+    return f->filter.can_extents (&next_ops, &nxdata, handle);
+  else
+    return f->backend.next->can_extents (f->backend.next, conn);
+}
+
 static int
 filter_can_fua (struct backend *b, struct connection *conn)
 {
@@ -646,6 +679,30 @@ filter_zero (struct backend *b, struct connection *conn,
                                   count, offset, flags, err);
 }
 
+static int
+filter_extents (struct backend *b, struct connection *conn,
+                uint32_t count, uint64_t offset, uint32_t flags,
+                struct nbdkit_extents *extents, int *err)
+{
+  struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  void *handle = connection_get_handle (conn, f->backend.i);
+  struct b_conn nxdata = { .b = f->backend.next, .conn = conn };
+
+  assert (!(flags & ~NBDKIT_FLAG_REQ_ONE));
+
+  debug ("%s: extents count=%" PRIu32 " offset=%" PRIu64 " flags=0x%" PRIx32,
+         f->name, count, offset, flags);
+
+  if (f->filter.extents)
+    return f->filter.extents (&next_ops, &nxdata, handle,
+                              count, offset, flags,
+                              extents, err);
+  else
+    return f->backend.next->extents (f->backend.next, conn,
+                                     count, offset, flags,
+                                     extents, err);
+}
+
 static struct backend filter_functions = {
   .free = filter_free,
   .thread_model = filter_thread_model,
@@ -667,6 +724,7 @@ static struct backend filter_functions = {
   .is_rotational = filter_is_rotational,
   .can_trim = filter_can_trim,
   .can_zero = filter_can_zero,
+  .can_extents = filter_can_extents,
   .can_fua = filter_can_fua,
   .can_multi_conn = filter_can_multi_conn,
   .pread = filter_pread,
@@ -674,6 +732,7 @@ static struct backend filter_functions = {
   .flush = filter_flush,
   .trim = filter_trim,
   .zero = filter_zero,
+  .extents = filter_extents,
 };
 
 /* Register and load a filter. */
diff --git a/server/plugins.c b/server/plugins.c
index 28b96ad..0b0fe77 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -199,6 +199,8 @@ plugin_dump_fields (struct backend *b)
   HAS (trim);
   HAS (zero);
   HAS (can_multi_conn);
+  HAS (can_extents);
+  HAS (extents);
 #undef HAS
 
   /* Custom fields. */
@@ -392,6 +394,21 @@ plugin_can_zero (struct backend *b, struct connection *conn)
   return plugin_can_write (b, conn);
 }
 
+static int
+plugin_can_extents (struct backend *b, struct connection *conn)
+{
+  struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+
+  assert (connection_get_handle (conn, 0));
+
+  debug ("can_extents");
+
+  if (p->plugin.can_extents)
+    return p->plugin.can_extents (connection_get_handle (conn, 0));
+  else
+    return p->plugin.extents != NULL;
+}
+
 static int
 plugin_can_fua (struct backend *b, struct connection *conn)
 {
@@ -651,6 +668,39 @@ plugin_zero (struct backend *b, struct connection *conn,
   return r;
 }
 
+static int
+plugin_extents (struct backend *b, struct connection *conn,
+                uint32_t count, uint64_t offset, uint32_t flags,
+                struct nbdkit_extents *extents, int *err)
+{
+  struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+  bool req_one = flags & NBDKIT_FLAG_REQ_ONE;
+  int r;
+
+  assert (connection_get_handle (conn, 0));
+  assert (!(flags & ~NBDKIT_FLAG_REQ_ONE));
+
+  /* This should be true because plugin_can_extents checks it. */
+  assert (p->plugin.extents);
+
+  debug ("extents count=%" PRIu32 " offset=%" PRIu64 " req_one=%d",
+         count, offset, req_one);
+
+  if (!count)
+    return 0;
+
+  r = p->plugin.extents (connection_get_handle (conn, 0), count, offset,
+                         flags, extents);
+  if (r >= 0 && nbdkit_extents_count (extents) < 1) {
+    nbdkit_error ("extents: plugin must return at least one extent");
+    nbdkit_set_error (EINVAL);
+    r = -1;
+  }
+  if (r == -1)
+    *err = get_error (p);
+  return r;
+}
+
 static struct backend plugin_functions = {
   .free = plugin_free,
   .thread_model = plugin_thread_model,
@@ -672,6 +722,7 @@ static struct backend plugin_functions = {
   .is_rotational = plugin_is_rotational,
   .can_trim = plugin_can_trim,
   .can_zero = plugin_can_zero,
+  .can_extents = plugin_can_extents,
   .can_fua = plugin_can_fua,
   .can_multi_conn = plugin_can_multi_conn,
   .pread = plugin_pread,
@@ -679,6 +730,7 @@ static struct backend plugin_functions = {
   .flush = plugin_flush,
   .trim = plugin_trim,
   .zero = plugin_zero,
+  .extents = plugin_extents,
 };
 
 /* Register and load a plugin. */
diff --git a/server/Makefile.am b/server/Makefile.am
index be70700..5ee662e 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -43,6 +43,7 @@ nbdkit_SOURCES = \
 	connections.c \
 	crypto.c \
 	debug.c \
+	extents.c \
 	filters.c \
 	internal.h \
 	locks.c \
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 672abd2..240953e 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -40,8 +40,13 @@
   # The functions we want plugins and filters to call.
   global:
     nbdkit_absolute_path;
+    nbdkit_add_extent;
     nbdkit_debug;
     nbdkit_error;
+    nbdkit_extents_count;
+    nbdkit_extents_free;
+    nbdkit_extents_new;
+    nbdkit_get_extent;
     nbdkit_parse_bool;
     nbdkit_parse_size;
     nbdkit_read_password;
-- 
2.20.1




More information about the Libguestfs mailing list