Make it possible for plugins to provide an alternative behavior for
NBD_CMD_CACHE rather than just discarding the buffer of .pread.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
docs/nbdkit-plugin.pod | 52 +++++++++++++++++++++++++++++++++++++++++
include/nbdkit-plugin.h | 2 ++
server/plugins.c | 17 +++++++++-----
3 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 318be4c..0657151 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -620,6 +620,31 @@ with an error message and return C<-1>.
This callback is not required. If omitted, then we return false.
+=head2 C<.can_cache>
+
+ int can_cache (void *handle);
+
+This is called during the option negotiation phase to find out if the
+plugin supports a cache operation. The nature of the caching is
+unspecified (including whether there are limits on how much can be
+cached at once, and whether writes to a cached region have
+write-through or write-back semantics), but the command exists to let
+clients issue a hint to the server that they will be accessing that
+region of the export.
+
+If there is an error, C<.can_cache> should call C<nbdkit_error> with
+an error message and return C<-1>. Since the NBD protocol has
+reserved the right to add future flags to mandate specific caching
+capabilities, this function should only return C<1> on success (in the
+future, other positive results may be treated as a bitmask of
+supported flags).
+
+This callback is not required. If omitted, then we return true iff a
+C<.cache> callback has been defined. Note that nbdkit always
+advertises caching capabilities to the client regardless of this
+callback; if this callback returns false, nbdkit handles a client
+cache request by calling C<.pread> and ignoring the resulting data.
+
=head2 C<.pread>
int pread (void *handle, void *buf, uint32_t count, uint64_t offset,
@@ -814,6 +839,33 @@ C<nbdkit_extent_add> returns C<0> on success or
C<-1> on failure. On
failure C<nbdkit_error> and/or C<nbdkit_set_error> has already been
called. C<errno> will be set to a suitable value.
+=head2 C<.cache>
+
+ int cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
+
+During the data serving phase, this callback is used to give the
+plugin a hint that the client intends to make further accesses to the
+given region of the export. The nature of caching is not specified
+further by the NBD specification (for example, a server may place
+limits on how much may be cached at once, and there is no way to
+control if writes to a cached area have write-through or write-back
+semantics). In fact, the cache command can always fail and still be
+compliant, and success might not guarantee a performance gain. If
+this callback is omitted, nbdkit defaults to providing cache semantics
+obtained by calling C<.pread> over the same region and ignoring the
+results.
+
+This function will not be called if C<.can_cache> returned false. The
+parameter C<flags> exists in case of future NBD protocol extensions;
+at this time, it will be 0 on input. A plugin must fail this function
+if C<flags> includes an unrecognized flag, as that may indicate a
+requirement that the plugin comply must with a specific caching
+semantic.
+
+If there is an error, C<.cache> should call C<nbdkit_error> with an
+error message, and C<nbdkit_set_error> to record an appropriate error
+(unless C<errno> is sufficient), then return C<-1>.
+
=head1 THREADS
Each nbdkit plugin must declare its thread safety model by defining
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index 54b4ce2..e9b1808 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -128,6 +128,8 @@ struct nbdkit_plugin {
int (*can_extents) (void *handle);
int (*extents) (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
struct nbdkit_extents *extents);
+ int (*can_cache) (void *handle);
+ int (*cache) (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
};
extern void nbdkit_set_error (int err);
diff --git a/server/plugins.c b/server/plugins.c
index cabf543..947fe79 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -201,6 +201,8 @@ plugin_dump_fields (struct backend *b)
HAS (can_multi_conn);
HAS (can_extents);
HAS (extents);
+ HAS (can_cache);
+ HAS (cache);
#undef HAS
/* Custom fields. */
@@ -451,12 +453,16 @@ plugin_can_multi_conn (struct backend *b, struct connection *conn)
static int
plugin_can_cache (struct backend *b, struct connection *conn)
{
+ struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+
assert (connection_get_handle (conn, 0));
debug ("can_cache");
- /* FIXME: return default based on plugin->cache */
- return 0;
+ if (p->plugin.can_cache)
+ return p->plugin.can_cache (connection_get_handle (conn, 0));
+ else
+ return p->plugin.cache != NULL;
}
/* Plugins and filters can call this to set the true errno, in cases
@@ -710,16 +716,15 @@ plugin_cache (struct backend *b, struct connection *conn,
int *err)
{
struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
- int r = -1;
+ int r;
assert (connection_get_handle (conn, 0));
assert (!flags);
+ assert (p->plugin.cache);
debug ("cache count=%" PRIu32 " offset=%" PRIu64, count, offset);
- /* FIXME: assert plugin->cache and call it */
- assert (false);
-
+ r = p->plugin.cache (connection_get_handle (conn, 0), count, offset, flags);
if (r == -1)
*err = get_error (p);
return r;
--
2.20.1