<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>