Add a python language binding for the .zero callback, used for
implementing NBD_CMD_WRITE_ZEROES. Unlike the pwrite callback
with no return type, and unlike C where we can manipulate errno,
we need a way to distinguish between hard error (the usual
exception), and the fallback error (return false so the binding
can turn it into EOPNOTSUPP), so success requires returning true.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
plugins/python/nbdkit-python-plugin.pod | 18 +++++++++++++++++
plugins/python/python.c | 35 +++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/plugins/python/nbdkit-python-plugin.pod
b/plugins/python/nbdkit-python-plugin.pod
index 9b0f0ef..b3eb883 100644
--- a/plugins/python/nbdkit-python-plugin.pod
+++ b/plugins/python/nbdkit-python-plugin.pod
@@ -196,6 +196,24 @@ L<fdatasync(2)> or equivalent on the backing store.
The body of your C<trim> function should "punch a hole" in the
backing store.
+=item C<zero>
+
+(Optional)
+
+ def zero(h, count, offset, may_trim):
+ # return a boolean
+
+The body of your C<zero> function should ensure that C<count> bytes
+of the disk, starting at C<offset>, will read back as zero. If
+C<may_trim> is true, the operation may be optimized as a trim as long
+as subsequent reads see zeroes. Return true if the write was
+successful, and false to trigger a graceful fallback to C<pwrite>.
+
+NBD only supports whole writes, so your function should try to
+write the whole region (perhaps requiring a loop). If the write
+fails or is partial, and you do not want the fallback to C<pwrite>,
+your function should throw an exception.
+
=back
=head2 MISSING CALLBACKS
diff --git a/plugins/python/python.c b/plugins/python/python.c
index 0504715..46f50f2 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -423,6 +423,40 @@ py_trim (void *handle, uint32_t count, uint64_t offset)
}
static int
+py_zero (void *handle, uint32_t count, uint64_t offset, int may_trim)
+{
+ PyObject *obj = handle;
+ PyObject *fn;
+ PyObject *args;
+ PyObject *r;
+ int ret;
+
+ if (callback_defined ("zero", &fn)) {
+ PyErr_Clear ();
+
+ args = PyTuple_New (4);
+ Py_INCREF (obj); /* decremented by Py_DECREF (args) */
+ PyTuple_SetItem (args, 0, obj);
+ PyTuple_SetItem (args, 1, PyLong_FromUnsignedLongLong (count));
+ PyTuple_SetItem (args, 2, PyLong_FromUnsignedLongLong (offset));
+ PyTuple_SetItem (args, 3, PyBool_FromLong (may_trim));
+ r = PyObject_CallObject (fn, args);
+ Py_DECREF (fn);
+ Py_DECREF (args);
+ if (check_python_failure ("zero") == -1)
+ return -1;
+ ret = r == Py_True;
+ Py_DECREF (r);
+ if (ret)
+ return 0;
+ }
+
+ nbdkit_debug ("zero falling back to pwrite");
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+static int
py_can_write (void *handle)
{
PyObject *obj = handle;
@@ -579,6 +613,7 @@ static struct nbdkit_plugin plugin = {
.pwrite = py_pwrite,
.flush = py_flush,
.trim = py_trim,
+ .zero = py_zero,
};
NBDKIT_REGISTER_PLUGIN(plugin)
--
2.9.3