On Tue, Jan 16, 2018 at 10:52:12AM -0600, Eric Blake wrote:
/* This header also defines some useful functions like nbdkit_debug * and nbdkit_parse_size which are appropriate for filters to use.
Too much copy-and-paste?
*/ #include <nbdkit-plugin.h>
I guess you get those useful functions by inclusion, rather than by direct definition.
Yes, that comment describes why we include the header. I guess I could delete the comment if it's confusing.
struct nbdkit_next { 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); };
While we want to support plugins that use the old API, should we declare that filters only use the new API? Or does my work to add FUA passthrough need to be careful on how a filter does the work; particularly if mixing a filter without FUA knowledge with a backend that can do it? I guess as long as we stabilize all of the filter and FUA code into the same nbdkit release, it's okay if we tweak the filter definition slightly in the next few patches.
I was hoping we could combine the FUA patches (or rather, push them all together) so that in the final version the filter .pwrite definition above would contain a FUA/flags parameter.
struct nbdkit_filter { /* Do not set these fields directly; use NBDKIT_REGISTER_FILTER. * They exist so that we can support filters compiled against * one version of the header with a runtime compiled against a * different version with more (or fewer) fields. */ uint64_t _struct_size; int _api_version;
/* New fields will only be added at the end of the struct. */ const char *name; const char *longname; const char *version; const char *description;
void (*load) (void); void (*unload) (void);
int (*config) (nbdkit_next_config *next, void *nxdata, const char *key, const char *value); int (*config_complete) (nbdkit_next_config_complete *next, void *nxdata); const char *config_help;
void * (*open) (int readonly);
Is it conceivable that opening a filter may want to read data from the underlying backend?
One case where this could make sense would be with a "partition" filter (which needs to read the partition table early), and I did consider that. However I believe -- although haven't proven by implementation -- that a partition filter can still be written, it just needs to read the partition table once in one of the other functions, whichever is called first, with a bit of mutex-ing. The advantage to make open not be a passthrough is it somewhat simplifies the implementation, otherwise it has to deal with the new handle returned from the next element in the chain.
Do we have guarantees on lifecycle ordering across the chain (if the command line uses filter1 filter2 plugin, we open plugin first, filter2 second, filter1 last; then close in reverse order)?
At the moment we open plugin, then filter2, then filter1, and close them in the same order. I was hoping not to define the order, since I don't think it matters(?) It could also be different for load/unload vs open/close.
Different filters can be stacked:
NBD ┌─────────┐ ┌─────────┐ ┌────────┐ client ───▶│ filter1 │───▶│ filter2 │── ─ ─ ──▶│ plugin │ request └─────────┘ └─────────┘ └────────┘
Does conversion from POD to other formats preserve proper formatting of this ASCII-art?
We've used utf-8 in POD in libguestfs since forever.
=head2 C<.name>
const char *name;
This field (a string) is required, and B<must> contain only ASCII alphanumeric characters and be unique amongst all filters.
Do filters and plugins share the same namespace, or can you have a filter whose name matches a plugin?
Different namespaces because they go into different directories (plugindir vs filterdir).
=head2 C<.open>
void * (*open) (int readonly);
This is called when a new client connection is opened and can be used to allocate any per-connection data structures needed by the filter. The handle (which is not the same as the plugin handle) is passed back to other filter callbacks and could be freed in the C<.close> callback.
Note that the handle is completely opaque to nbdkit, but it must not be NULL.
If there is an error, C<.open> should call C<nbdkit_error> with an error message and return C<NULL>.
I guess if you wanted to be truly opaque and support NULL, you'd have to have something like:
int (*open) (int readonly, void **result)
which populates *result when returning 0, and returns -1 on failure. But I'm also fine with your choice of stating that the opaque data has to be anything other than NULL.
It's consistent with how plugins work now. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://people.redhat.com/~rjones/virt-df/