[Libguestfs] [nbdkit PATCH v2 6/6] python: Support zero callback
Richard W.M. Jones
rjones at redhat.com
Thu Jan 26 10:14:35 UTC 2017
On Wed, Jan 25, 2017 at 08:42:36PM -0600, Eric Blake wrote:
> Add a python language binding for the .zero callback, used for
> implementing NBD_CMD_WRITE_ZEROES. The caller doesn't have to
> return anything, but should use nbdkit.set_error(errno.EOPNOTSUPP)
> to get an automatic fallback to pwrite.
>
> Enhance the example to show the use of the fallback mechanism,
> and to serve as a test of nbdkit.set_error().
Also looks good.
Rich.
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
> plugins/python/example.py | 11 ++++++++
> plugins/python/nbdkit-python-plugin.pod | 20 ++++++++++++++
> plugins/python/python.c | 46 +++++++++++++++++++++++++++++++++
> 3 files changed, 77 insertions(+)
>
> diff --git a/plugins/python/example.py b/plugins/python/example.py
> index 184896e..8826eba 100644
> --- a/plugins/python/example.py
> +++ b/plugins/python/example.py
> @@ -24,6 +24,9 @@
> # ><fs> mount /dev/sda1 /
> # ><fs> [etc]
>
> +import nbdkit
> +import errno
> +
> # This is the string used to store the emulated disk (initially all
> # zero bytes). There is one disk per nbdkit instance, so if you
> # reconnect to the same server you should see the same disk. You
> @@ -56,3 +59,11 @@ def pwrite(h, buf, offset):
> global disk
> end = offset + len (buf)
> disk[offset:end] = buf
> +
> +def zero(h, count, offset, may_trim):
> + global disk
> + if may_trim:
> + disk[offset:offset+count] = bytearray(count)
> + else:
> + nbdkit.set_error(errno.EOPNOTSUPP)
> + raise Exception
> diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod
> index 8b3d08c..b78c0e2 100644
> --- a/plugins/python/nbdkit-python-plugin.pod
> +++ b/plugins/python/nbdkit-python-plugin.pod
> @@ -215,6 +215,26 @@ The body of your C<trim> function should "punch a hole" in the
> backing store. If the trim fails, your function should throw an
> exception, optionally using C<nbdkit.set_error> first.
>
> +=item C<zero>
> +
> +(Optional)
> +
> + def zero(h, count, offset, may_trim):
> + # no return value
> +
> +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.
> +
> +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, your function should throw an exception,
> +optionally using C<nbdkit.set_error> first. In particular, if
> +you would like to automatically fall back to C<pwrite> (perhaps
> +because there is nothing to optimize if C<may_trim> is false),
> +use C<nbdkit.set_error(errno.EOPNOTSUPP)>.
> +
> =back
>
> =head2 MISSING CALLBACKS
> diff --git a/plugins/python/python.c b/plugins/python/python.c
> index 4a0ff50..644ced4 100644
> --- a/plugins/python/python.c
> +++ b/plugins/python/python.c
> @@ -54,6 +54,8 @@
> static const char *script;
> static PyObject *module;
>
> +static int last_error;
> +
> static PyObject *
> set_error (PyObject *self, PyObject *args)
> {
> @@ -62,6 +64,7 @@ set_error (PyObject *self, PyObject *args)
> if (!PyArg_ParseTuple(args, "i", &err))
> return NULL;
> nbdkit_set_error (err);
> + last_error = err;
> Py_RETURN_NONE;
> }
>
> @@ -441,6 +444,48 @@ 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;
> +
> + if (callback_defined ("zero", &fn)) {
> + PyErr_Clear ();
> +
> + last_error = 0;
> + 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 (last_error == EOPNOTSUPP) {
> + /* When user requests this particular error, we want to
> + gracefully fall back, and to accomodate both a normal return
> + and an exception. */
> + nbdkit_debug ("zero requested falling back to pwrite");
> + if (r)
> + Py_DECREF (r);
> + PyErr_Clear ();
> + return -1;
> + }
> + if (check_python_failure ("zero") == -1)
> + return -1;
> + Py_DECREF (r);
> + return 0;
> + }
> +
> + nbdkit_debug ("zero missing, falling back to pwrite");
> + errno = EOPNOTSUPP;
> + return -1;
> +}
> +
> +static int
> py_can_write (void *handle)
> {
> PyObject *obj = handle;
> @@ -597,6 +642,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
>
> _______________________________________________
> Libguestfs mailing list
> Libguestfs at redhat.com
> https://www.redhat.com/mailman/listinfo/libguestfs
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
libguestfs lets you edit virtual machines. Supports shell scripting,
bindings from many languages. http://libguestfs.org
More information about the Libguestfs
mailing list