[Libguestfs] [nbdkit PATCH v2 07/24] sh: Implement .cache script callback

Eric Blake eblake at redhat.com
Thu May 16 03:57:57 UTC 2019


It's easy to expose new callbacks to sh plugins, by borrowing
tri-state code from can_fua. It's possible that nbdkit emulate will
actually work well (in our example.sh script, the kernel caching a
pread from one dd invocation may indeed speed up the next access), but
for the sake of the example, I demonstrated advertising a no-op
handler.

The shell plugin, coupled with Rich's work on libnbd as a client-side
library for actually exercising calls to NBD_CMD_CACHE, will be a
useful way to prove that cache commands even make it through the
stack. (Remember, qemu 3.0 was released with a fatally flawed
NBD_CMD_CACHE server implementation, because there were no open source
clients at the time that could actually send the command to test the
server with).

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 plugins/sh/nbdkit-sh-plugin.pod | 27 ++++++++---
 plugins/sh/sh.c                 | 81 +++++++++++++++++++++++++++++++++
 plugins/sh/example.sh           |  7 +++
 3 files changed, 109 insertions(+), 6 deletions(-)

diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod
index 8af88b4..39b99a2 100644
--- a/plugins/sh/nbdkit-sh-plugin.pod
+++ b/plugins/sh/nbdkit-sh-plugin.pod
@@ -220,7 +220,7 @@ This method is required.

 Unlike in other languages, you B<must> provide the C<can_*> methods
 otherwise they are assumed to all return false and your C<pwrite>,
-C<flush>, C<trim>, C<zero> and C<extents> methods will never be
+C<flush>, C<trim>, C<zero>, and C<extents> methods will never be
 called.  The reason for this is obscure: In other languages we can
 detect if (eg) a C<pwrite> method is defined and synthesize an
 appropriate response if no actual C<can_write> method is defined.
@@ -243,13 +243,20 @@ The script should exit with code C<0> for true or code C<3> for false.

 =item C<can_fua>

+=item C<can_cache>
+
  /path/to/script can_fua <handle>
+ /path/to/script can_cache <handle>

-This controls Forced Unit Access (FUA) behaviour of the core server.
+These control Forced Unit Access (FUA) and caching behaviour of the
+core server.

-Unlike the other C<can_*> callbacks, this one is I<not> a boolean.  It
-must print either "none", "emulate" or "native" to stdout.  The
-meaning of these is described in L<nbdkit-plugin(3)>.
+Unlike the other C<can_*> callbacks, these two are I<not> a boolean.
+They must print either "none", "emulate" or "native" to stdout.  The
+meaning of these is described in L<nbdkit-plugin(3)>.  Furthermore,
+you B<must> provide a C<can_cache> method if you desire the C<cache>
+callback to be utilized, similar to the reasoning behind requiring
+C<can_write> to utilize C<pwrite>.

 =item C<can_multi_conn>

@@ -334,6 +341,14 @@ Unlike in other languages, if you provide an C<extents> method you
 B<must> also provide a C<can_extents> method which exits with code
 C<0> (true).

+=item C<cache>
+
+ /path/to/script cache <handle> <count> <offset>
+
+Unlike in other languages, if you provide a C<cache> method you
+B<must> also provide a C<can_cache> method which prints "native" and
+exits with code C<0> (true).
+
 =back

 =head2 Missing callbacks
@@ -365,4 +380,4 @@ Richard W.M. Jones

 =head1 COPYRIGHT

-Copyright (C) 2018 Red Hat Inc.
+Copyright (C) 2018-2019 Red Hat Inc.
diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c
index a5beb57..862be21 100644
--- a/plugins/sh/sh.c
+++ b/plugins/sh/sh.c
@@ -578,6 +578,53 @@ sh_can_multi_conn (void *handle)
   return boolean_method (handle, "can_multi_conn");
 }

+/* Not a boolean method, the method prints "none", "emulate" or "native". */
+static int
+sh_can_cache (void *handle)
+{
+  char *h = handle;
+  const char *args[] = { script, "can_cache", h, NULL };
+  CLEANUP_FREE char *s = NULL;
+  size_t slen;
+  int r;
+
+  switch (call_read (&s, &slen, args)) {
+  case OK:
+    if (slen > 0 && s[slen-1] == '\n')
+      s[slen-1] = '\0';
+    if (strcasecmp (s, "none") == 0)
+      r = NBDKIT_CACHE_NONE;
+    else if (strcasecmp (s, "emulate") == 0)
+      r = NBDKIT_CACHE_EMULATE;
+    else if (strcasecmp (s, "native") == 0)
+      r = NBDKIT_CACHE_NATIVE;
+    else {
+      nbdkit_error ("%s: could not parse output from can_cache method: %s",
+                    script, s);
+      r = -1;
+    }
+    return r;
+
+  case MISSING:
+    /* NBDKIT_CACHE_EMULATE means that nbdkit will call .pread.  However
+     * we cannot know if that fallback would be efficient, so the safest
+     * default is to return NBDKIT_CACHE_NONE.
+     */
+    return NBDKIT_CACHE_NONE;
+
+  case ERROR:
+    return -1;
+
+  case RET_FALSE:
+    nbdkit_error ("%s: %s method returned unexpected code (3/false)",
+                  script, "can_cache");
+    errno = EIO;
+    return -1;
+
+  default: abort ();
+  }
+}
+
 static int
 sh_flush (void *handle, uint32_t flags)
 {
@@ -782,6 +829,38 @@ sh_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
   }
 }

+static int
+sh_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
+{
+  char *h = handle;
+  char cbuf[32], obuf[32];
+  const char *args[] = { script, "cache", h, cbuf, obuf, NULL };
+
+  snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
+  snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
+  assert (!flags);
+
+  switch (call (args)) {
+  case OK:
+    return 0;
+
+  case MISSING:
+    /* Ignore lack of cache callback. */
+    return 0;
+
+  case ERROR:
+    return -1;
+
+  case RET_FALSE:
+    nbdkit_error ("%s: %s method returned unexpected code (3/false)",
+                  script, "cache");
+    errno = EIO;
+    return -1;
+
+  default: abort ();
+  }
+}
+
 #define sh_config_help \
   "script=<FILENAME>     (required) The shell script to run.\n" \
   "[other arguments may be used by the plugin that you load]"
@@ -812,6 +891,7 @@ static struct nbdkit_plugin plugin = {
   .can_extents       = sh_can_extents,
   .can_fua           = sh_can_fua,
   .can_multi_conn    = sh_can_multi_conn,
+  .can_cache         = sh_can_cache,

   .pread             = sh_pread,
   .pwrite            = sh_pwrite,
@@ -819,6 +899,7 @@ static struct nbdkit_plugin plugin = {
   .trim              = sh_trim,
   .zero              = sh_zero,
   .extents           = sh_extents,
+  .cache             = sh_cache,

   .errno_is_preserved = 1,
 };
diff --git a/plugins/sh/example.sh b/plugins/sh/example.sh
index 63228c8..60c46ce 100755
--- a/plugins/sh/example.sh
+++ b/plugins/sh/example.sh
@@ -133,6 +133,13 @@ case "$1" in
         fallocate --help >/dev/null 2>&1 || exit 3
         ;;

+    can_cache)
+	# Caching is not advertised to the client unless can_cache prints
+	# a tri-state value.  Here, we choose for caching to be a no-op,
+	# by omitting counterpart handling for 'cache'.
+	echo native
+	;;
+
     *)
         # Unknown methods must exit with code 2.
         exit 2
-- 
2.20.1




More information about the Libguestfs mailing list