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