filters: Cache and change semantics of can_zero
The tri-state return pattern has proved valuable in other cases where
a backend wants to opt in or out of nbdkit fallbacks. Using a
tri-state return pattern at the backend level unifies the different
semantics between plugin and filter .can_zero return values, and makes
it possible for plugins to use cached results of .can_zero rather than
calling into the plugin on each .zero request. As in other recent
patches, it is easy to write an sh script that demonstrates a
resulting speedup from the caching. All filters are in-tree and do
not have a promise of API compatability, so it's easy to update all of
them (the log filter is okay as-is, the nozero filter is easy to
update, and no other filter overrides .can_zero). But for backwards
compatibility reasons, we cannot change the semantics of the plugin's
return value for .can_zero while remaining at API version 2; so that
remains a bool, and merely selects between EMULATE or NATIVE at the
backend level.
$ cat script
case "$1" in
get_size) echo 1m;;
can_zero) sleep 1;;
can_write | zero) ;;
*) exit 2 ;;
esac
Pre-patch:
$ /bin/time -f %e ./nbdkit -U - sh script \
--run 'qemu-io -f raw -c "w -z 0 512" -c "w -z 512 512" $uri >/dev/null'
3.10
Post-patch:
$ /bin/time -f %e ./nbdkit -U - sh script \
--run 'qemu-io -f raw -c "w -z 0 512" -c "w -z 512 512" $uri >/dev/null'
1.08
Signed-off-by: Eric Blake <eblake@redhat.com>