[Libguestfs] [nbdkit PATCH v3 09/16] filters: Use struct context for next_ops

Eric Blake eblake at redhat.com
Fri Mar 5 23:31:13 UTC 2021


Expand the internal work done in an earlier patch to now also affect
the public interface exposed to filters.  Thankfully, we've already
declared filters to not have a stable API/ABI, so this change doesn't
hurt.  Previously all backend_* functions that got a 'struct backend*'
parameter then had to do a connection lookup for the matching handle;
now we pass a 'struct context*' that already has the handle.  The main
impact to this patch is that the connection lookup moves from
backend.c to filters.c, but it should be obvious that we are getting
closer to the point where a backend context is completely independent
of the current client connection.

However, while we continue to use 'struct context' in the server, I
intentionally exposed the public name as 'nbdkit_next'; the rationale
for this is to enable later patches where I merge 'next_ops' and
'nxdata' into a single pointer, all while still being opaque to the
filters.  Similarly, I kept nbdkit-filter.h rather type-agnostic
(typed strongly when used in server/ and the retry filter, but still
two indistinguishable void* in all other filters).  Later patches will
then tighten things up with the needed mechanical churn to all the
existing filters, including documentation updates.

One oddball is nbdkit_backend_reopen, which now needs to update nxdata
after adjusting contexts (in fact, nxdata can now be NULL after a
failed reopen has disassociated any previous context from the
connection).  The fact that all the retry filter tests still pass is a
good sign, even though future patches will be working to replace
nbdkit_backend_reopen with finer-grained functions usable by more
filters.
---
 include/nbdkit-filter.h              |  91 ++++++-------
 server/internal.h                    |  48 +++----
 server/backend.c                     | 184 +++++++++++++--------------
 server/connections.c                 |   8 +-
 server/extents.c                     |   6 +-
 server/filters.c                     | 132 ++++++++++++-------
 server/plugins.c                     |  10 +-
 server/protocol-handshake-newstyle.c |  15 ++-
 server/protocol-handshake.c          |  24 ++--
 server/protocol.c                    |  24 ++--
 filters/retry/retry.c                |  16 +--
 11 files changed, 302 insertions(+), 256 deletions(-)

diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index 0c172936..d16ea330 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -50,14 +50,17 @@ extern "C" {
 #define NBDKIT_ZERO_NATIVE   2

 #ifdef NBDKIT_INTERNAL
-/* Opaque type encapsulating all information needed for calling into
+/* Opaque types encapsulating all information needed for calling into
  * the next filter or plugin.
  */
 typedef struct backend nbdkit_backend;
+typedef struct context nbdkit_next;
 #elif defined NBDKIT_RETRY_FILTER /* Hack to expose reopen to retry filter */
 typedef struct nbdkit_backend nbdkit_backend;
+typedef struct nbdkit_next nbdkit_next;
 #else
 typedef void nbdkit_backend;
+typedef void nbdkit_next;
 #endif

 /* Next ops. */
@@ -76,34 +79,34 @@ typedef int nbdkit_next_open (nbdkit_backend *backend,

 struct nbdkit_next_ops {
   /* These callbacks are the same as normal plugin operations. */
-  int64_t (*get_size) (nbdkit_backend *nxdata);
-  const char * (*export_description) (nbdkit_backend *nxdata);
+  int64_t (*get_size) (nbdkit_next *nxdata);
+  const char * (*export_description) (nbdkit_next *nxdata);

-  int (*can_write) (nbdkit_backend *nxdata);
-  int (*can_flush) (nbdkit_backend *nxdata);
-  int (*is_rotational) (nbdkit_backend *nxdata);
-  int (*can_trim) (nbdkit_backend *nxdata);
-  int (*can_zero) (nbdkit_backend *nxdata);
-  int (*can_fast_zero) (nbdkit_backend *nxdata);
-  int (*can_extents) (nbdkit_backend *nxdata);
-  int (*can_fua) (nbdkit_backend *nxdata);
-  int (*can_multi_conn) (nbdkit_backend *nxdata);
-  int (*can_cache) (nbdkit_backend *nxdata);
+  int (*can_write) (nbdkit_next *nxdata);
+  int (*can_flush) (nbdkit_next *nxdata);
+  int (*is_rotational) (nbdkit_next *nxdata);
+  int (*can_trim) (nbdkit_next *nxdata);
+  int (*can_zero) (nbdkit_next *nxdata);
+  int (*can_fast_zero) (nbdkit_next *nxdata);
+  int (*can_extents) (nbdkit_next *nxdata);
+  int (*can_fua) (nbdkit_next *nxdata);
+  int (*can_multi_conn) (nbdkit_next *nxdata);
+  int (*can_cache) (nbdkit_next *nxdata);

-  int (*pread) (nbdkit_backend *nxdata,
+  int (*pread) (nbdkit_next *nxdata,
                 void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err);
-  int (*pwrite) (nbdkit_backend *nxdata,
+  int (*pwrite) (nbdkit_next *nxdata,
                  const void *buf, uint32_t count, uint64_t offset,
                  uint32_t flags, int *err);
-  int (*flush) (nbdkit_backend *nxdata, uint32_t flags, int *err);
-  int (*trim) (nbdkit_backend *nxdata, uint32_t count, uint64_t offset,
+  int (*flush) (nbdkit_next *nxdata, uint32_t flags, int *err);
+  int (*trim) (nbdkit_next *nxdata, uint32_t count, uint64_t offset,
                uint32_t flags, int *err);
-  int (*zero) (nbdkit_backend *nxdata, uint32_t count, uint64_t offset,
+  int (*zero) (nbdkit_next *nxdata, uint32_t count, uint64_t offset,
                uint32_t flags, int *err);
-  int (*extents) (nbdkit_backend *nxdata, uint32_t count, uint64_t offset,
+  int (*extents) (nbdkit_next *nxdata, uint32_t count, uint64_t offset,
                   uint32_t flags, struct nbdkit_extents *extents, int *err);
-  int (*cache) (nbdkit_backend *nxdata, uint32_t count, uint64_t offset,
+  int (*cache) (nbdkit_next *nxdata, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err);
 };

@@ -123,12 +126,12 @@ NBDKIT_EXTERN_DECL (struct nbdkit_extent, nbdkit_get_extent,
                     (const struct nbdkit_extents *, size_t));
 NBDKIT_EXTERN_DECL (struct nbdkit_extents *, nbdkit_extents_full,
                     (struct nbdkit_next_ops *next_ops,
-                     nbdkit_backend *nxdata,
+                     nbdkit_next *nxdata,
                      uint32_t count, uint64_t offset,
                      uint32_t flags, int *err));
 NBDKIT_EXTERN_DECL (int, nbdkit_extents_aligned,
                     (struct nbdkit_next_ops *next_ops,
-                     nbdkit_backend *nxdata,
+                     nbdkit_next *nxdata,
                      uint32_t count, uint64_t offset,
                      uint32_t flags, uint32_t align,
                      struct nbdkit_extents *extents, int *err));
@@ -153,7 +156,7 @@ NBDKIT_EXTERN_DECL (const struct nbdkit_export, nbdkit_get_export,
  */
 NBDKIT_EXTERN_DECL (int, nbdkit_backend_reopen,
                     (nbdkit_backend *backend, int readonly,
-                     const char *exportname, nbdkit_backend **nxdata));
+                     const char *exportname, nbdkit_next **nxdata));
 #endif

 /* Filter struct. */
@@ -200,56 +203,56 @@ struct nbdkit_filter {
                   int readonly, const char *exportname, int is_tls);
   void (*close) (void *handle);

-  int (*prepare) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*prepare) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                   void *handle, int readonly);
-  int (*finalize) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*finalize) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                    void *handle);

-  int64_t (*get_size) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int64_t (*get_size) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                        void *handle);
   const char * (*export_description) (struct nbdkit_next_ops *next_ops,
-                                      nbdkit_backend *nxdata, void *handle);
+                                      nbdkit_next *nxdata, void *handle);

-  int (*can_write) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*can_write) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                     void *handle);
-  int (*can_flush) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*can_flush) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                     void *handle);
   int (*is_rotational) (struct nbdkit_next_ops *next_ops,
-                        nbdkit_backend *nxdata, void *handle);
-  int (*can_trim) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+                        nbdkit_next *nxdata, void *handle);
+  int (*can_trim) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                    void *handle);
-  int (*can_zero) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*can_zero) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                    void *handle);
   int (*can_fast_zero) (struct nbdkit_next_ops *next_ops,
-                        nbdkit_backend *nxdata, void *handle);
-  int (*can_extents) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+                        nbdkit_next *nxdata, void *handle);
+  int (*can_extents) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                       void *handle);
-  int (*can_fua) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*can_fua) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                   void *handle);
   int (*can_multi_conn) (struct nbdkit_next_ops *next_ops,
-                         nbdkit_backend *nxdata, void *handle);
-  int (*can_cache) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+                         nbdkit_next *nxdata, void *handle);
+  int (*can_cache) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                     void *handle);

-  int (*pread) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*pread) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                 void *handle, void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err);
-  int (*pwrite) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*pwrite) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                  void *handle,
                  const void *buf, uint32_t count, uint64_t offset,
                  uint32_t flags, int *err);
-  int (*flush) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*flush) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                 void *handle, uint32_t flags, int *err);
-  int (*trim) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*trim) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                int *err);
-  int (*zero) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*zero) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                int *err);
-  int (*extents) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*extents) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                   void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                   struct nbdkit_extents *extents, int *err);
-  int (*cache) (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+  int (*cache) (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
                 int *err);
 };
diff --git a/server/internal.h b/server/internal.h
index e8828506..e5e8f797 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -415,8 +415,6 @@ extern int backend_list_exports (struct backend *b, int readonly,
   __attribute__((__nonnull__ (1, 3)));
 extern const char *backend_default_export (struct backend *b, int readonly)
   __attribute__((__nonnull__ (1)));
-extern const char *backend_export_description (struct backend *b)
-  __attribute__((__nonnull__ (1)));
 /* exportname is only valid for this call and almost certainly will be
  * freed on return of this function, so backends must save the
  * exportname if they need to refer to it later.
@@ -424,66 +422,68 @@ extern const char *backend_export_description (struct backend *b)
 extern struct context *backend_open (struct backend *b,
                                      int readonly, const char *exportname)
   __attribute__((__nonnull__ (1, 3)));
-extern int backend_prepare (struct backend *b)
+extern int backend_prepare (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_finalize (struct backend *b)
+extern int backend_finalize (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern void backend_close (struct backend *b)
+extern void backend_close (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern bool backend_valid_range (struct backend *b,
+extern bool backend_valid_range (struct context *c,
                                  uint64_t offset, uint32_t count)
   __attribute__((__nonnull__ (1)));

 extern int backend_reopen (struct backend *b,
                            int readonly, const char *exportname)
   __attribute__((__nonnull__ (1, 3)));
-extern int64_t backend_get_size (struct backend *b)
+extern const char *backend_export_description (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_write (struct backend *b)
+extern int64_t backend_get_size (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_flush (struct backend *b)
+extern int backend_can_write (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_is_rotational (struct backend *b)
+extern int backend_can_flush (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_trim (struct backend *b)
+extern int backend_is_rotational (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_zero (struct backend *b)
+extern int backend_can_trim (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_fast_zero (struct backend *b)
+extern int backend_can_zero (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_extents (struct backend *b)
+extern int backend_can_fast_zero (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_fua (struct backend *b)
+extern int backend_can_extents (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_multi_conn (struct backend *b)
+extern int backend_can_fua (struct context *c)
   __attribute__((__nonnull__ (1)));
-extern int backend_can_cache (struct backend *b)
+extern int backend_can_multi_conn (struct context *c)
+  __attribute__((__nonnull__ (1)));
+extern int backend_can_cache (struct context *c)
   __attribute__((__nonnull__ (1)));

-extern int backend_pread (struct backend *b,
+extern int backend_pread (struct context *c,
                           void *buf, uint32_t count, uint64_t offset,
                           uint32_t flags, int *err)
   __attribute__((__nonnull__ (1, 2, 6)));
-extern int backend_pwrite (struct backend *b,
+extern int backend_pwrite (struct context *c,
                            const void *buf, uint32_t count, uint64_t offset,
                            uint32_t flags, int *err)
   __attribute__((__nonnull__ (1, 2, 6)));
-extern int backend_flush (struct backend *b,
+extern int backend_flush (struct context *c,
                           uint32_t flags, int *err)
   __attribute__((__nonnull__ (1, 3)));
-extern int backend_trim (struct backend *b,
+extern int backend_trim (struct context *c,
                          uint32_t count, uint64_t offset, uint32_t flags,
                          int *err)
   __attribute__((__nonnull__ (1, 5)));
-extern int backend_zero (struct backend *b,
+extern int backend_zero (struct context *c,
                          uint32_t count, uint64_t offset, uint32_t flags,
                          int *err)
   __attribute__((__nonnull__ (1, 5)));
-extern int backend_extents (struct backend *b,
+extern int backend_extents (struct context *c,
                             uint32_t count, uint64_t offset, uint32_t flags,
                             struct nbdkit_extents *extents, int *err)
   __attribute__((__nonnull__ (1, 5, 6)));
-extern int backend_cache (struct backend *b,
+extern int backend_cache (struct context *c,
                           uint32_t count, uint64_t offset,
                           uint32_t flags, int *err)
   __attribute__((__nonnull__ (1, 5)));
diff --git a/server/backend.c b/server/backend.c
index 45a6da2f..94ff0caa 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -258,8 +258,11 @@ backend_open (struct backend *b, int readonly, const char *exportname)
   controlpath_debug ("%s: open returned handle %p", b->name, c->handle);

   if (c->handle == NULL) {
-    if (b->i) /* Do not strand backend if this layer failed */
-      backend_close (b->next);
+    if (b->i) { /* Do not strand backend if this layer failed */
+      struct context *c2 = get_context (conn, b->next);
+      if (c2 != NULL)
+        backend_close (c2);
+    }
     free (c);
     return NULL;
   }
@@ -269,10 +272,10 @@ backend_open (struct backend *b, int readonly, const char *exportname)
 }

 int
-backend_prepare (struct backend *b)
+backend_prepare (struct context *c)
 {
   GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle);
   assert ((c->state & (HANDLE_OPEN | HANDLE_CONNECTED)) == HANDLE_OPEN);
@@ -281,9 +284,11 @@ backend_prepare (struct backend *b)
    * plugin, similar to typical .open order.  But remember that
    * a filter may skip opening its backend.
    */
-  if (b->i && get_context (conn, b->next) != NULL &&
-      backend_prepare (b->next) == -1)
-    return -1;
+  if (b->i) {
+    struct context *c2 = get_context (conn, b->next);
+    if (c2 != NULL && backend_prepare (c2) == -1)
+      return -1;
+  }

   controlpath_debug ("%s: prepare readonly=%d", b->name, c->can_write == 0);

@@ -294,10 +299,10 @@ backend_prepare (struct backend *b)
 }

 int
-backend_finalize (struct backend *b)
+backend_finalize (struct context *c)
 {
   GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   /* Call these in reverse order to .prepare above, starting from the
    * filter furthest away from the plugin, and matching .close order.
@@ -316,16 +321,19 @@ backend_finalize (struct backend *b)
     }
   }

-  if (b->i && get_context (conn, b->next))
-    return backend_finalize (b->next);
+  if (b->i) {
+    struct context *c2 = get_context (conn, b->next);
+    if (c2 != NULL)
+      return backend_finalize (c2);
+  }
   return 0;
 }

 void
-backend_close (struct backend *b)
+backend_close (struct context *c)
 {
   GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   /* outer-to-inner order, opposite .open */
   assert (c->handle);
@@ -334,16 +342,16 @@ backend_close (struct backend *b)
   b->close (c);
   free (c);
   set_context (conn, b, NULL);
-  if (b->i && get_context (conn, b->next))
-    backend_close (b->next);
+  if (b->i) {
+    struct context *c2 = get_context (conn, b->next);
+    if (c2 != NULL)
+      backend_close (c2);
+  }
 }

 bool
-backend_valid_range (struct backend *b, uint64_t offset, uint32_t count)
+backend_valid_range (struct context *c, uint64_t offset, uint32_t count)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
-
   assert (c->exportsize <= INT64_MAX); /* Guaranteed by negotiation phase */
   return count > 0 && offset <= c->exportsize &&
     offset + count <= c->exportsize;
@@ -355,23 +363,24 @@ int
 backend_reopen (struct backend *b, int readonly, const char *exportname)
 {
   GET_CONN;
-  struct context *h;
+  struct context *c;

   controlpath_debug ("%s: reopen readonly=%d exportname=\"%s\"",
                      b->name, readonly, exportname);

-  if (get_context (conn, b)) {
-    if (backend_finalize (b) == -1)
+  c = get_context (conn, b);
+  if (c) {
+    if (backend_finalize (c) == -1)
       return -1;
-    backend_close (b);
+    backend_close (c);
   }
-  h = backend_open (b, readonly, exportname);
-  if (h == NULL)
+  c = backend_open (b, readonly, exportname);
+  if (c == NULL)
     return -1;
-  set_context (conn, b, h);
-  if (backend_prepare (b) == -1) {
-    backend_finalize (b);
-    backend_close (b);
+  set_context (conn, b, c);
+  if (backend_prepare (c) == -1) {
+    backend_finalize (c);
+    backend_close (c);
     return -1;
   }
   return 0;
@@ -380,10 +389,9 @@ backend_reopen (struct backend *b, int readonly, const char *exportname)
 /* Wrappers for all callbacks in a filter's struct nbdkit_next_ops. */

 const char *
-backend_export_description (struct backend *b)
+backend_export_description (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   const char *s;

   controlpath_debug ("%s: export_description", b->name);
@@ -402,10 +410,9 @@ backend_export_description (struct backend *b)
 }

 int64_t
-backend_get_size (struct backend *b)
+backend_get_size (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->exportsize == -1) {
@@ -416,10 +423,9 @@ backend_get_size (struct backend *b)
 }

 int
-backend_can_write (struct backend *b)
+backend_can_write (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_write == -1) {
@@ -430,10 +436,9 @@ backend_can_write (struct backend *b)
 }

 int
-backend_can_flush (struct backend *b)
+backend_can_flush (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_flush == -1) {
@@ -444,10 +449,9 @@ backend_can_flush (struct backend *b)
 }

 int
-backend_is_rotational (struct backend *b)
+backend_is_rotational (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->is_rotational == -1) {
@@ -458,16 +462,15 @@ backend_is_rotational (struct backend *b)
 }

 int
-backend_can_trim (struct backend *b)
+backend_can_trim (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_trim == -1) {
     controlpath_debug ("%s: can_trim", b->name);
-    r = backend_can_write (b);
+    r = backend_can_write (c);
     if (r != 1) {
       c->can_trim = 0;
       return r;
@@ -478,16 +481,15 @@ backend_can_trim (struct backend *b)
 }

 int
-backend_can_zero (struct backend *b)
+backend_can_zero (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_zero == -1) {
     controlpath_debug ("%s: can_zero", b->name);
-    r = backend_can_write (b);
+    r = backend_can_write (c);
     if (r != 1) {
       c->can_zero = NBDKIT_ZERO_NONE;
       return r; /* Relies on 0 == NBDKIT_ZERO_NONE */
@@ -498,16 +500,15 @@ backend_can_zero (struct backend *b)
 }

 int
-backend_can_fast_zero (struct backend *b)
+backend_can_fast_zero (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_fast_zero == -1) {
     controlpath_debug ("%s: can_fast_zero", b->name);
-    r = backend_can_zero (b);
+    r = backend_can_zero (c);
     if (r < NBDKIT_ZERO_EMULATE) {
       c->can_fast_zero = 0;
       return r; /* Relies on 0 == NBDKIT_ZERO_NONE */
@@ -518,10 +519,9 @@ backend_can_fast_zero (struct backend *b)
 }

 int
-backend_can_extents (struct backend *b)
+backend_can_extents (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_extents == -1) {
@@ -532,16 +532,15 @@ backend_can_extents (struct backend *b)
 }

 int
-backend_can_fua (struct backend *b)
+backend_can_fua (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_fua == -1) {
     controlpath_debug ("%s: can_fua", b->name);
-    r = backend_can_write (b);
+    r = backend_can_write (c);
     if (r != 1) {
       c->can_fua = NBDKIT_FUA_NONE;
       return r; /* Relies on 0 == NBDKIT_FUA_NONE */
@@ -552,10 +551,9 @@ backend_can_fua (struct backend *b)
 }

 int
-backend_can_multi_conn (struct backend *b)
+backend_can_multi_conn (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_multi_conn == -1) {
@@ -566,10 +564,9 @@ backend_can_multi_conn (struct backend *b)
 }

 int
-backend_can_cache (struct backend *b)
+backend_can_cache (struct context *c)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   if (c->can_cache == -1) {
@@ -580,16 +577,15 @@ backend_can_cache (struct backend *b)
 }

 int
-backend_pread (struct backend *b,
+backend_pread (struct context *c,
                void *buf, uint32_t count, uint64_t offset,
                uint32_t flags, int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
-  assert (backend_valid_range (b, offset, count));
+  assert (backend_valid_range (c, offset, count));
   assert (flags == 0);
   datapath_debug ("%s: pread count=%" PRIu32 " offset=%" PRIu64,
                   b->name, count, offset);
@@ -601,18 +597,17 @@ backend_pread (struct backend *b,
 }

 int
-backend_pwrite (struct backend *b,
+backend_pwrite (struct context *c,
                 const void *buf, uint32_t count, uint64_t offset,
                 uint32_t flags, int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   bool fua = !!(flags & NBDKIT_FLAG_FUA);
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   assert (c->can_write == 1);
-  assert (backend_valid_range (b, offset, count));
+  assert (backend_valid_range (c, offset, count));
   assert (!(flags & ~NBDKIT_FLAG_FUA));
   if (fua)
     assert (c->can_fua > NBDKIT_FUA_NONE);
@@ -626,11 +621,10 @@ backend_pwrite (struct backend *b,
 }

 int
-backend_flush (struct backend *b,
+backend_flush (struct context *c,
                uint32_t flags, int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -645,19 +639,18 @@ backend_flush (struct backend *b,
 }

 int
-backend_trim (struct backend *b,
+backend_trim (struct context *c,
               uint32_t count, uint64_t offset, uint32_t flags,
               int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   bool fua = !!(flags & NBDKIT_FLAG_FUA);
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   assert (c->can_write == 1);
   assert (c->can_trim == 1);
-  assert (backend_valid_range (b, offset, count));
+  assert (backend_valid_range (c, offset, count));
   assert (!(flags & ~NBDKIT_FLAG_FUA));
   if (fua)
     assert (c->can_fua > NBDKIT_FUA_NONE);
@@ -671,12 +664,11 @@ backend_trim (struct backend *b,
 }

 int
-backend_zero (struct backend *b,
+backend_zero (struct context *c,
               uint32_t count, uint64_t offset, uint32_t flags,
               int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   bool fua = !!(flags & NBDKIT_FLAG_FUA);
   bool fast = !!(flags & NBDKIT_FLAG_FAST_ZERO);
   int r;
@@ -684,7 +676,7 @@ backend_zero (struct backend *b,
   assert (c->handle && (c->state & HANDLE_CONNECTED));
   assert (c->can_write == 1);
   assert (c->can_zero > NBDKIT_ZERO_NONE);
-  assert (backend_valid_range (b, offset, count));
+  assert (backend_valid_range (c, offset, count));
   assert (!(flags & ~(NBDKIT_FLAG_MAY_TRIM | NBDKIT_FLAG_FUA |
                       NBDKIT_FLAG_FAST_ZERO)));
   if (fua)
@@ -706,17 +698,16 @@ backend_zero (struct backend *b,
 }

 int
-backend_extents (struct backend *b,
+backend_extents (struct context *c,
                  uint32_t count, uint64_t offset, uint32_t flags,
                  struct nbdkit_extents *extents, int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   assert (c->can_extents >= 0);
-  assert (backend_valid_range (b, offset, count));
+  assert (backend_valid_range (c, offset, count));
   assert (!(flags & ~NBDKIT_FLAG_REQ_ONE));
   datapath_debug ("%s: extents count=%" PRIu32 " offset=%" PRIu64 " req_one=%d",
                   b->name, count, offset, !!(flags & NBDKIT_FLAG_REQ_ONE));
@@ -737,17 +728,16 @@ backend_extents (struct backend *b,
 }

 int
-backend_cache (struct backend *b,
+backend_cache (struct context *c,
                uint32_t count, uint64_t offset,
                uint32_t flags, int *err)
 {
-  GET_CONN;
-  struct context *c = get_context (conn, b);
+  struct backend *b = c->b;
   int r;

   assert (c->handle && (c->state & HANDLE_CONNECTED));
   assert (c->can_cache > NBDKIT_CACHE_NONE);
-  assert (backend_valid_range (b, offset, count));
+  assert (backend_valid_range (c, offset, count));
   assert (flags == 0);
   datapath_debug ("%s: cache count=%" PRIu32 " offset=%" PRIu64,
                   b->name, count, offset);
@@ -758,7 +748,7 @@ backend_cache (struct backend *b,

     while (count) {
       limit = MIN (count, sizeof buf);
-      if (backend_pread (b, buf, limit, offset, flags, err) == -1)
+      if (backend_pread (c, buf, limit, offset, flags, err) == -1)
         return -1;
       count -= limit;
     }
diff --git a/server/connections.c b/server/connections.c
index 0115875b..930491a6 100644
--- a/server/connections.c
+++ b/server/connections.c
@@ -224,7 +224,7 @@ handle_single_connection (int sockin, int sockout)

   /* Finalize (for filters), called just before close. */
   lock_request ();
-  r = backend_finalize (top);
+  r = backend_finalize (get_context (conn, top));
   unlock_request ();
   if (r == -1)
     goto done;
@@ -348,6 +348,7 @@ static void
 free_connection (struct connection *conn)
 {
   struct backend *b;
+  struct context *c;

   if (!conn)
     return;
@@ -360,8 +361,9 @@ free_connection (struct connection *conn)
    */
   if (!quit) {
     lock_request ();
-    if (get_context (conn, top))
-      backend_close (top);
+    c = get_context (conn, top);
+    if (c)
+      backend_close (c);
     unlock_request ();
   }

diff --git a/server/extents.c b/server/extents.c
index cbc0f0dd..9699fe6a 100644
--- a/server/extents.c
+++ b/server/extents.c
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2019-2020 Red Hat Inc.
+ * Copyright (C) 2019-2021 Red Hat Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -213,7 +213,7 @@ nbdkit_add_extent (struct nbdkit_extents *exts,
 /* Compute aligned extents on behalf of a filter. */
 int
 nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
-                        nbdkit_backend *nxdata,
+                        nbdkit_next *nxdata,
                         uint32_t count, uint64_t offset,
                         uint32_t flags, uint32_t align,
                         struct nbdkit_extents *exts, int *err)
@@ -298,7 +298,7 @@ nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
  * covering the region [offset..offset+count-1].
  */
 struct nbdkit_extents *
-nbdkit_extents_full (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+nbdkit_extents_full (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                      uint32_t count, uint64_t offset, uint32_t flags,
                      int *err)
 {
diff --git a/server/filters.c b/server/filters.c
index 7986e56f..5f2087d1 100644
--- a/server/filters.c
+++ b/server/filters.c
@@ -328,11 +328,13 @@ static struct nbdkit_next_ops next_ops = {
 static int
 filter_prepare (struct context *c, int readonly)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.prepare &&
-      f->filter.prepare (&next_ops, b->next, c->handle, readonly) == -1)
+      f->filter.prepare (&next_ops, c_next, c->handle, readonly) == -1)
     return -1;

   return 0;
@@ -341,11 +343,13 @@ filter_prepare (struct context *c, int readonly)
 static int
 filter_finalize (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.finalize &&
-      f->filter.finalize (&next_ops, b->next, c->handle) == -1)
+      f->filter.finalize (&next_ops, c_next, c->handle) == -1)
     return -1;
   return 0;
 }
@@ -353,145 +357,169 @@ filter_finalize (struct context *c)
 static const char *
 filter_export_description (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.export_description)
-    return f->filter.export_description (&next_ops, b->next, c->handle);
+    return f->filter.export_description (&next_ops, c_next, c->handle);
   else
-    return backend_export_description (b->next);
+    return backend_export_description (c_next);
 }

 static int64_t
 filter_get_size (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.get_size)
-    return f->filter.get_size (&next_ops, b->next, c->handle);
+    return f->filter.get_size (&next_ops, c_next, c->handle);
   else
-    return backend_get_size (b->next);
+    return backend_get_size (c_next);
 }

 static int
 filter_can_write (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_write)
-    return f->filter.can_write (&next_ops, b->next, c->handle);
+    return f->filter.can_write (&next_ops, c_next, c->handle);
   else
-    return backend_can_write (b->next);
+    return backend_can_write (c_next);
 }

 static int
 filter_can_flush (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_flush)
-    return f->filter.can_flush (&next_ops, b->next, c->handle);
+    return f->filter.can_flush (&next_ops, c_next, c->handle);
   else
-    return backend_can_flush (b->next);
+    return backend_can_flush (c_next);
 }

 static int
 filter_is_rotational (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.is_rotational)
-    return f->filter.is_rotational (&next_ops, b->next, c->handle);
+    return f->filter.is_rotational (&next_ops, c_next, c->handle);
   else
-    return backend_is_rotational (b->next);
+    return backend_is_rotational (c_next);
 }

 static int
 filter_can_trim (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_trim)
-    return f->filter.can_trim (&next_ops, b->next, c->handle);
+    return f->filter.can_trim (&next_ops, c_next, c->handle);
   else
-    return backend_can_trim (b->next);
+    return backend_can_trim (c_next);
 }

 static int
 filter_can_zero (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_zero)
-    return f->filter.can_zero (&next_ops, b->next, c->handle);
+    return f->filter.can_zero (&next_ops, c_next, c->handle);
   else
-    return backend_can_zero (b->next);
+    return backend_can_zero (c_next);
 }

 static int
 filter_can_fast_zero (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_fast_zero)
-    return f->filter.can_fast_zero (&next_ops, b->next, c->handle);
+    return f->filter.can_fast_zero (&next_ops, c_next, c->handle);
   else
-    return backend_can_fast_zero (b->next);
+    return backend_can_fast_zero (c_next);
 }

 static int
 filter_can_extents (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_extents)
-    return f->filter.can_extents (&next_ops, b->next, c->handle);
+    return f->filter.can_extents (&next_ops, c_next, c->handle);
   else
-    return backend_can_extents (b->next);
+    return backend_can_extents (c_next);
 }

 static int
 filter_can_fua (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_fua)
-    return f->filter.can_fua (&next_ops, b->next, c->handle);
+    return f->filter.can_fua (&next_ops, c_next, c->handle);
   else
-    return backend_can_fua (b->next);
+    return backend_can_fua (c_next);
 }

 static int
 filter_can_multi_conn (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_multi_conn)
-    return f->filter.can_multi_conn (&next_ops, b->next, c->handle);
+    return f->filter.can_multi_conn (&next_ops, c_next, c->handle);
   else
-    return backend_can_multi_conn (b->next);
+    return backend_can_multi_conn (c_next);
 }

 static int
 filter_can_cache (struct context *c)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.can_cache)
-    return f->filter.can_cache (&next_ops, b->next, c->handle);
+    return f->filter.can_cache (&next_ops, c_next, c->handle);
   else
-    return backend_can_cache (b->next);
+    return backend_can_cache (c_next);
 }

 static int
@@ -499,14 +527,16 @@ filter_pread (struct context *c,
               void *buf, uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.pread)
-    return f->filter.pread (&next_ops, b->next, c->handle,
+    return f->filter.pread (&next_ops, c_next, c->handle,
                             buf, count, offset, flags, err);
   else
-    return backend_pread (b->next, buf, count, offset, flags, err);
+    return backend_pread (c_next, buf, count, offset, flags, err);
 }

 static int
@@ -514,27 +544,31 @@ filter_pwrite (struct context *c,
                const void *buf, uint32_t count, uint64_t offset,
                uint32_t flags, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.pwrite)
-    return f->filter.pwrite (&next_ops, b->next, c->handle,
+    return f->filter.pwrite (&next_ops, c_next, c->handle,
                              buf, count, offset, flags, err);
   else
-    return backend_pwrite (b->next, buf, count, offset, flags, err);
+    return backend_pwrite (c_next, buf, count, offset, flags, err);
 }

 static int
 filter_flush (struct context *c,
               uint32_t flags, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.flush)
-    return f->filter.flush (&next_ops, b->next, c->handle, flags, err);
+    return f->filter.flush (&next_ops, c_next, c->handle, flags, err);
   else
-    return backend_flush (b->next, flags, err);
+    return backend_flush (c_next, flags, err);
 }

 static int
@@ -542,28 +576,32 @@ filter_trim (struct context *c,
              uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.trim)
-    return f->filter.trim (&next_ops, b->next, c->handle, count, offset,
+    return f->filter.trim (&next_ops, c_next, c->handle, count, offset,
                            flags, err);
   else
-    return backend_trim (b->next, count, offset, flags, err);
+    return backend_trim (c_next, count, offset, flags, err);
 }

 static int
 filter_zero (struct context *c,
              uint32_t count, uint64_t offset, uint32_t flags, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.zero)
-    return f->filter.zero (&next_ops, b->next, c->handle,
+    return f->filter.zero (&next_ops, c_next, c->handle,
                            count, offset, flags, err);
   else
-    return backend_zero (b->next, count, offset, flags, err);
+    return backend_zero (c_next, count, offset, flags, err);
 }

 static int
@@ -571,15 +609,17 @@ filter_extents (struct context *c,
                 uint32_t count, uint64_t offset, uint32_t flags,
                 struct nbdkit_extents *extents, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.extents)
-    return f->filter.extents (&next_ops, b->next, c->handle,
+    return f->filter.extents (&next_ops, c_next, c->handle,
                               count, offset, flags,
                               extents, err);
   else
-    return backend_extents (b->next, count, offset, flags,
+    return backend_extents (c_next, count, offset, flags,
                             extents, err);
 }

@@ -588,14 +628,16 @@ filter_cache (struct context *c,
               uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
 {
+  GET_CONN;
   struct backend *b = c->b;
   struct backend_filter *f = container_of (b, struct backend_filter, backend);
+  struct context *c_next = get_context (conn, b->next);

   if (f->filter.cache)
-    return f->filter.cache (&next_ops, b->next, c->handle,
+    return f->filter.cache (&next_ops, c_next, c->handle,
                             count, offset, flags, err);
   else
-    return backend_cache (b->next, count, offset, flags, err);
+    return backend_cache (c_next, count, offset, flags, err);
 }

 static struct backend filter_functions = {
@@ -696,9 +738,11 @@ filter_register (struct backend *next, size_t index, const char *filename,

 int
 nbdkit_backend_reopen (struct backend *b, int readonly,
-                       const char *exportname, struct backend **nxdata)
+                       const char *exportname, struct context **nxdata)
 {
-  /* For now, we don't need to update nxdata. */
-  assert (b == *nxdata);
-  return backend_reopen (b, readonly, exportname);
+  GET_CONN;
+  int r = backend_reopen (b, readonly, exportname);
+
+  *nxdata = get_context (conn, b);
+  return r;
 }
diff --git a/server/plugins.c b/server/plugins.c
index 17126ea1..9144cdae 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -488,7 +488,7 @@ plugin_can_fast_zero (struct context *c)
    */
   if (p->plugin.zero == NULL)
     return 1;
-  r = backend_can_zero (b);
+  r = backend_can_zero (c);
   if (r == -1)
     return -1;
   return !r;
@@ -625,7 +625,7 @@ plugin_pwrite (struct context *c,
   bool fua = flags & NBDKIT_FLAG_FUA;
   bool need_flush = false;

-  if (fua && backend_can_fua (b) != NBDKIT_FUA_NATIVE) {
+  if (fua && backend_can_fua (c) != NBDKIT_FUA_NATIVE) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -654,7 +654,7 @@ plugin_trim (struct context *c,
   bool fua = flags & NBDKIT_FLAG_FUA;
   bool need_flush = false;

-  if (fua && backend_can_fua (b) != NBDKIT_FUA_NATIVE) {
+  if (fua && backend_can_fua (c) != NBDKIT_FUA_NATIVE) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
@@ -686,14 +686,14 @@ plugin_zero (struct context *c,
   bool emulate = false;
   bool need_flush = false;

-  if (fua && backend_can_fua (b) != NBDKIT_FUA_NATIVE) {
+  if (fua && backend_can_fua (c) != NBDKIT_FUA_NATIVE) {
     flags &= ~NBDKIT_FLAG_FUA;
     need_flush = true;
   }
   if (!count)
     return 0;

-  if (backend_can_zero (b) == NBDKIT_ZERO_NATIVE) {
+  if (backend_can_zero (c) == NBDKIT_ZERO_NATIVE) {
     errno = 0;
     if (p->plugin.zero)
       r = p->plugin.zero (c->handle, count, offset, flags);
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index 4bc802cc..cfcd090d 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -341,6 +341,7 @@ negotiate_handshake_newstyle_options (void)
   const char *optname;
   uint64_t exportsize;
   struct backend *b;
+  struct context *c;

   for (nr_options = MAX_NR_OPTIONS; nr_options > 0; --nr_options) {
     CLEANUP_FREE char *data = NULL;
@@ -567,10 +568,11 @@ negotiate_handshake_newstyle_options (void)
          */
         if (finish_newstyle_options (&exportsize,
                                      &data[4], exportnamelen) == -1) {
-          if (get_context (conn, top)) {
-            if (backend_finalize (top) == -1)
+          c = get_context (conn, top);
+          if (c) {
+            if (backend_finalize (c) == -1)
               return -1;
-            backend_close (top);
+            backend_close (c);
           }
           if (send_newstyle_option_reply (option, NBD_REP_ERR_UNKNOWN) == -1)
             return -1;
@@ -588,6 +590,7 @@ negotiate_handshake_newstyle_options (void)
          * NBD_INFO_EXPORT if it was requested, because we replied
          * already above).
          */
+        c = get_context (conn, top);
         for (i = 0; i < nrinfos; ++i) {
           memcpy (&info, &data[4 + exportnamelen + 2 + i*2], 2);
           info = be16toh (info);
@@ -616,7 +619,7 @@ negotiate_handshake_newstyle_options (void)
             break;
           case NBD_INFO_DESCRIPTION:
             {
-              const char *desc = backend_export_description (top);
+              const char *desc = backend_export_description (c);

               if (!desc) {
                 debug ("newstyle negotiation: %s: "
@@ -647,9 +650,9 @@ negotiate_handshake_newstyle_options (void)
         return -1;

       if (option == NBD_OPT_INFO) {
-        if (backend_finalize (top) == -1)
+        if (backend_finalize (c) == -1)
           return -1;
-        backend_close (top);
+        backend_close (c);
       }

       break;
diff --git a/server/protocol-handshake.c b/server/protocol-handshake.c
index 69f8b530..61521d43 100644
--- a/server/protocol-handshake.c
+++ b/server/protocol-handshake.c
@@ -87,10 +87,10 @@ protocol_common_open (uint64_t *exportsize, uint16_t *flags,
   set_context (conn, top, c);

   /* Prepare (for filters), called just after open. */
-  if (backend_prepare (top) == -1)
+  if (backend_prepare (c) == -1)
     return -1;

-  size = backend_get_size (top);
+  size = backend_get_size (c);
   if (size == -1)
     return -1;
   if (size < 0) {
@@ -102,56 +102,56 @@ protocol_common_open (uint64_t *exportsize, uint16_t *flags,
   /* Check all flags even if they won't be advertised, to prime the
    * cache and make later request validation easier.
    */
-  fl = backend_can_write (top);
+  fl = backend_can_write (c);
   if (fl == -1)
     return -1;
   if (!fl)
     eflags |= NBD_FLAG_READ_ONLY;

-  fl = backend_can_zero (top);
+  fl = backend_can_zero (c);
   if (fl == -1)
     return -1;
   if (fl)
     eflags |= NBD_FLAG_SEND_WRITE_ZEROES;

-  fl = backend_can_fast_zero (top);
+  fl = backend_can_fast_zero (c);
   if (fl == -1)
     return -1;
   if (fl)
     eflags |= NBD_FLAG_SEND_FAST_ZERO;

-  fl = backend_can_trim (top);
+  fl = backend_can_trim (c);
   if (fl == -1)
     return -1;
   if (fl)
     eflags |= NBD_FLAG_SEND_TRIM;

-  fl = backend_can_fua (top);
+  fl = backend_can_fua (c);
   if (fl == -1)
     return -1;
   if (fl)
     eflags |= NBD_FLAG_SEND_FUA;

-  fl = backend_can_flush (top);
+  fl = backend_can_flush (c);
   if (fl == -1)
     return -1;
   if (fl)
     eflags |= NBD_FLAG_SEND_FLUSH;

-  fl = backend_is_rotational (top);
+  fl = backend_is_rotational (c);
   if (fl == -1)
     return -1;
   if (fl)
     eflags |= NBD_FLAG_ROTATIONAL;

   /* multi-conn is useless if parallel connections are not allowed. */
-  fl = backend_can_multi_conn (top);
+  fl = backend_can_multi_conn (c);
   if (fl == -1)
     return -1;
   if (fl && (thread_model > NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS))
     eflags |= NBD_FLAG_CAN_MULTI_CONN;

-  fl = backend_can_cache (top);
+  fl = backend_can_cache (c);
   if (fl == -1)
     return -1;
   if (fl)
@@ -162,7 +162,7 @@ protocol_common_open (uint64_t *exportsize, uint16_t *flags,
    * not have to worry about errors, and makes test-layers easier to
    * write.
    */
-  fl = backend_can_extents (top);
+  fl = backend_can_extents (c);
   if (fl == -1)
     return -1;

diff --git a/server/protocol.c b/server/protocol.c
index e7ba4bf1..fedc701a 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2020 Red Hat Inc.
+ * Copyright (C) 2013-2021 Red Hat Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -53,6 +53,7 @@ validate_request (uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,
                   uint32_t *error)
 {
   GET_CONN;
+  struct context *c = get_context (conn, top);

   /* Readonly connection? */
   if (conn->eflags & NBD_FLAG_READ_ONLY &&
@@ -72,7 +73,7 @@ validate_request (uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,
   case NBD_CMD_TRIM:
   case NBD_CMD_WRITE_ZEROES:
   case NBD_CMD_BLOCK_STATUS:
-    if (!backend_valid_range (top, offset, count)) {
+    if (!backend_valid_range (c, offset, count)) {
       /* XXX Allow writes to extend the disk? */
       nbdkit_error ("invalid request: %s: offset and count are out of range: "
                     "offset=%" PRIu64 " count=%" PRIu32,
@@ -229,6 +230,8 @@ static uint32_t
 handle_request (uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,
                 void *buf, struct nbdkit_extents *extents)
 {
+  GET_CONN;
+  struct context *c = get_context (conn, top);
   uint32_t f = 0;
   int err = 0;

@@ -238,31 +241,31 @@ handle_request (uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,

   switch (cmd) {
   case NBD_CMD_READ:
-    if (backend_pread (top, buf, count, offset, 0, &err) == -1)
+    if (backend_pread (c, buf, count, offset, 0, &err) == -1)
       return err;
     break;

   case NBD_CMD_WRITE:
     if (flags & NBD_CMD_FLAG_FUA)
       f |= NBDKIT_FLAG_FUA;
-    if (backend_pwrite (top, buf, count, offset, f, &err) == -1)
+    if (backend_pwrite (c, buf, count, offset, f, &err) == -1)
       return err;
     break;

   case NBD_CMD_FLUSH:
-    if (backend_flush (top, 0, &err) == -1)
+    if (backend_flush (c, 0, &err) == -1)
       return err;
     break;

   case NBD_CMD_TRIM:
     if (flags & NBD_CMD_FLAG_FUA)
       f |= NBDKIT_FLAG_FUA;
-    if (backend_trim (top, count, offset, f, &err) == -1)
+    if (backend_trim (c, count, offset, f, &err) == -1)
       return err;
     break;

   case NBD_CMD_CACHE:
-    if (backend_cache (top, count, offset, 0, &err) == -1)
+    if (backend_cache (c, count, offset, 0, &err) == -1)
       return err;
     break;

@@ -273,14 +276,14 @@ handle_request (uint16_t cmd, uint16_t flags, uint64_t offset, uint32_t count,
       f |= NBDKIT_FLAG_FUA;
     if (flags & NBD_CMD_FLAG_FAST_ZERO)
       f |= NBDKIT_FLAG_FAST_ZERO;
-    if (backend_zero (top, count, offset, f, &err) == -1)
+    if (backend_zero (c, count, offset, f, &err) == -1)
       return err;
     break;

   case NBD_CMD_BLOCK_STATUS:
     if (flags & NBD_CMD_FLAG_REQ_ONE)
       f |= NBDKIT_FLAG_REQ_ONE;
-    if (backend_extents (top, count, offset, f,
+    if (backend_extents (c, count, offset, f,
                          extents, &err) == -1)
       return err;
     break;
@@ -683,7 +686,8 @@ protocol_recv_request_send_reply (void)

     /* Allocate the extents list for block status only. */
     if (cmd == NBD_CMD_BLOCK_STATUS) {
-      extents = nbdkit_extents_new (offset, backend_get_size (top));
+      struct context *c = get_context (conn, top);
+      extents = nbdkit_extents_new (offset, backend_get_size (c));
       if (extents == NULL) {
         error = ENOMEM;
         goto send_reply;
diff --git a/filters/retry/retry.c b/filters/retry/retry.c
index abd49eb8..8decee6a 100644
--- a/filters/retry/retry.c
+++ b/filters/retry/retry.c
@@ -176,7 +176,7 @@ valid_range (struct nbdkit_next_ops *next_ops, void *nxdata,

 static bool
 do_retry (struct retry_handle *h, struct retry_data *data,
-          nbdkit_backend **nxdata, const char *method, int *err)
+          nbdkit_next **nxdata, const char *method, int *err)
 {
   /* If it's the first retry, initialize the other fields in *data. */
   if (data->retry == 0)
@@ -225,7 +225,7 @@ do_retry (struct retry_handle *h, struct retry_data *data,
 }

 static int
-retry_pread (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_pread (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
              void *handle, void *buf, uint32_t count, uint64_t offset,
              uint32_t flags, int *err)
 {
@@ -246,7 +246,7 @@ retry_pread (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,

 /* Write. */
 static int
-retry_pwrite (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_pwrite (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
               void *handle,
               const void *buf, uint32_t count, uint64_t offset,
               uint32_t flags, int *err)
@@ -281,7 +281,7 @@ retry_pwrite (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,

 /* Trim. */
 static int
-retry_trim (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_trim (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
             void *handle,
             uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
@@ -316,7 +316,7 @@ retry_trim (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,

 /* Flush. */
 static int
-retry_flush (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_flush (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
              void *handle, uint32_t flags,
              int *err)
 {
@@ -341,7 +341,7 @@ retry_flush (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,

 /* Zero. */
 static int
-retry_zero (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_zero (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
             void *handle,
             uint32_t count, uint64_t offset, uint32_t flags,
             int *err)
@@ -381,7 +381,7 @@ retry_zero (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,

 /* Extents. */
 static int
-retry_extents (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_extents (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
                void *handle,
                uint32_t count, uint64_t offset, uint32_t flags,
                struct nbdkit_extents *extents, int *err)
@@ -429,7 +429,7 @@ retry_extents (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,

 /* Cache. */
 static int
-retry_cache (struct nbdkit_next_ops *next_ops, nbdkit_backend *nxdata,
+retry_cache (struct nbdkit_next_ops *next_ops, nbdkit_next *nxdata,
              void *handle,
              uint32_t count, uint64_t offset, uint32_t flags,
              int *err)
-- 
2.30.1




More information about the Libguestfs mailing list