It's easy to expose new callbacks to sh plugins. I have no idea how
any plugin would actually usefully cache anything in shell (you can't
really store it in a shell variable, since a new shell process is
started for each command); but if nothing else, this allows a user to
implement .can_cache returning true to bypass nbdkit's normal fallback
to .pread.
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(a)redhat.com>
---
plugins/sh/nbdkit-sh-plugin.pod | 12 +++++++++-
plugins/sh/sh.c | 40 +++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod
index 8af88b4..bfd58ab 100644
--- a/plugins/sh/nbdkit-sh-plugin.pod
+++ b/plugins/sh/nbdkit-sh-plugin.pod
@@ -218,9 +218,11 @@ This method is required.
=item C<can_extents>
+=item C<can_cache>
+
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>, C<extents>, and C<cache>
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.
@@ -232,6 +234,7 @@ possible with this plugin.
/path/to/script can_trim <handle>
/path/to/script can_zero <handle>
/path/to/script can_extents <handle>
+ /path/to/script can_cache <handle>
The script should exit with code C<0> for true or code C<3> for false.
@@ -334,6 +337,13 @@ 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 exits with code C<0> (true).
+
=back
=head2 Missing callbacks
diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c
index a5beb57..7aded44 100644
--- a/plugins/sh/sh.c
+++ b/plugins/sh/sh.c
@@ -578,6 +578,12 @@ sh_can_multi_conn (void *handle)
return boolean_method (handle, "can_multi_conn");
}
+static int
+sh_can_cache (void *handle)
+{
+ return boolean_method (handle, "can_cache");
+}
+
static int
sh_flush (void *handle, uint32_t flags)
{
@@ -782,6 +788,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 +850,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 +858,7 @@ static struct nbdkit_plugin plugin = {
.trim = sh_trim,
.zero = sh_zero,
.extents = sh_extents,
+ .cache = sh_cache,
.errno_is_preserved = 1,
};
--
2.20.1