qemu-nbd defaults to permitting only a single NBD client. Of course,
you can run qemu-nbd -t to work around it, but other servers may have
similar restrictions, at which point nbdkit's nbd plugin can provide
the automatic fanout to multiple clients via a single server link.
Note that the shared=true parameter makes the previously-added retry=N
parameter more useful, as it is much easier to run into the scenario
where the server has not created the socket in time for
.config_complete, when compared to a later .open from nbdkit's first
client.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
plugins/nbd/nbdkit-nbd-plugin.pod | 18 ++++-
plugins/nbd/nbd.c | 110 ++++++++++++++++++++----------
2 files changed, 88 insertions(+), 40 deletions(-)
diff --git a/plugins/nbd/nbdkit-nbd-plugin.pod b/plugins/nbd/nbdkit-nbd-plugin.pod
index 4f9efa1..8dd8681 100644
--- a/plugins/nbd/nbdkit-nbd-plugin.pod
+++ b/plugins/nbd/nbdkit-nbd-plugin.pod
@@ -5,7 +5,7 @@ nbdkit-nbd-plugin - nbdkit nbd plugin
=head1 SYNOPSIS
nbdkit nbd { socket=SOCKNAME | hostname=HOST [port=PORT] } [export=NAME]
- [retry=N]
+ [retry=N] [shared=BOOL]
=head1 DESCRIPTION
@@ -56,6 +56,15 @@ empty string).
If the initial connection attempt to the server fails, retry up to
B<N> times more after a second delay between tries (default 0).
+=item B<shared=>BOOL
+
+If this parameter is false (default), the plugin will open a distinct
+connection to the server for each client making a connection to
+nbdkit, and the remote server need not be running at the time nbdkit
+is started. If this parameter is set to true, the plugin will open a
+single connection to the server when nbdkit is first started, and all
+clients to nbdkit will share that single connection.
+
=back
=head1 EXAMPLES
@@ -76,10 +85,13 @@ that the old server exits.
Combine nbdkit's partition filter with qemu-nbd's ability to visit
qcow2 files (nbdkit does not have a native qcow2 plugin), performing
the same task as the deprecated C<qemu-nbd -P 1 -f qcow2
-/path/to/image.qcow2> command:
+/path/to/image.qcow2> command. Allow multiple clients, even though
+C<qemu-nbd> without B<-t> normally quits after the first client, and
+utilize a 5-second retry to give qemu-nbd time to create the socket:
( sock=`mktemp -u`
- nbdkit --exit-with-parent --filter=partition nbd socket=$sock partition=1 &
+ nbdkit --exit-with-parent --filter=partition nbd socket=$sock \
+ shared=1 retry=5 partition=1 &
exec qemu-nbd -k $sock -f qcow2 /path/to/image.qcow2 )
Conversely, expose the contents of export I<foo> from a new style
diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c
index 6665cd9..9389a55 100644
--- a/plugins/nbd/nbd.c
+++ b/plugins/nbd/nbd.c
@@ -57,6 +57,37 @@
#include "byte-swapping.h"
#include "cleanup.h"
+/* The per-transaction details */
+struct transaction {
+ uint64_t cookie;
+ sem_t sem;
+ void *buf;
+ uint64_t offset;
+ uint32_t count;
+ uint32_t err;
+ struct nbdkit_extents *extents;
+ struct transaction *next;
+};
+
+/* The per-connection handle */
+struct handle {
+ /* These fields are read-only once initialized */
+ int fd;
+ int flags;
+ int64_t size;
+ bool structured;
+ bool extents;
+ pthread_t reader;
+
+ /* Prevents concurrent threads from interleaving writes to server */
+ pthread_mutex_t write_lock;
+
+ pthread_mutex_t trans_lock; /* Covers access to all fields below */
+ struct transaction *trans;
+ uint64_t unique;
+ bool dead;
+};
+
/* Connect to server via absolute name of Unix socket */
static char *sockname;
@@ -73,9 +104,18 @@ static const char *export;
/* Number of retries */
static unsigned long retry;
+/* True to share single server connection among all clients */
+static bool shared;
+static struct handle *shared_handle;
+
+static struct handle *nbd_open_handle (int readonly);
+static void nbd_close_handle (struct handle *h);
+
static void
nbd_unload (void)
{
+ if (shared)
+ nbd_close_handle (shared_handle);
free (sockname);
free (servname);
}
@@ -89,6 +129,7 @@ static int
nbd_config (const char *key, const char *value)
{
char *end;
+ int r;
if (strcmp (key, "socket") == 0) {
/* See FILENAMES AND PATHS in nbdkit-plugin(3) */
@@ -111,6 +152,12 @@ nbd_config (const char *key, const char *value)
return -1;
}
}
+ else if (strcmp (key, "shared") == 0) {
+ r = nbdkit_parse_bool (value);
+ if (r == -1)
+ return -1;
+ shared = r;
+ }
else {
nbdkit_error ("unknown parameter '%s'", key);
return -1;
@@ -157,6 +204,9 @@ nbd_config_complete (void)
if (!export)
export = "";
+
+ if (shared && (shared_handle = nbd_open_handle (false)) == NULL)
+ return -1;
return 0;
}
@@ -168,37 +218,6 @@ nbd_config_complete (void)
#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
-/* The per-transaction details */
-struct transaction {
- uint64_t cookie;
- sem_t sem;
- void *buf;
- uint64_t offset;
- uint32_t count;
- uint32_t err;
- struct nbdkit_extents *extents;
- struct transaction *next;
-};
-
-/* The per-connection handle */
-struct handle {
- /* These fields are read-only once initialized */
- int fd;
- int flags;
- int64_t size;
- bool structured;
- bool extents;
- pthread_t reader;
-
- /* Prevents concurrent threads from interleaving writes to server */
- pthread_mutex_t write_lock;
-
- pthread_mutex_t trans_lock; /* Covers access to all fields below */
- struct transaction *trans;
- uint64_t unique;
- bool dead;
-};
-
/* Read an entire buffer, returning 0 on success or -1 with errno set. */
static int
read_full (int fd, void *buf, size_t len)
@@ -960,9 +979,9 @@ nbd_connect_tcp (void)
return fd;
}
-/* Create the per-connection handle. */
-static void *
-nbd_open (int readonly)
+/* Create the shared or per-connection handle. */
+static struct handle *
+nbd_open_handle (int readonly)
{
struct handle *h;
struct old_handshake old;
@@ -1091,12 +1110,19 @@ nbd_open (int readonly)
return NULL;
}
+/* Create the per-connection handle. */
+static void *
+nbd_open (int readonly)
+{
+ if (shared)
+ return shared_handle;
+ return nbd_open_handle (readonly);
+}
+
/* Free up the per-connection handle. */
static void
-nbd_close (void *handle)
+nbd_close_handle (struct handle *h)
{
- struct handle *h = handle;
-
if (!h->dead) {
nbd_request_raw (h, 0, NBD_CMD_DISC, 0, 0, 0, NULL);
shutdown (h->fd, SHUT_WR);
@@ -1109,6 +1135,16 @@ nbd_close (void *handle)
free (h);
}
+/* Free up the per-connection handle. */
+static void
+nbd_close (void *handle)
+{
+ struct handle *h = handle;
+
+ if (!shared)
+ nbd_close_handle (h);
+}
+
/* Get the file size. */
static int64_t
nbd_get_size (void *handle)
--
2.20.1