On 04/19/2018 12:39 PM, Richard W.M. Jones wrote:
On Wed, Apr 11, 2018 at 12:03:41AM -0500, Eric Blake wrote:
> +=item C<can_fua>
> +
> +(Optional)
> +
> + def can_fua(h):
> + # return a boolean
> +
> +Unlike the C counterpart, the Python callback does not need a
> +tri-state return value, because Python introspection is sufficient to
> +learn whether callbacks support FUA. Thus, this function only returns
> +a bool, which merely controls whether Forced Unit Access (FUA) support
> +is advertised to the client on a per-connection basis. If omitted or
> +this returns true, FUA support is advertised as native if any of the
> +C<pwrite>, C<zero>, or C<trim> callbacks support an optional
parameter
> +C<fua>; or as emulated if there is a C<flush> callback.
This is a true description of what the code does ... but it's pretty
complicated.
First of all, if it returns true why don't we just "believe" the
plugin? The callbacks don't have fua optargs, but so what?
The cases are:
can_fua omitted, no fua parameter or flush: NBDKIT_FUA_NONE
- client is told that FUA doesn't work
- matches the C case (useful default when no flush is present)
- python.c never sees a FUA flag
can_fua omitted, at least one fua parameter: NBDKIT_FUA_NATIVE
- client is told that FUA works
- filters are told to pass FUA down instead of emulating with flush
- this one has no C counterpart, because C can't introspect if a fua
parameter exists
- python.c can see a FUA flag, and must either pass it on to the python
code or emulate it locally
can_fua omitted, but flush exists: NBDKIT_FUA_EMULATE
- client is told that FUA works
- FUA is emulated by connections.c calling .flush
- matches the C case (useful default when flush is present)
- python.c never sees a FUA flag
can_fua present, returns false: NBDKIT_FUA_NONE
- client is told that FUA doesn't work
- matches the C case where a plugin returns NBDKIT_FUA_NONE to avoid
seeing the FUA flag, even when the plugin otherwise understands FUA
- python.c never sees a FUA flag
can_fua present, returns true, at least one fua parameter: NBDKIT_FUA_NATIVE
- client is told that FUA works
- matches the C case where a plugin returns NBDKIT_FUA_NATIVE to be
passed the FUA flag
- python.c can see FUA flag, and must either pass it on to the python
code or emulate it locally
can_fua present, returns true, no fua parameter: NBDKIT_FUA_EMULATE
- client is told that FUA works
- matches the C case where a plugin returns NBDKIT_FUA_EMULATE to
advertise support but not be passed the flag
- python.c never sees a FUA flag
So your question is why did I let the plugin just return True/False
instead of a tri-state. First, because figuring out how to make the C
code expose a tri-state value to the python code was not trivial (I
didn't have an example to copy from, and did not want to research it
when I could come up with a working solution that did not need it in the
first place). Second, because the python code has something that the C
code does not: introspection. The C code has no way to know whether a
plugin supports FUA or not, unless the plugin opts in; so the C plugin
writer MUST write .can_fua to see the FUA flag. But the python code,
introspection alone is enough to learn if the plugin supports a fua
parameter, so THAT can be treated as the required opt-in, rather than
requiring the plugin writer to supply can_fua. And it feels more
Pythonic, if you will, to make intelligent decisions in python.c glue
code without requiring the Python plugin writer to jump through as many
hoops.
I guess the counter-argument to my claim is that if we DID expose
nbdkit.FUA_NONE, .FUA_EMULATE, and .FUA_NATIVE constants to the python
code, and required a can_fua callback to return one of those three
values (similar to the C semantics) rather than True/False, then a
plugin writer can supply a can_fua that returns NATIVE and use a
'**kwargs' in pwrite to process the FUA parameter, even though
introspection would not have seen the fua parameter. It might be easier
to document, but I'm not sure if it is any easier to code, and it is
certainly a bit more of a pain that a plugin writer can't opt-in to FUA
support just by merely declaring a named fua=False parameter to pwrite,
but that they also have to write an explicit can_fua.
And would it be easier both for us and for plugin implementors
if we stuck to the C API here instead of being clever?
I'm still open to any convincing arguments that stricter matching of the
C interface is worth the effort.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization:
qemu.org |
libvirt.org