<div dir="auto"><div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Aug 6, 2020, 16:16 Richard W.M. Jones <<a href="mailto:rjones@redhat.com">rjones@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">The pool is only used for readonly connections, since writable<br>
connections usually take a lock on the server side and therefore you<br>
cannot open more than one.<br>
---<br>
 plugins/vddk/nbdkit-vddk-plugin.pod |   7 +<br>
 plugins/vddk/vddk.c                 | 201 ++++++++++++++++++++++------<br>
 2 files changed, 164 insertions(+), 44 deletions(-)<br>
<br>
diff --git a/plugins/vddk/nbdkit-vddk-plugin.pod b/plugins/vddk/nbdkit-vddk-plugin.pod<br>
index e5539da9..288aed4b 100644<br>
--- a/plugins/vddk/nbdkit-vddk-plugin.pod<br>
+++ b/plugins/vddk/nbdkit-vddk-plugin.pod<br>
@@ -175,6 +175,13 @@ Read the password from file descriptor number C<FD>, inherited from<br>
 the parent process when nbdkit starts up.  This is also a secure<br>
 method to supply a password.<br>
<br>
+=item B<pool-max=>N<br>
+<br>
+To improve performance, for read-only connections (see I<-r> option)<br>
+the plugin will open a pool of VixDiskLibHandle disk handles.  You can<br>
+use this option to control the maximum size of the pool.  The default<br>
+is 8.  To disable this feature, set it to 0 or 1.<br>
+<br>
 =item B<port=>PORT<br>
<br>
 The port on the VCenter/ESXi host.  Defaults to 443.<br>
diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c<br>
index 5926e181..173d5031 100644<br>
--- a/plugins/vddk/vddk.c<br>
+++ b/plugins/vddk/vddk.c<br>
@@ -52,6 +52,7 @@<br>
 #include "isaligned.h"<br>
 #include "minmax.h"<br>
 #include "rounding.h"<br>
+#include "vector.h"<br>
<br>
 #include "vddk.h"<br>
 #include "vddk-structs.h"<br>
@@ -85,6 +86,7 @@ char *libdir;                              /* libdir */<br>
 static uint16_t nfc_host_port;             /* nfchostport */<br>
 char *password;                            /* password */<br>
 static uint16_t port;                      /* port */<br>
+static unsigned pool_max = 8;              /* pool-max */<br>
 static const char *server_name;            /* server */<br>
 static bool single_link;                   /* single-link */<br>
 static const char *snapshot_moref;         /* snapshot */<br>
@@ -233,6 +235,12 @@ vddk_config (const char *key, const char *value)<br>
     if (nbdkit_parse_uint16_t ("port", value, &port) == -1)<br>
       return -1;<br>
   }<br>
+  else if (strcmp (key, "pool-max") == 0) {<br>
+    if (nbdkit_parse_unsigned ("pool-max", value, &pool_max) == -1)<br>
+      return -1;<br>
+    if (pool_max < 1)<br>
+      pool_max = 1;<br>
+  }<br>
   else if (strcmp (key, "reexeced_") == 0) {<br>
     /* Special name because it is only for internal use. */<br>
     reexeced = (char *)value;<br>
@@ -482,20 +490,37 @@ vddk_dump_plugin (void)<br>
  * <a href="https://code.vmware.com/docs/11750/virtual-disk-development-kit-programming-guide/GUID-6BE903E8-DC70-46D9-98E4-E34A2002C2AD.html" rel="noreferrer noreferrer" target="_blank">https://code.vmware.com/docs/11750/virtual-disk-development-kit-programming-guide/GUID-6BE903E8-DC70-46D9-98E4-E34A2002C2AD.html</a><br>
  *<br>
  * Before nbdkit 1.22 we used SERIALIZE_ALL_REQUESTS.  Since nbdkit<br>
- * 1.22 we changed this to SERIALIZE_REQUESTS and added a mutex around<br>
- * calls to VixDiskLib_Open and VixDiskLib_Close.  This is not quite<br>
- * within the letter of the rules, but is within the spirit.<br>
+ * 1.22 we changed this to PARALLEL, added a mutex around calls to<br>
+ * VixDiskLib_Open and VixDiskLib_Close, and use a pool of disk<br>
+ * handles.  This is not quite within the letter of the rules, but is<br>
+ * within the spirit.<br>
  */<br>
-#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS<br>
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL<br>
<br>
 /* Lock protecting open/close calls - see above. */<br>
 static pthread_mutex_t open_close_lock = PTHREAD_MUTEX_INITIALIZER;<br>
<br>
-/* The per-connection handle. */<br>
+struct handle;<br>
 struct vddk_handle {<br>
+  VixDiskLibHandle vddk_handle;<br>
+  bool in_use;<br>
+  struct handle *h;<br>
+};<br>
+DEFINE_VECTOR_TYPE(vddk_handles, struct vddk_handle)<br>
+<br>
+/* The per-connection handle. */<br>
+struct handle {<br>
   VixDiskLibConnectParams *params; /* connection parameters */<br>
   VixDiskLibConnection connection; /* connection */<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Given the poor results, I suspect that that handles created using same connection share a lock. This also makes sense if connection abstract a blocking socket.</div><div dir="auto"><br></div><div dir="auto">With multiple nbd connections we got 60% improvement. I expect to see similar results if we use multiple connection+handle pairs.</div><div dir="auto"><br></div><div dir="auto">Can you try to move the connection into the vddk_handle struct?</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
-  VixDiskLibHandle handle;         /* disk handle */<br>
+  int readonly;                    /* readonly flag for this connection */<br>
+  uint32_t flags;                  /* open flags */<br>
+<br>
+  /* Pool of VDDK disk handles.  Do not access this directly, use<br>
+   * GET_VDDK_HANDLE_FOR_CURRENT_SCOPE macro to get a free handle.<br>
+   */<br>
+  pthread_mutex_t vddk_handles_lock;<br>
+  pthread_cond_t vddk_handles_cond;<br>
+  vddk_handles vddk_handles;<br>
 };<br>
<br>
 static inline VixDiskLibConnectParams *<br>
@@ -531,17 +556,28 @@ free_connect_params (VixDiskLibConnectParams *params)<br>
 static void *<br>
 vddk_open (int readonly)<br>
 {<br>
-  ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&open_close_lock);<br>
-  struct vddk_handle *h;<br>
+  struct handle *h;<br>
   VixError err;<br>
-  uint32_t flags;<br>
<br>
-  h = malloc (sizeof *h);<br>
+  h = calloc (1, sizeof *h);<br>
   if (h == NULL) {<br>
-    nbdkit_error ("malloc: %m");<br>
+    nbdkit_error ("calloc: %m");<br>
     return NULL;<br>
   }<br>
<br>
+  h->readonly = readonly;<br>
+  pthread_mutex_init (&h->vddk_handles_lock, NULL);<br>
+  pthread_cond_init (&h->vddk_handles_cond, NULL);<br>
+<br>
+  /* We have to reserve this vector to ensure that it is not<br>
+   * reallocated, as that would make the pointer returned by<br>
+   * get_vddk_handle in another thread invalid.<br>
+   */<br>
+  if (vddk_handles_reserve (&h->vddk_handles, pool_max) == -1) {<br>
+    nbdkit_error ("realloc: %m");<br>
+    goto err0;<br>
+  }<br>
+<br>
   h->params = allocate_connect_params ();<br>
   if (h->params == NULL) {<br>
     nbdkit_error ("allocate VixDiskLibConnectParams: %m");<br>
@@ -589,49 +625,120 @@ vddk_open (int readonly)<br>
     goto err1;<br>
   }<br>
<br>
-  flags = 0;<br>
+  h->flags = 0;<br>
   if (readonly)<br>
-    flags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY;<br>
+    h->flags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY;<br>
   if (single_link)<br>
-    flags |= VIXDISKLIB_FLAG_OPEN_SINGLE_LINK;<br>
+    h->flags |= VIXDISKLIB_FLAG_OPEN_SINGLE_LINK;<br>
   if (unbuffered)<br>
-    flags |= VIXDISKLIB_FLAG_OPEN_UNBUFFERED;<br>
-<br>
-  DEBUG_CALL ("VixDiskLib_Open",<br>
-              "connection, %s, %d, &handle", filename, flags);<br>
-  err = VixDiskLib_Open (h->connection, filename, flags, &h->handle);<br>
-  if (err != VIX_OK) {<br>
-    VDDK_ERROR (err, "VixDiskLib_Open: %s", filename);<br>
-    goto err2;<br>
-  }<br>
-<br>
-  nbdkit_debug ("transport mode: %s",<br>
-                VixDiskLib_GetTransportMode (h->handle));<br>
+    h->flags |= VIXDISKLIB_FLAG_OPEN_UNBUFFERED;<br>
<br>
   return h;<br>
<br>
- err2:<br>
-  DEBUG_CALL ("VixDiskLib_Disconnect", "connection");<br>
-  VixDiskLib_Disconnect (h->connection);<br>
  err1:<br>
   free_connect_params (h->params);<br>
  err0:<br>
+  free (h->vddk_handles.ptr);<br>
+  pthread_cond_destroy (&h->vddk_handles_cond);<br>
+  pthread_mutex_destroy (&h->vddk_handles_lock);<br>
   free (h);<br>
   return NULL;<br>
 }<br>
<br>
+/* Get a VDDK handle on demand. */<br>
+static VixDiskLibHandle<br>
+open_vddk_handle (struct handle *h)<br>
+{<br>
+  ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&open_close_lock);<br>
+  VixDiskLibHandle vddk_handle;<br>
+  VixError err;<br>
+<br>
+  DEBUG_CALL ("VixDiskLib_Open",<br>
+              "connection, %s, %d, &handle", filename, h->flags);<br>
+  err = VixDiskLib_Open (h->connection, filename, h->flags, &vddk_handle);<br>
+  if (err != VIX_OK) {<br>
+    VDDK_ERROR (err, "VixDiskLib_Open: %s", filename);<br>
+    return NULL;<br>
+  }<br>
+<br>
+  nbdkit_debug ("transport mode: %s",<br>
+                VixDiskLib_GetTransportMode (vddk_handle));<br>
+  return vddk_handle;<br>
+}<br>
+<br>
+static struct vddk_handle *<br>
+get_vddk_handle (struct handle *h)<br>
+{<br>
+  const unsigned max = h->readonly ? pool_max : 1;<br>
+  ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&h->vddk_handles_lock);<br>
+  VixDiskLibHandle vddk_handle;<br>
+  size_t i;<br>
+<br>
+ again:<br>
+  /* See if there's a handle in the pool which is not in use. */<br>
+  for (i = 0; i < h->vddk_handles.size; ++i) {<br>
+    if (!h->vddk_handles.ptr[i].in_use) {<br>
+      h->vddk_handles.ptr[i].in_use = true;<br>
+      return &h->vddk_handles.ptr[i];<br>
+    }<br>
+  }<br>
+<br>
+  /* If the pool is too big we have to wait for another thread to<br>
+   * finish using its handle and try again.<br>
+   */<br>
+  if (h->vddk_handles.size >= max) {<br>
+    pthread_cond_wait (&h->vddk_handles_cond, &h->vddk_handles_lock);<br>
+    goto again;<br>
+  }<br>
+<br>
+  /* Open another handle and add it to the pool.  Note that<br>
+   * vddk_handles_append cannot fail because we reserved space in<br>
+   * vddk_open.<br>
+   */<br>
+  vddk_handle = open_vddk_handle (h);<br>
+  if (vddk_handle == NULL)<br>
+    return NULL;<br>
+  vddk_handles_append (&h->vddk_handles,<br>
+                       (struct vddk_handle){ vddk_handle, true, h });<br>
+  return &h->vddk_handles.ptr[h->vddk_handles.size-1];<br>
+}<br>
+<br>
+static void<br>
+put_vddk_handle (struct vddk_handle **p)<br>
+{<br>
+  if (*p) {<br>
+    ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&(*p)->h->vddk_handles_lock);<br>
+    assert ((*p)->in_use);<br>
+    (*p)->in_use = false;<br>
+    pthread_cond_signal (&(*p)->h->vddk_handles_cond);<br>
+  }<br>
+}<br>
+<br>
+/* Wrap get/put_vddk_handle in nicer macro. */<br>
+#define GET_VDDK_HANDLE_FOR_CURRENT_SCOPE(h, name)      \<br>
+  __attribute__((cleanup (put_vddk_handle)))            \<br>
+  struct vddk_handle *name##_h = get_vddk_handle (h);   \<br>
+  if (name##_h == NULL) return -1;                      \<br>
+  VixDiskLibHandle name = name##_h->vddk_handle<br>
+<br>
 /* Free up the per-connection handle. */<br>
 static void<br>
 vddk_close (void *handle)<br>
 {<br>
   ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&open_close_lock);<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  size_t i;<br>
<br>
-  DEBUG_CALL ("VixDiskLib_Close", "handle");<br>
-  VixDiskLib_Close (h->handle);<br>
+  for (i = 0; i < h->vddk_handles.size; ++i) {<br>
+    DEBUG_CALL ("VixDiskLib_Close", "handle");<br>
+    VixDiskLib_Close (h->vddk_handles.ptr[i].vddk_handle);<br>
+  }<br>
+  free (h->vddk_handles.ptr);<br>
   DEBUG_CALL ("VixDiskLib_Disconnect", "connection");<br>
   VixDiskLib_Disconnect (h->connection);<br>
   free_connect_params (h->params);<br>
+  pthread_cond_destroy (&h->vddk_handles_cond);<br>
+  pthread_mutex_destroy (&h->vddk_handles_lock);<br>
   free (h);<br>
 }<br>
<br>
@@ -639,13 +746,14 @@ vddk_close (void *handle)<br>
 static int64_t<br>
 vddk_get_size (void *handle)<br>
 {<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  GET_VDDK_HANDLE_FOR_CURRENT_SCOPE (h, vddk_handle);<br>
   VixDiskLibInfo *info;<br>
   VixError err;<br>
   uint64_t size;<br>
<br>
   DEBUG_CALL ("VixDiskLib_GetInfo", "handle, &info");<br>
-  err = VixDiskLib_GetInfo (h->handle, &info);<br>
+  err = VixDiskLib_GetInfo (vddk_handle, &info);<br>
   if (err != VIX_OK) {<br>
     VDDK_ERROR (err, "VixDiskLib_GetInfo");<br>
     return -1;<br>
@@ -687,7 +795,8 @@ static int<br>
 vddk_pread (void *handle, void *buf, uint32_t count, uint64_t offset,<br>
             uint32_t flags)<br>
 {<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  GET_VDDK_HANDLE_FOR_CURRENT_SCOPE (h, vddk_handle);<br>
   VixError err;<br>
<br>
   /* Align to sectors. */<br>
@@ -706,7 +815,7 @@ vddk_pread (void *handle, void *buf, uint32_t count, uint64_t offset,<br>
                        "handle, %" PRIu64 " sectors, "<br>
                        "%" PRIu32 " sectors, buffer",<br>
                        offset, count);<br>
-  err = VixDiskLib_Read (h->handle, offset, count, buf);<br>
+  err = VixDiskLib_Read (vddk_handle, offset, count, buf);<br>
   if (err != VIX_OK) {<br>
     VDDK_ERROR (err, "VixDiskLib_Read");<br>
     return -1;<br>
@@ -726,7 +835,8 @@ vddk_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,<br>
              uint32_t flags)<br>
 {<br>
   const bool fua = flags & NBDKIT_FLAG_FUA;<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  GET_VDDK_HANDLE_FOR_CURRENT_SCOPE (h, vddk_handle);<br>
   VixError err;<br>
<br>
   /* Align to sectors. */<br>
@@ -745,7 +855,7 @@ vddk_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,<br>
                        "handle, %" PRIu64 " sectors, "<br>
                        "%" PRIu32 " sectors, buffer",<br>
                        offset, count);<br>
-  err = VixDiskLib_Write (h->handle, offset, count, buf);<br>
+  err = VixDiskLib_Write (vddk_handle, offset, count, buf);<br>
   if (err != VIX_OK) {<br>
     VDDK_ERROR (err, "VixDiskLib_Write");<br>
     return -1;<br>
@@ -761,7 +871,8 @@ vddk_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,<br>
 static int<br>
 vddk_flush (void *handle, uint32_t flags)<br>
 {<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  GET_VDDK_HANDLE_FOR_CURRENT_SCOPE (h, vddk_handle);<br>
   VixError err;<br>
<br>
   /* The Flush call was not available in VDDK < 6.0 so this is simply<br>
@@ -771,7 +882,7 @@ vddk_flush (void *handle, uint32_t flags)<br>
     return 0;<br>
<br>
   DEBUG_CALL ("VixDiskLib_Flush", "handle");<br>
-  err = VixDiskLib_Flush (h->handle);<br>
+  err = VixDiskLib_Flush (vddk_handle);<br>
   if (err != VIX_OK) {<br>
     VDDK_ERROR (err, "VixDiskLib_Flush");<br>
     return -1;<br>
@@ -783,7 +894,8 @@ vddk_flush (void *handle, uint32_t flags)<br>
 static int<br>
 vddk_can_extents (void *handle)<br>
 {<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  GET_VDDK_HANDLE_FOR_CURRENT_SCOPE (h, vddk_handle);<br>
   VixError err;<br>
   VixDiskLibBlockList *block_list;<br>
<br>
@@ -808,7 +920,7 @@ vddk_can_extents (void *handle)<br>
   DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",<br>
               "handle, 0, %d sectors, %d sectors",<br>
               VIXDISKLIB_MIN_CHUNK_SIZE, VIXDISKLIB_MIN_CHUNK_SIZE);<br>
-  err = VixDiskLib_QueryAllocatedBlocks (h->handle,<br>
+  err = VixDiskLib_QueryAllocatedBlocks (vddk_handle,<br>
                                          0, VIXDISKLIB_MIN_CHUNK_SIZE,<br>
                                          VIXDISKLIB_MIN_CHUNK_SIZE,<br>
                                          &block_list);<br>
@@ -864,7 +976,8 @@ static int<br>
 vddk_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,<br>
               struct nbdkit_extents *extents)<br>
 {<br>
-  struct vddk_handle *h = handle;<br>
+  struct handle *h = handle;<br>
+  GET_VDDK_HANDLE_FOR_CURRENT_SCOPE (h, vddk_handle);<br>
   bool req_one = flags & NBDKIT_FLAG_REQ_ONE;<br>
   uint64_t position, end, start_sector;<br>
<br>
@@ -896,7 +1009,7 @@ vddk_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,<br>
                 "handle, %" PRIu64 " sectors, %" PRIu64 " sectors, "<br>
                 "%d sectors",<br>
                 start_sector, nr_sectors, VIXDISKLIB_MIN_CHUNK_SIZE);<br>
-    err = VixDiskLib_QueryAllocatedBlocks (h->handle,<br>
+    err = VixDiskLib_QueryAllocatedBlocks (vddk_handle,<br>
                                            start_sector, nr_sectors,<br>
                                            VIXDISKLIB_MIN_CHUNK_SIZE,<br>
                                            &block_list);<br>
-- <br>
2.27.0<br>
<br>
</blockquote></div></div></div>