[Libguestfs] [nbdkit PATCH v3 11/15] plugins: Expose new FUA callbacks

Eric Blake eblake at redhat.com
Thu Mar 8 23:03:07 UTC 2018


The NBD protocol supports Forced Unit Access (FUA) as a more efficient
way to wait for just one write to land in persistent storage, rather
than all outstanding writes at the time of a flush; modeled after
the kernel's block I/O flag of the same name.  While we can emulate
the proper semantics with a full-blown flush, there are some plugins
that can properly pass the FUA flag on to the end storage and thereby
avoid some overhead.

This patch introduces new callbacks, and updates the documentation
to the new API, while ensuring that plugins compiled to the old API
still work.  The new API adds .can_fua, then adds a flags parameter
to all five data callbacks, even though only three of them will use
a flag at the moment.  A plugin client has to opt in to both the
version 2 API and provide .can_fua with a return of NBDKIT_FUA_NATIVE
before nbdkit will pass the NBDKIT_FLAG_FUA to the plugin.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 docs/nbdkit-filter.pod  |  12 ++--
 docs/nbdkit-plugin.pod  | 151 +++++++++++++++++++++++++++++++++++++++++++-----
 docs/nbdkit.pod         |   7 ++-
 include/nbdkit-plugin.h |  30 ++++++++++
 src/internal.h          |   1 +
 src/plugins.c           | 136 +++++++++++++++++++++++++++----------------
 6 files changed, 265 insertions(+), 72 deletions(-)

diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 851bc37..471c4bc 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -385,12 +385,12 @@ allowed.
                  void *handle);

 This controls how the Forced Unit Access flag will be handled; it is
-only relevant for connections that are not read-only.  For now, this
-function has no counterpart in plugins, although it will be added.
-Unlike other feature functions with just two success states, this one
-returns three success values: C<NBDKIT_FUA_NONE> to avoid advertising
-FUA support to the client, C<NBDKIT_FUA_EMULATE> if FUA is emulated by
-nbdkit calling flush after a write transaction, and
+only relevant for connections that are not read-only.  This intercepts
+the corresponding plugin method, to control feature bits advertised to
+the client.  Unlike other feature functions with just two success
+states, this one returns three success values: C<NBDKIT_FUA_NONE> to
+avoid advertising FUA support to the client, C<NBDKIT_FUA_EMULATE> if
+FUA is emulated by nbdkit calling flush after a write transaction, and
 C<NBDKIT_FUA_NATIVE> if FUA semantics are handled by the plugin
 without the overhead of an extra flush from nbdkit.

diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 44822fc..933f710 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -6,6 +6,8 @@ nbdkit-plugin - How to write nbdkit plugins

 =head1 SYNOPSIS

+ #define NBDKIT_API_VERSION 2
+ 
  #include <nbdkit-plugin.h>
  
  #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
@@ -63,9 +65,22 @@ L<nbdkit-perl-plugin(3)>,
 L<nbdkit-python-plugin(3)>,
 L<nbdkit-ruby-plugin(3)>.

+=head1 C<#define NBDKIT_API_VERSION 2>
+
+Plugins must choose which API version they want to use, by defining
+NBDKIT_API_VERSION to a positive integer prior to including
+C<nbdkit-plugin.h> (or any other nbdkit header).  The default version
+is 1 for backwards-compatibility with nbdkit v1.1.26 and earlier;
+however, it is recommended that new plugins be written to the maximum
+version (currently 2) as it enables more features and better
+interaction with nbdkit filters.  Therefore, the rest of this document
+only covers the version 2 interface.  A newer nbdkit will always
+support plugins compiled against any prior API version.
+
 =head1 C<nbdkit-plugin.h>

-All plugins should start by including this header file:
+All plugins should start by including this header file, after
+optionally choosing an API version:

  #include <nbdkit-plugin.h>

@@ -127,14 +142,20 @@ A new client has connected.

 =item C<.can_write>, C<.get_size> and other option negotiation callbacks

-These are called during option negotiation with the client, but
-before any data is served.
+These are called during option negotiation with the client, but before
+any data is served.  These callbacks may return different values
+across different C<.open> calls, but within a single connection, must
+always return the same value; other code in nbdkit may cache the
+per-connection value returned rather than using the callback a second
+time.

 =item C<.pread>, C<.pwrite> and other data serving callbacks

 After option negotiation has finished, these may be called to serve
 data.  Depending on the thread model chosen, they might be called in
-parallel from multiple threads.
+parallel from multiple threads.  The data serving callbacks include a
+flags argument; the results of the negotiation callbacks influence
+whether particular flags will ever be passed to a data callback.

 =item C<.close>

@@ -152,6 +173,58 @@ is called once just before the plugin is unloaded from memory.

 =back

+=head1 Flags
+
+The following flags are defined by nbdkit, and used in various data
+serving callbacks as follows:
+
+=over 4
+
+=item C<NBDKIT_FLAG_MAY_TRIM>
+
+This flag is used by the C<.zero> callback; a plugin may ignore it
+without changing valid semantics, so there is no way to disable this
+flag.
+
+=item C<NBDKIT_FLAG_FUA>
+
+This flag represents Forced Unit Access semantics.  It is used by the
+C<.pwrite>, C<.zero>, and C<.trim> callbacks to indicate that the
+plugin must not return a result until the action has landed in
+persistent storage.  This flag will not be sent to the plugin unless
+C<.can_fua> is provided and returns C<NBDKIT_FUA_NATIVE>.
+
+=back
+
+The following defines are valid as successful return values for
+C<.can_fua>:
+
+=over 4
+
+=item C<NBDKIT_FUA_NONE>
+
+Forced Unit Access is not supported; the client must manually request
+a flush after writes have completed.  The C<NBDKIT_FLAG_FUA> flag will
+not be passed to the plugin's write callbacks.
+
+=item C<NBDKIT_FUA_EMULATE>
+
+The client may request Forced Unit Access, but it is implemented by
+emulation, where nbdkit calls C<.flush> after a write operation; this
+is semantically correct, but may hurt performance as it tends to flush
+more data than just what the client requested.  The C<NBDKIT_FLAG_FUA>
+flag will not be passed to the plugin's write callbacks.
+
+=item C<NBDKIT_FUA_NATIVE>
+
+The client may request Forced Unit Access, which results in the
+C<NBDKIT_FLAG_FUA> flag being passed to the plugin's write callbacks
+(C<.pwrite>, C<.trim>, and C<.zero>).  When the flag is set, these
+callbacks must not return success until the client's request has
+landed in persistent storage.
+
+=back
+
 =head1 ERROR HANDLING

 If there is an error in the plugin, the plugin should call
@@ -414,9 +487,33 @@ error message and return C<-1>.
 This callback is not required.  If omitted, then we return true iff a
 C<.trim> callback has been defined.

+=head2 C<.can_fua>
+
+ int can_fua (void *handle);
+
+This is called during the option negotiation phase to find out if the
+plugin supports the Forced Unit Access (FUA) flag on write, zero, and
+trim requests.  If this returns C<NBDKIT_FUA_NONE>, FUA support is not
+advertised to the guest; if this returns C<NBDKIT_FUA_EMULATE>, the
+C<.flush> callback must work (even if C<.can_flush> returns false),
+and FUA support is emulated by calling C<.flush> after any write
+operation; if this returns C<NBDKIT_FUA_NATIVE>, then the C<.pwrite>,
+C<.zero>, and C<.trim> callbacks (if implemented) must handle the flag
+C<NBDKIT_FLAG_FUA>, by not returning until that action has landed in
+persistent storage.
+
+If there is an error, C<.can_fua> should call C<nbdkit_error> with an
+error message and return C<-1>.
+
+This callback is not required unless a plugin wants to specifically
+handle FUA requests.  If omitted, nbdkit checks C<.can_flush>, and
+behaves as if this function returns C<NBDKIT_FUA_NONE> or
+C<NBDKIT_FUA_EMULATE> as appropriate.
+
 =head2 C<.pread>

- int pread (void *handle, void *buf, uint32_t count, uint64_t offset);
+ int pread (void *handle, void *buf, uint32_t count, uint64_t offset,
+            uint32_t flags);

 During the data serving phase, nbdkit calls this callback to read data
 from the backing store.  C<count> bytes starting at C<offset> in the
@@ -424,6 +521,9 @@ backing store should be read and copied into C<buf>.  nbdkit takes
 care of all bounds- and sanity-checking, so the plugin does not need
 to worry about that.

+The parameter C<flags> exists in case of future NBD protocol
+extensions; at this time, it will be 0 on input.
+
 The callback must read the whole C<count> bytes if it can.  The NBD
 protocol doesn't allow partial reads (instead, these would be errors).
 If the whole C<count> bytes was read, the callback should return C<0>
@@ -436,7 +536,8 @@ message, and C<nbdkit_set_error> to record an appropriate error

 =head2 C<.pwrite>

- int pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset);
+ int pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
+             uint32_t flags);

 During the data serving phase, nbdkit calls this callback to write
 data to the backing store.  C<count> bytes starting at C<offset> in
@@ -444,6 +545,10 @@ the backing store should be written using the data in C<buf>.  nbdkit
 takes care of all bounds- and sanity-checking, so the plugin does not
 need to worry about that.

+This function will not be called if C<.can_write> returned false.  The
+parameter C<flags> may include C<NBDKIT_FLAG_FUA> on input based on
+the result of C<.can_fua>.
+
 The callback must write the whole C<count> bytes if it can.  The NBD
 protocol doesn't allow partial writes (instead, these would be
 errors).  If the whole C<count> bytes was written successfully, the
@@ -456,40 +561,56 @@ message, and C<nbdkit_set_error> to record an appropriate error

 =head2 C<.flush>

- int flush (void *handle);
+ int flush (void *handle, uint32_t flags);

 During the data serving phase, this callback is used to
 L<fdatasync(2)> the backing store, ie. to ensure it has been
 completely written to a permanent medium.  If that is not possible
 then you can omit this callback.

+This function will not be called directly by the client if
+C<.can_flush> returned false; however, it may still be called by
+nbdkit if C<.can_fua> returned C<NBDKIT_FUA_EMULATE>.  The parameter
+C<flags> exists in case of future NBD protocol extensions; at this
+time, it will be 0 on input.
+
 If there is an error, C<.flush> 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<.trim>

- int trim (void *handle, uint32_t count, uint64_t offset);
+ int trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags);

 During the data serving phase, this callback is used to "punch holes"
 in the backing store.  If that is not possible then you can omit this
 callback.

+This function will not be called if C<.can_trim> returned false.  The
+parameter C<flags> may include C<NBDKIT_FLAG_FUA> on input based on
+the result of C<.can_fua>.
+
 If there is an error, C<.trim> 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<.zero>

- int zero (void *handle, uint32_t count, uint64_t offset, int may_trim);
+ int zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags);

 During the data serving phase, this callback is used to write C<count>
-bytes of zeroes at C<offset> in the backing store.  If C<may_trim> is
-non-zero, the operation can punch a hole instead of writing actual
-zero bytes, but only if subsequent reads from the hole read as zeroes.
-If this callback is omitted, or if it fails with C<EOPNOTSUPP>
-(whether by C<nbdkit_set_error> or C<errno>), then C<.pwrite> will be
-used instead.
+bytes of zeroes at C<offset> in the backing store.
+
+This function will not be called if C<.can_write> returned false.  On
+input, the parameter C<flags> may include C<NBDKIT_FLAG_MAY_TRIM>
+unconditionally, and C<NBDKIT_FLAG_FUA> based on the result of
+C<.can_fua>.
+
+If C<NBDKIT_FLAG_MAY_TRIM> is requested, the operation can punch a
+hole instead of writing actual zero bytes, but only if subsequent
+reads from the hole read as zeroes.  If this callback is omitted, or
+if it fails with C<EOPNOTSUPP> (whether by C<nbdkit_set_error> or
+C<errno>), then C<.pwrite> will be used instead.

 The callback must write the whole C<count> bytes if it can.  The NBD
 protocol doesn't allow partial writes (instead, these would be
diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index dacf11c..d49ae53 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -827,7 +827,12 @@ information about that plugin, eg:
  [etc]

 Plugins which ship with nbdkit usually have the same version as the
-corresponding nbdkit binary.
+corresponding nbdkit binary.  The nbdkit binary will always be able to
+utilize plugins compiled against an older version of the header;
+however, newer plugins may not be fully supported by an older nbdkit
+binary (for example, a plugin compiled with C<NBDKIT_API_VERSION> of 2
+fails to load with an older nbdkit that only knows
+C<NBDKIT_API_VERSION> 1).

 =head2 Detect if a plugin is installed

diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 1981061..3f541a1 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -42,7 +42,13 @@
 extern "C" {
 #endif

+/* By default, a plugin gets API version 1; but you may request
+ * version 2 prior to including this header */
+#ifndef NBDKIT_API_VERSION
 #define NBDKIT_API_VERSION                            1
+#elif (NBDKIT_API_VERSION - 0) < 1 || NBDKIT_API_VERSION > 2
+#error Unsupported API version
+#endif

 struct nbdkit_plugin {
   /* Do not set these fields directly; use NBDKIT_REGISTER_PLUGIN.
@@ -80,16 +86,40 @@ struct nbdkit_plugin {
   int (*is_rotational) (void *handle);
   int (*can_trim) (void *handle);

+#if NBDKIT_API_VERSION == 1
   int (*pread) (void *handle, void *buf, uint32_t count, uint64_t offset);
   int (*pwrite) (void *handle, const void *buf, uint32_t count, uint64_t offset);
   int (*flush) (void *handle);
   int (*trim) (void *handle, uint32_t count, uint64_t offset);
   int (*zero) (void *handle, uint32_t count, uint64_t offset, int may_trim);
+#else
+  int (*_pread_old) (void *, void *, uint32_t, uint64_t);
+  int (*_pwrite_old) (void *, const void *, uint32_t, uint64_t);
+  int (*_flush_old) (void *);
+  int (*_trim_old) (void *, uint32_t, uint64_t);
+  int (*_zero_old) (void *, uint32_t, uint64_t, int);
+#endif

   int errno_is_preserved;

   void (*dump_plugin) (void);

+  int (*can_fua) (void *handle);
+#if NBDKIT_API_VERSION == 1
+  int (*_unused1) (void *, void *, uint32_t, uint64_t);
+  int (*_unused2) (void *, const void *, uint32_t, uint64_t, uint32_t);
+  int (*_unused3) (void *, uint32_t);
+  int (*_unused4) (void *, uint32_t, uint64_t, uint32_t);
+  int (*_unused5) (void *, uint32_t, uint64_t, uint32_t);
+#else
+  int (*pread) (void *handle, void *buf, uint32_t count, uint64_t offset,
+                uint32_t flags);
+  int (*pwrite) (void *handle, const void *buf, uint32_t count,
+                 uint64_t offset, uint32_t flags);
+  int (*flush) (void *handle, uint32_t flags);
+  int (*trim) (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
+  int (*zero) (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
+#endif
   /* int (*set_exportname) (void *handle, const char *exportname); */
 };

diff --git a/src/internal.h b/src/internal.h
index d873cd5..ea3155c 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -40,6 +40,7 @@
 #include <sys/socket.h>
 #include <pthread.h>

+#define NBDKIT_API_VERSION 2
 #include "nbdkit-plugin.h"
 #include "nbdkit-filter.h"

diff --git a/src/plugins.c b/src/plugins.c
index 10c911d..d44e724 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -180,6 +180,12 @@ plugin_dump_fields (struct backend *b)
   HAS (can_flush);
   HAS (is_rotational);
   HAS (can_trim);
+  HAS (_pread_old);
+  HAS (_pwrite_old);
+  HAS (_flush_old);
+  HAS (_trim_old);
+  HAS (_zero_old);
+  HAS (can_fua);
   HAS (pread);
   HAS (pwrite);
   HAS (flush);
@@ -301,7 +307,7 @@ plugin_can_write (struct backend *b, struct connection *conn)
   if (p->plugin.can_write)
     return p->plugin.can_write (connection_get_handle (conn, 0));
   else
-    return p->plugin.pwrite != NULL;
+    return p->plugin.pwrite || p->plugin._pwrite_old;
 }

 static int
@@ -316,7 +322,7 @@ plugin_can_flush (struct backend *b, struct connection *conn)
   if (p->plugin.can_flush)
     return p->plugin.can_flush (connection_get_handle (conn, 0));
   else
-    return p->plugin.flush != NULL;
+    return p->plugin.flush || p->plugin._flush_old;
 }

 static int
@@ -346,7 +352,7 @@ plugin_can_trim (struct backend *b, struct connection *conn)
   if (p->plugin.can_trim)
     return p->plugin.can_trim (connection_get_handle (conn, 0));
   else
-    return p->plugin.trim != NULL;
+    return p->plugin.trim || p->plugin._trim_old;
 }

 /* Grab the appropriate error value.
@@ -377,14 +383,20 @@ plugin_can_fua (struct backend *b, struct connection *conn)
   struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
   int r;

+  assert (connection_get_handle (conn, 0));
+
   debug ("can_fua");

-  /* TODO - wire FUA flag support into plugins. Until then, this copies
-   * can_flush, since that's how we emulate FUA. */
+  if (p->plugin.can_fua) {
+    r = p->plugin.can_fua (connection_get_handle (conn, 0));
+    if (r > NBDKIT_FUA_EMULATE && p->plugin._api_version == 1)
+      r = NBDKIT_FUA_EMULATE;
+    return r;
+  }
   r = plugin_can_flush (b, conn);
   if (r == -1)
     return -1;
-  if (r == 0 || !p->plugin.flush)
+  if (r == 0 || !(p->plugin.flush || p->plugin._flush_old))
     return NBDKIT_FUA_NONE;
   return NBDKIT_FUA_EMULATE;
 }
@@ -398,12 +410,17 @@ plugin_pread (struct backend *b, struct connection *conn,
   int r;

   assert (connection_get_handle (conn, 0));
-  assert (p->plugin.pread != NULL);
+  assert (p->plugin.pread || p->plugin._pread_old);
   assert (!flags);

   debug ("pread count=%" PRIu32 " offset=%" PRIu64, count, offset);

-  r = p->plugin.pread (connection_get_handle (conn, 0), buf, count, offset);
+  if (p->plugin.pread)
+    r = p->plugin.pread (connection_get_handle (conn, 0), buf, count, offset,
+                         0);
+  else
+    r = p->plugin._pread_old (connection_get_handle (conn, 0), buf, count,
+                              offset);
   if (r == -1)
     *err = get_error (p);
   return r;
@@ -421,16 +438,17 @@ plugin_flush (struct backend *b, struct connection *conn, uint32_t flags,

   debug ("flush");

-  if (p->plugin.flush != NULL) {
-    r = p->plugin.flush (connection_get_handle (conn, 0));
-    if (r == -1)
-      *err = get_error (p);
-    return r;
-  }
+  if (p->plugin.flush)
+    r = p->plugin.flush (connection_get_handle (conn, 0), 0);
+  else if (p->plugin._flush_old)
+    r = p->plugin._flush_old (connection_get_handle (conn, 0));
   else {
     *err = EINVAL;
     return -1;
   }
+  if (r == -1)
+    *err = get_error (p);
+  return r;
 }

 static int
@@ -441,6 +459,7 @@ plugin_pwrite (struct backend *b, struct connection *conn,
   int r;
   struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
   bool fua = flags & NBDKIT_FLAG_FUA;
+  bool need_flush = false;

   assert (connection_get_handle (conn, 0));
   assert (!(flags & ~NBDKIT_FLAG_FUA));
@@ -448,17 +467,22 @@ plugin_pwrite (struct backend *b, struct connection *conn,
   debug ("pwrite count=%" PRIu32 " offset=%" PRIu64 " fua=%d", count, offset,
          fua);

-  if (p->plugin.pwrite != NULL)
-    r = p->plugin.pwrite (connection_get_handle (conn, 0),
-                          buf, count, offset);
+  if (fua && plugin_can_fua (b, conn) != NBDKIT_FUA_NATIVE) {
+    flags &= ~NBDKIT_FLAG_FUA;
+    need_flush = true;
+  }
+  if (p->plugin.pwrite)
+    r = p->plugin.pwrite (connection_get_handle (conn, 0), buf, count, offset,
+                          flags);
+  else if (p->plugin._pwrite_old)
+    r = p->plugin._pwrite_old (connection_get_handle (conn, 0),
+                               buf, count, offset);
   else {
     *err = EROFS;
     return -1;
   }
-  if (r != -1 && fua) {
-    assert (p->plugin.flush);
-    r = p->plugin.flush (connection_get_handle (conn, 0));
-  }
+  if (r != -1 && need_flush)
+    r = plugin_flush (b, conn, 0, err);
   if (r == -1)
     *err = get_error (p);
   return r;
@@ -471,6 +495,7 @@ plugin_trim (struct backend *b, struct connection *conn,
   int r;
   struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
   bool fua = flags & NBDKIT_FLAG_FUA;
+  bool need_flush = false;

   assert (connection_get_handle (conn, 0));
   assert (!(flags & ~NBDKIT_FLAG_FUA));
@@ -478,16 +503,20 @@ plugin_trim (struct backend *b, struct connection *conn,
   debug ("trim count=%" PRIu32 " offset=%" PRIu64 " fua=%d", count, offset,
          fua);

-  if (p->plugin.trim != NULL)
-    r = p->plugin.trim (connection_get_handle (conn, 0), count, offset);
+  if (fua && plugin_can_fua (b, conn) != NBDKIT_FUA_NATIVE) {
+    flags &= ~NBDKIT_FLAG_FUA;
+    need_flush = true;
+  }
+  if (p->plugin.trim)
+    r = p->plugin.trim (connection_get_handle (conn, 0), count, offset, flags);
+  else if (p->plugin._trim_old)
+    r = p->plugin._trim_old (connection_get_handle (conn, 0), count, offset);
   else {
     *err = EINVAL;
     return -1;
   }
-  if (r != -1 && fua) {
-    assert (p->plugin.flush);
-    r = p->plugin.flush (connection_get_handle (conn, 0));
-  }
+  if (r != -1 && need_flush)
+    r = plugin_flush (b, conn, 0, err);
   if (r == -1)
     *err = get_error (p);
   return r;
@@ -500,9 +529,11 @@ plugin_zero (struct backend *b, struct connection *conn,
   struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
   char *buf;
   uint32_t limit;
-  int result;
-  int may_trim = (flags & NBDKIT_FLAG_MAY_TRIM) != 0;
+  int r = -1;
+  bool may_trim = flags & NBDKIT_FLAG_MAY_TRIM;
   bool fua = flags & NBDKIT_FLAG_FUA;
+  bool emulate = false;
+  bool need_flush = false;

   assert (connection_get_handle (conn, 0));
   assert (!(flags & ~(NBDKIT_FLAG_MAY_TRIM | NBDKIT_FLAG_FUA)));
@@ -510,19 +541,27 @@ plugin_zero (struct backend *b, struct connection *conn,
   debug ("zero count=%" PRIu32 " offset=%" PRIu64 " may_trim=%d fua=%d",
          count, offset, may_trim, fua);

+  if (fua && plugin_can_fua (b, conn) != NBDKIT_FUA_NATIVE) {
+    flags &= ~NBDKIT_FLAG_FUA;
+    need_flush = true;
+  }
   if (!count)
     return 0;
-  if (p->plugin.zero) {
-    errno = 0;
-    result = p->plugin.zero (connection_get_handle (conn, 0),
-                             count, offset, may_trim);
-    if (result == -1)
-      *err = get_error (p);
-    if (result == 0 || *err != EOPNOTSUPP)
-      goto done;
-  }
+  errno = 0;
+  if (p->plugin.zero)
+    r = p->plugin.zero (connection_get_handle (conn, 0), count, offset, flags);
+  else if (p->plugin._zero_old)
+    r = p->plugin._zero_old (connection_get_handle (conn, 0), count, offset,
+                             may_trim);
+  else
+    emulate = true;
+  if (r == -1)
+    *err = emulate ? EOPNOTSUPP : get_error (p);
+  if (r == 0 || *err != EOPNOTSUPP)
+    goto done;

-  assert (p->plugin.pwrite);
+  assert (p->plugin.pwrite || p->plugin._pwrite_old);
+  flags &= ~NBDKIT_FLAG_MAY_TRIM;
   threadlocal_set_error (0);
   limit = count < MAX_REQUEST_SIZE ? count : MAX_REQUEST_SIZE;
   buf = calloc (limit, 1);
@@ -532,9 +571,8 @@ plugin_zero (struct backend *b, struct connection *conn,
   }

   while (count) {
-    result = p->plugin.pwrite (connection_get_handle (conn, 0),
-                               buf, limit, offset);
-    if (result == -1)
+    r = plugin_pwrite (b, conn, buf, limit, offset, flags, err);
+    if (r == -1)
       break;
     count -= limit;
     if (count < limit)
@@ -546,13 +584,11 @@ plugin_zero (struct backend *b, struct connection *conn,
   errno = *err;

  done:
-  if (result != -1 && fua) {
-    assert (p->plugin.flush);
-    result = p->plugin.flush (connection_get_handle (conn, 0));
-  }
-  if (result == -1)
+  if (r != -1 && need_flush)
+    r = plugin_flush (b, conn, 0, err);
+  if (r == -1)
     *err = get_error (p);
-  return result;
+  return r;
 }

 static struct backend plugin_functions = {
@@ -619,7 +655,7 @@ plugin_register (size_t index, const char *filename,
   }

   /* Check for incompatible future versions. */
-  if (plugin->_api_version != 1) {
+  if (plugin->_api_version < 0 || plugin->_api_version > 2) {
     fprintf (stderr, "%s: %s: plugin is incompatible with this version of nbdkit (_api_version = %d)\n",
              program_name, p->filename, plugin->_api_version);
     exit (EXIT_FAILURE);
@@ -654,7 +690,7 @@ plugin_register (size_t index, const char *filename,
              program_name, p->filename);
     exit (EXIT_FAILURE);
   }
-  if (p->plugin.pread == NULL) {
+  if (p->plugin.pread == NULL && p->plugin._pread_old == NULL) {
     fprintf (stderr, "%s: %s: plugin must have a .pread callback\n",
              program_name, p->filename);
     exit (EXIT_FAILURE);
-- 
2.14.3




More information about the Libguestfs mailing list