[Libguestfs] [PATCH nbdkit] New plugin: blkio

Richard W.M. Jones rjones at redhat.com
Wed Jan 4 18:14:35 UTC 2023


Using libblkio (https://libblkio.gitlab.io/) this implements a plugin
for reading and writing various disk sources mainly used in
virtualization and other high performance cases.  These include: NVMe,
vhost-user, vhost-vdpa and VFIO PCI.

Note the current implementation uses the simplest libblkio API mode
(blocking) and so it is not suitable for high performance access.
---
 plugins/blkio/nbdkit-blkio-plugin.pod | 117 +++++++++++
 configure.ac                          |  20 ++
 plugins/blkio/Makefile.am             |  80 ++++++++
 plugins/blkio/blkio.c                 | 270 ++++++++++++++++++++++++++
 README.md                             |   4 +
 TODO                                  |   7 +
 6 files changed, 498 insertions(+)

diff --git a/plugins/blkio/nbdkit-blkio-plugin.pod b/plugins/blkio/nbdkit-blkio-plugin.pod
new file mode 100644
index 000000000..c76784687
--- /dev/null
+++ b/plugins/blkio/nbdkit-blkio-plugin.pod
@@ -0,0 +1,117 @@
+=head1 NAME
+
+nbdkit-blkio-plugin - libblkio plugin for NVMe, vhost-user, vDPA, VFIO
+
+=head1 SYNOPSIS
+
+ nbdkit blkio [driver=]DRIVER [path=FILENAME] [num-queues=N] ...
+
+=head1 DESCRIPTION
+
+nbdkit-blkio-plugin is an L<nbdkit(1)> plugin for using
+L<libblkio|https://libblkio.gitlab.io/> to access various disk sources
+used for high performance applications and virtualization.  These
+include: NVMe, vhost-user, vDPA and VFIO.
+
+The first parameter after the plugin name should be the L<libblkio
+driver|https://libblkio.gitlab.io/libblkio/blkio.html#drivers>.  For
+example:
+
+ nbdkit blkio virtio-blk-vhost-user path=vhost.sock
+
+=head2 Driver: C<nvme-io_uring>
+
+ nbdkit blkio nvme-io_uring path=/dev/ng0n1
+
+Connect to an NVMe device, issuing commands through Linux io_uring
+(requires Linux E<ge> 5.19).
+
+=head2 Driver: C<virtio-blk-vfio-pci>
+
+ nbdkit blkio virtio-blk-vfio-pci path=/sys/bus/pci/devices/0000:00:01.0
+
+Connect to a PCI device which implements virtio-blk using VFIO.  The
+path is the path to the device's sysfs directory (see L<lspci(8)>).
+
+=head2 Driver: C<virtio-blk-vhost-user>
+
+ nbdkit blkio virtio-blk-vhost-user path=vhost.sock
+
+Connect to a vhost-user-blk device, such as one exported by
+L<qemu-storage-daemon(1)>.  The path is the vhost-user Unix domain
+socket.
+
+=head2 Driver: C<virtio-blk-vhost-vdpa>
+
+ nbdkit blkio virtio-blk-vhost-vdpa path=chardev
+
+Connect to a vDPA device which might be implemented in software
+(eg. VDUSE) or hardware.  The path is the vhost-vdpa character device.
+
+=head2 Driver: C<io_uring>
+
+ nbdkit blkio io_uring path=FILENAME
+
+You can use this driver to access local files and block devices
+through the libblkio C<io_uring> driver, but it is usually better and
+easier to use L<nbdkit-file-plugin(1)>.
+
+=head1 PARAMETERS
+
+=over 4
+
+=item [B<driver=>]DRIVER
+
+The name of the libblkio driver to use.
+
+This parameter is required.
+
+C<driver=> is a magic config key and may be omitted in most cases.
+See L<nbdkit(1)/Magic parameters>.
+
+=item PROPERTYB<=>VALUE
+
+Properties such as C<path>, C<num-entries> etc are translated to
+libblkio properties.  Consult the L<libblkio
+documentation|https://libblkio.gitlab.io/libblkio/blkio.html> for a
+complete list.
+
+=item B<get=>PROPERTY
+
+Get (print) the value of a property after connecting.  The property is
+fetched and printed in nbdkit debug output, so you will need to use
+the I<--verbose> flag.  This is useful for debugging.
+
+=back
+
+=head1 FILES
+
+=over 4
+
+=item F<$plugindir/nbdkit-blkio-plugin.so>
+
+The plugin.
+
+Use C<nbdkit --dump-config> to find the location of C<$plugindir>.
+
+=back
+
+=head1 VERSION
+
+C<nbdkit-blkio-plugin> first appeared in nbdkit 1.34.
+
+=head1 SEE ALSO
+
+L<nbdkit-file-plugin(1)>,
+L<lspci(8)>,
+L<qemu-storage-daemon(1)>,
+L<https://libblkio.gitlab.io>,
+L<https://libblkio.gitlab.io/libblkio/blkio.html>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2014-2023 Red Hat Inc.
diff --git a/configure.ac b/configure.ac
index e03374b12..6afeac38e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,7 @@ lang_plugins="\
         tcl \
         "
 non_lang_plugins="\
+        blkio \
         cdi \
         curl \
         data \
@@ -1094,6 +1095,23 @@ AS_IF([test "x$enable_golang" != "xno"],[
 ],[GOLANG=no])
 AM_CONDITIONAL([HAVE_GOLANG],[test "x$GOLANG" != "xno"])
 
+dnl Check for libblkio (only if you want to compile the blkio plugin).
+AC_ARG_WITH([libblkio],
+    [AS_HELP_STRING([--without-libblkio],
+                    [disable blkio plugin @<:@default=check@:>@])],
+    [],
+    [with_libblkio=check])
+AS_IF([test "$with_libblkio" != "no"],[
+    PKG_CHECK_MODULES([LIBBLKIO], [blkio],[
+        printf "libblkio version is "; $PKG_CONFIG --modversion blkio
+        AC_SUBST([LIBBLKIO_CFLAGS])
+        AC_SUBST([LIBBLKIO_LIBS])
+        AC_DEFINE([HAVE_LIBBLKIO],[1],[libblkio found at compile time.])
+    ],
+    [AC_MSG_WARN([libblkio not found, blkio plugin will be disabled])])
+])
+AM_CONDITIONAL([HAVE_LIBBLKIO],[test "x$LIBBLKIO_LIBS" != "x"])
+
 dnl Check for curl (only if you want to compile the curl plugin).
 AC_ARG_WITH([curl],
     [AS_HELP_STRING([--without-curl],
@@ -1402,6 +1420,7 @@ AC_CONFIG_FILES([Makefile
                  include/Makefile
                  include/nbdkit-version.h
                  plugins/Makefile
+                 plugins/blkio/Makefile
                  plugins/cc/Makefile
                  plugins/cdi/Makefile
                  plugins/curl/Makefile
@@ -1533,6 +1552,7 @@ feature "TLS"                 test "x$GNUTLS_LIBS" != "x"
 echo
 echo "Optional plugins:"
 echo
+feature "blkio"               test "x$HAVE_LIBBLKIO_TRUE" = "x"
 feature "curl"                test "x$HAVE_CURL_TRUE" = "x"
 feature "example4"            test "x$HAVE_PERL_TRUE" = "x"
 feature "floppy"              test "x$HAVE_ICONV_TRUE" = "x"
diff --git a/plugins/blkio/Makefile.am b/plugins/blkio/Makefile.am
new file mode 100644
index 000000000..dbedcc2eb
--- /dev/null
+++ b/plugins/blkio/Makefile.am
@@ -0,0 +1,80 @@
+# nbdkit
+# Copyright (C) 2014-2023 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = nbdkit-blkio-plugin.pod
+
+if HAVE_LIBBLKIO
+
+plugin_LTLIBRARIES = nbdkit-blkio-plugin.la
+
+nbdkit_blkio_plugin_la_SOURCES = \
+	blkio.c \
+	$(top_srcdir)/include/nbdkit-plugin.h \
+	$(NULL)
+
+nbdkit_blkio_plugin_la_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_builddir)/include \
+	-I$(top_srcdir)/common/utils \
+	$(NULL)
+nbdkit_blkio_plugin_la_CFLAGS = \
+	$(WARNINGS_CFLAGS) \
+	$(LIBBLKIO_CFLAGS) \
+	$(NULL)
+nbdkit_blkio_plugin_la_LIBADD = \
+	$(top_builddir)/common/utils/libutils.la \
+	$(IMPORT_LIBRARY_ON_WINDOWS) \
+	$(LIBBLKIO_LIBS) \
+	$(NULL)
+nbdkit_blkio_plugin_la_LDFLAGS = \
+	-module -avoid-version -shared $(NO_UNDEFINED_ON_WINDOWS) \
+	$(NULL)
+if USE_LINKER_SCRIPT
+nbdkit_blkio_plugin_la_LDFLAGS += \
+	-Wl,--version-script=$(top_srcdir)/plugins/plugins.syms
+endif
+
+if HAVE_POD
+
+man_MANS = nbdkit-blkio-plugin.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-blkio-plugin.1: nbdkit-blkio-plugin.pod \
+		$(top_builddir)/podwrapper.pl
+	$(PODWRAPPER) --section=1 --man $@ \
+	    --html $(top_builddir)/html/$@.html \
+	    $<
+
+endif HAVE_POD
+
+endif
diff --git a/plugins/blkio/blkio.c b/plugins/blkio/blkio.c
new file mode 100644
index 000000000..058850f63
--- /dev/null
+++ b/plugins/blkio/blkio.c
@@ -0,0 +1,270 @@
+/* nbdkit
+ * Copyright (C) 2014-2023 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <blkio.h>
+
+#define NBDKIT_API_VERSION 2
+#include <nbdkit-plugin.h>
+
+#include "vector.h"
+#include "const-string-vector.h"
+
+/* libblkio could do parallel, but we would need to reimplement this
+ * plugin to use the libblkio event model.
+ */
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
+
+struct property {
+  const char *name;
+  const char *value;
+};
+DEFINE_VECTOR_TYPE(properties, struct property)
+
+static const char *driver = NULL;               /* driver name - required */
+static properties props = empty_vector;         /* other command line params */
+static const_string_vector gets = empty_vector; /* get= parameters */
+
+static void
+bio_unload (void)
+{
+  properties_reset (&props);
+  const_string_vector_reset (&gets);
+}
+
+/* Called for each key=value passed on the command line. */
+static int
+bio_config (const char *key, const char *value)
+{
+  if (strcmp (key, "driver") == 0) {
+    if (driver != NULL) {
+      nbdkit_error ("'driver' property set more than once");
+      return -1;
+    }
+    driver = value;
+  }
+  else if (strcmp (key, "get") == 0) {
+    if (const_string_vector_append (&gets, value) == -1)
+      return -1;
+  }
+  else if (strcmp (key, "read-only") == 0) {
+    nbdkit_error ("do not set the libblkio \"read-only\" parameter, "
+                  "use the nbdkit -r flag if read-only is required");
+    return -1;
+  }
+  else /* general property */ {
+    struct property prop = { .name = key, .value = value };
+    if (properties_append (&props, prop) == -1)
+      return -1;
+  }
+
+  return 0;
+}
+
+/* Check the user did pass a driver parameter. */
+static int
+bio_config_complete (void)
+{
+  if (driver == NULL) {
+    nbdkit_error ("you must supply the driver=<DRIVER> parameter "
+                  "after the plugin name on the command line");
+    return -1;
+  }
+
+  return 0;
+}
+
+#define bio_config_help \
+  "driver=<DRIVER> (required) Driver name (eg. \"nvme-io_uring\").\n" \
+  "PROPERTY=VALUE             Set arbitrary libblkio property.\n" \
+  "get=PROPERTY               Print property name after connection."
+
+/* XXX Should be possible to query this from libblkio. */
+static bool
+is_preconnect_property (const char *name)
+{
+  return strcmp (name, "can-add-queues") == 0 ||
+    strcmp (name, "driver") == 0 ||
+    strcmp (name, "fd") == 0 ||
+    strcmp (name, "path") == 0 ||
+    strcmp (name, "read-only") == 0;
+}
+
+/* Create the per-connection handle. */
+static void *
+bio_open (int readonly)
+{
+  struct blkio *b;
+  size_t i;
+
+  errno = blkio_create (driver, &b);
+  if (errno != 0) {
+    nbdkit_error ("blkio_create: error opening driver: %s: %m", driver);
+    return NULL;
+  }
+
+  /* Always set the read-only property to a true or false value. */
+  errno = blkio_set_bool (b, "read-only", !!readonly);
+  if (errno != 0) {
+    nbdkit_error ("error setting property: read-only=%s: %m",
+                  readonly ? "true" : "false");
+    blkio_destroy (&b);
+    return NULL;
+  }
+
+  /* Set the pre-connect properties. */
+  for (i = 0; i < props.len; ++i) {
+    const struct property *prop = &props.ptr[i];
+
+    if (is_preconnect_property (prop->name)) {
+      errno = blkio_set_str (b, prop->name, prop->value);
+      if (errno != 0) {
+        nbdkit_error ("error setting property: %s=%s: %m",
+                      prop->name, prop->value);
+        blkio_destroy (&b);
+        return NULL;
+      }
+    }
+  }
+
+  /* Connect. */
+  errno = blkio_connect (b);
+  if (errno != 0) {
+    nbdkit_error ("blkio_connect: failed to connect to device: %m");
+    blkio_destroy (&b);
+    return NULL;
+  }
+
+  /* Set the post-connect properties. */
+  for (i = 0; i < props.len; ++i) {
+    const struct property *prop = &props.ptr[i];
+
+    if (! is_preconnect_property (prop->name)) {
+      errno = blkio_set_str (b, prop->name, prop->value);
+      if (errno != 0) {
+        nbdkit_error ("error setting property: %s=%s: %m",
+                      prop->name, prop->value);
+        blkio_destroy (&b);
+        return NULL;
+      }
+    }
+  }
+
+  /* Start the block device. */
+  errno = blkio_start (b);
+  if (errno != 0) {
+    nbdkit_error ("blkio_start: failed to start device: %m");
+    blkio_destroy (&b);
+    return NULL;
+  }
+
+  /* Print any properties requested on the command line. */
+  for (i = 0; i < gets.len; ++i) {
+    const char *name = gets.ptr[i];
+    char *value = NULL;
+
+    if (blkio_get_str (b, name, &value) == 0)
+      nbdkit_debug ("get %s = %s", name, value);
+    else
+      nbdkit_debug ("could not get property %s: %m", name);
+    free (value);
+  }
+
+  return b;
+}
+
+/* Close the handle. */
+static void
+bio_close (void *handle)
+{
+  struct blkio *b = handle;
+
+  blkio_destroy (&b);
+}
+
+/* Get the device size. */
+static int64_t
+bio_get_size (void *handle)
+{
+  struct blkio *b = handle;
+  uint64_t r;
+
+  errno = blkio_get_uint64 (b, "capacity", &r);
+  if (errno != 0) {
+    nbdkit_error ("error reading device capacity: %m");
+    return -1;
+  }
+
+  return r;
+}
+
+/* Read data from the device. */
+static int
+bio_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
+           uint32_t flags)
+{
+  nbdkit_error ("XXX NOT IMPL XXX");
+  return -1;
+}
+
+/* Write data to the device. */
+static int
+bio_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
+            uint32_t flags)
+{
+  nbdkit_error ("XXX NOT IMPL XXX");
+  return -1;
+}
+
+static struct nbdkit_plugin plugin = {
+  .name               = "blkio",
+  .version            = PACKAGE_VERSION,
+  .unload             = bio_unload,
+  .config             = bio_config,
+  .config_complete    = bio_config_complete,
+  .config_help        = bio_config_help,
+  .magic_config_key   = "driver",
+  .open               = bio_open,
+  .close              = bio_close,
+  .get_size           = bio_get_size,
+  .pread              = bio_pread,
+  .pwrite             = bio_pwrite,
+  .errno_is_preserved = 1,
+};
+
+NBDKIT_REGISTER_PLUGIN(plugin)
diff --git a/README.md b/README.md
index eff94379b..a204ccf39 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,10 @@ For the bittorrent plugin:
 
 * [libtorrent-rasterbar](https://www.libtorrent.org)
 
+For the blkio plugin:
+
+* [libblkio](https://libblkio.gitlab.io/)
+
 For the containerized data importer (CDI) plugin:
 
 * podman
diff --git a/TODO b/TODO
index feeb09289..d4c85df9b 100644
--- a/TODO
+++ b/TODO
@@ -155,6 +155,13 @@ nbdkit-cdi-plugin:
 * Look at using skopeo instead of podman pull
   (https://github.com/containers/skopeo)
 
+nbdkit-blkio-plugin:
+
+* Use event-driven mode instead of blocking mode.  This involves
+  restructuring the plugin so that there is one or more background
+  threads to handle the events, and nbdkit threads issue requests to
+  these threads.  (See how it is done in the VDDK plugin.)
+
 Suggestions for language plugins
 --------------------------------
 
-- 
2.37.3



More information about the Libguestfs mailing list