[Libguestfs] [nbdkit PATCH 5/7] plugins: Implement FUA support

Eric Blake eblake at redhat.com
Tue Jan 16 02:51:45 UTC 2018


Implement the logic to actually use FUA callbacks defined by a
plugin.  This includes separating the tracking of advertising
flush vs. fua to clients, as it is feasible that a plugin might
support fua but not flush.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 src/connections.c | 15 ++++++++++++---
 src/internal.h    |  1 +
 src/plugins.c     | 54 ++++++++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/src/connections.c b/src/connections.c
index e4a0a82..2884f0e 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -79,6 +79,7 @@ struct connection {
   int is_rotational;
   int can_trim;
   int using_tls;
+  int can_fua;

   int sockin, sockout;
   connection_recv_function recv;
@@ -367,10 +368,18 @@ compute_eflags (struct connection *conn, uint16_t *flags)
   if (fl == -1)
     return -1;
   if (fl) {
-    eflags |= NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA;
+    eflags |= NBD_FLAG_SEND_FLUSH;
     conn->can_flush = 1;
   }

+  fl = plugin_can_fua (conn);
+  if (fl == -1)
+    return -1;
+  if (fl) {
+    eflags |= NBD_FLAG_SEND_FUA;
+    conn->can_fua = 1;
+  }
+
   fl = plugin_is_rotational (conn);
   if (fl == -1)
     return -1;
@@ -814,7 +823,7 @@ validate_request (struct connection *conn,
     *error = EINVAL;
     return false;
   }
-  if (!conn->can_flush && (flags & NBD_CMD_FLAG_FUA)) {
+  if (!conn->can_fua && (flags & NBD_CMD_FLAG_FUA)) {
     nbdkit_error ("invalid request: FUA flag not supported");
     *error = EINVAL;
     return false;
@@ -873,7 +882,7 @@ handle_request (struct connection *conn,
                 uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,
                 void *buf)
 {
-  bool fua = conn->can_flush && (flags & NBD_CMD_FLAG_FUA);
+  bool fua = conn->can_fua && (flags & NBD_CMD_FLAG_FUA);

   /* The plugin should call nbdkit_set_error() to request a particular
      error, otherwise we fallback to errno or EIO. */
diff --git a/src/internal.h b/src/internal.h
index 3ab08d3..88a2eb8 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -163,6 +163,7 @@ extern int plugin_can_write (struct connection *conn);
 extern int plugin_can_flush (struct connection *conn);
 extern int plugin_is_rotational (struct connection *conn);
 extern int plugin_can_trim (struct connection *conn);
+extern int plugin_can_fua (struct connection *conn);
 extern int plugin_pread (struct connection *conn, void *buf, uint32_t count, uint64_t offset);
 extern int plugin_pwrite (struct connection *conn, void *buf, uint32_t count, uint64_t offset, int fua);
 extern int plugin_flush (struct connection *conn);
diff --git a/src/plugins.c b/src/plugins.c
index 61f0cc8..93cc535 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -271,6 +271,10 @@ plugin_dump_fields (void)
   HAS (flush);
   HAS (trim);
   HAS (zero);
+  HAS (can_fua);
+  HAS (pwrite_fua);
+  HAS (zero_fua);
+  HAS (trim_fua);
 #undef HAS

   /* Custom fields. */
@@ -437,7 +441,7 @@ plugin_can_write (struct connection *conn)
   if (plugin.can_write)
     return plugin.can_write (connection_get_handle (conn));
   else
-    return plugin.pwrite != NULL;
+    return plugin.pwrite || plugin.pwrite_fua;
 }

 int
@@ -454,6 +458,21 @@ plugin_can_flush (struct connection *conn)
     return plugin.flush != NULL;
 }

+int
+plugin_can_fua (struct connection *conn)
+{
+  assert (dl);
+  assert (connection_get_handle (conn));
+
+  debug ("can_fua");
+
+  if (plugin.can_fua)
+    return plugin.can_fua (connection_get_handle (conn));
+  if (plugin.pwrite_fua)
+    return 1;
+  return plugin_can_flush (conn);
+}
+
 int
 plugin_is_rotational (struct connection *conn)
 {
@@ -506,7 +525,12 @@ plugin_pwrite (struct connection *conn,
   debug ("pwrite count=%" PRIu32 " offset=%" PRIu64 " fua=%d", count, offset,
          fua);

-  if (plugin.pwrite != NULL)
+  if (plugin.pwrite_fua) {
+    r = plugin.pwrite_fua (connection_get_handle (conn), buf, count, offset,
+                           fua);
+    fua = 0;
+  }
+  else if (plugin.pwrite != NULL)
     r = plugin.pwrite (connection_get_handle (conn), buf, count, offset);
   else {
     errno = EROFS;
@@ -545,7 +569,11 @@ plugin_trim (struct connection *conn, uint32_t count, uint64_t offset, int fua)
   debug ("trim count=%" PRIu32 " offset=%" PRIu64 " fua=%d", count, offset,
          fua);

-  if (plugin.trim != NULL)
+  if (plugin.trim_fua) {
+    r = plugin.trim_fua (connection_get_handle (conn), count, offset, fua);
+    fua = 0;
+  }
+  else if (plugin.trim)
     r = plugin.trim (connection_get_handle (conn), count, offset);
   else {
     errno = EINVAL;
@@ -574,19 +602,25 @@ plugin_zero (struct connection *conn,

   if (!count)
     return 0;
-  if (plugin.zero) {
+  if (plugin.zero || plugin.zero_fua) {
     errno = 0;
-    result = plugin.zero (connection_get_handle (conn), count, offset, may_trim);
+    result = plugin.zero_fua
+      ? plugin.zero_fua (connection_get_handle (conn), count, offset, may_trim,
+                         fua)
+      : plugin.zero (connection_get_handle (conn), count, offset, may_trim);
     if (result == -1) {
       err = threadlocal_get_error ();
       if (!err && plugin_errno_is_preserved ())
         err = errno;
     }
-    if (result == 0 || err != EOPNOTSUPP)
+    if (result == 0 || err != EOPNOTSUPP) {
+      if (plugin.zero_fua)
+        fua = 0;
       goto done;
+    }
   }

-  assert (plugin.pwrite);
+  assert (plugin.pwrite || plugin.pwrite_fua);
   threadlocal_set_error (0);
   limit = count < MAX_REQUEST_SIZE ? count : MAX_REQUEST_SIZE;
   buf = calloc (limit, 1);
@@ -595,14 +629,18 @@ plugin_zero (struct connection *conn,
     return -1;
   }

+  /* If we have to emulate FUA, we can flush once at the end rather
+     than on each write iteration */
   while (count) {
-    result = plugin.pwrite (connection_get_handle (conn), buf, limit, offset);
+    result = plugin_pwrite (conn, buf, limit, offset, fua && plugin.pwrite_fua);
     if (result < 0)
       break;
     count -= limit;
     if (count < limit)
       limit = count;
   }
+  if (plugin.pwrite_fua)
+    fua = 0;

   err = errno;
   free (buf);
-- 
2.14.3




More information about the Libguestfs mailing list