[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