On 01/19/2018 09:23 AM, Richard W.M. Jones wrote:
Filters can be placed in front of plugins to modify their behaviour.
This commit adds the <nbdkit-filter.h> header file, the manual page,
the ‘filterdir’ directory (like ‘plugindir’), the ‘filters/’ source
directory which will contain the actual filters, the ‘--filters’
parameter, and the filters backend logic.
---
+++ b/TODO
@@ -34,10 +34,8 @@ nbdkit there is no compelling reason unless the result is better than
qemu-nbd. For the majority of users it would be better if they were
directed to qemu-nbd for these use cases.
-Filters
--------
-
-It should be possible to layer filters over plugins to do things like:
+Suggestions for filters
+-----------------------
* adding artificial delays (see wdelay/rdelay options in the file
plugin)
How about a filter that intentionally disables WRITE_ZEROES and/or FUA
support, if for no other reason than for testing sane fallbacks and
comparing speed differences?
+++ b/docs/nbdkit-filter.pod
@@ -0,0 +1,501 @@
+=encoding utf8
+
+=head1 NAME
+
+nbdkit-filter - How to write nbdkit filters
+
+=head1 SYNOPSIS
+To see example filters, take a look at the source of nbdkit, in the
+C<filters> directory.
+
+Filters must be written in C and must be fully thread safe.
Should we mention that the rules on filter semantics are stricter than
for plugins, and that unlike plugins, we don't guarantee stable
cross-version API?
+=head1 CALLBACKS
+=head2 C<.get_size>
+
+ int64_t (*get_size) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ void *handle);
+
+This intercepts the plugin C<.get_size> method and can be used to read
+or modify the apparent size of the block device that the NBD client
+will see.
+
+The returned size must be E<ge> 0. If there is an error, C<.get_size>
+should call C<nbdkit_error> with an error message and return C<-1>.
The rule on non-zero size matches plugins, but I'm not sure if there is
a technical reason why we have that restriction. Down the road, when I
get the upstream NBD resize proposal finalized, I can see the ability to
create an image with size 0, then use resize to update it, as something
that would be nice to support.
+
+=head2 C<.pread>
+
+ int (*pread) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ void *handle, void *buf, uint32_t count, uint64_t offset);
Wrong signature, missing the uint32_t flags that the backend interface
has, and that I'm adding for plugins API_VERSION 2 for FUA support.
+=head2 C<.zero>
+
+ int (*zero) (struct nbdkit_next_ops *next_ops, void *nxdata,
+ void *handle, uint32_t count, uint64_t offset, int may_trim);
+
And so on (uint32_t flags instead of int may_trim).
+=head1 THREADS
+
+Because filters can be mixed and used with any plugin and thus any
+threading model supported by L<nbdkit-plugin(3)>, filters must be
+thread safe. They must be able to handle concurrent requests even on
+the same handle.
+
+Filters may have to use pthread primitives like mutexes to achieve
+this.
Would it ever make sense to allow a filter to intentionally request a
lower concurrency level than the underlying plugin? At connection time,
you'd compute the threading level as the lowest level requested by any
element of the chain; it might make it easier to write plugins that
aren't fully parallel (such filters may penalize the speed that can
otherwise be achieved by using the plugin in isolation - but again, it
may be useful for testing). That would imply adding a
backend.thread_model() callback, which needs to be exposed to filters
but not to plugins (plugins continue to use the #define at compile
time); if the filter does not define the .thread_model callback, it
defaults to fully-parallel; but if it does define the callback, it can
result in something less concurrent.
+++ b/docs/nbdkit.pod
@@ -7,7 +7,7 @@ nbdkit - A toolkit for creating NBD servers
=head1 SYNOPSIS
nbdkit [-e EXPORTNAME] [--exit-with-parent] [-f]
- [-g GROUP] [-i IPADDR]
+ [--filter=FILTER ...] [-g GROUP] [-i IPADDR]
[--newstyle] [--oldstyle] [-P PIDFILE] [-p PORT] [-r]
[--run CMD] [-s] [--selinux-label LABEL] [-t THREADS]
[--tls=off|on|require] [--tls-certificates /path/to/certificates]
@@ -119,6 +119,13 @@ not allowed with the oldstyle protocol.
I<Don't> fork into the background.
+=item B<--filter> FILTER
+
+Add a filter before the plugin. This option may be given one or more
+times to stack filters in front of the plugin. They are processed in
+the order they appear on the command line. See L</FILTERS> and
+L<nbdkit-filter(3)>.
+
Does it ever make sense to provide the same filter more than once on the
command line? Or are we stating that a given filter can only be used
once in the chain?
+struct nbdkit_next_ops {
+ int64_t (*get_size) (void *nxdata);
+
+ int (*can_write) (void *nxdata);
+ int (*can_flush) (void *nxdata);
+ int (*is_rotational) (void *nxdata);
+ int (*can_trim) (void *nxdata);
+
+ int (*pread) (void *nxdata, void *buf, uint32_t count, uint64_t offset);
+ int (*pwrite) (void *nxdata,
+ const void *buf, uint32_t count, uint64_t offset);
+ int (*flush) (void *nxdata);
+ int (*trim) (void *nxdata, uint32_t count, uint64_t offset);
+ int (*zero) (void *nxdata, uint32_t count, uint64_t offset, int may_trim);
+};
Your documentation matches your code, and did not pick up my potential
changes to add 'uint32_t flags' everywhere.
I'll reply shortly with what my rebase looks like on top of yours...
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization:
qemu.org |
libvirt.org