[Libguestfs] [nbdkit PATCH 5/5] python: Add can_fast_zero support

Eric Blake eblake at redhat.com
Mon Nov 25 22:48:38 UTC 2019


Add the can_fast_zero callback, and yet another bool keyword parameter
to the zero callback.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 plugins/python/nbdkit-python-plugin.pod | 35 ++++++++++++++-----
 plugins/python/python.c                 | 45 ++++++++++++++++++++++++-
 plugins/python/example.py               |  2 +-
 3 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod
index 6940f4d9..7b0051af 100644
--- a/plugins/python/nbdkit-python-plugin.pod
+++ b/plugins/python/nbdkit-python-plugin.pod
@@ -215,6 +215,18 @@ contents will be garbage collected.
  def can_zero(h):
    # return a boolean

+
+=item C<can_fast_zero>
+
+(Optional)
+
+ def can_fast_zero(h):
+   # return a boolean
+
+Unlike the C counterpart, this can often be omitted: the Python
+callback uses Python introspection to default to true if the C<zero>
+callback supports an optional parameter C<fast>.
+
 =item C<can_fua>

 (Optional)
@@ -313,7 +325,7 @@ request for FUA by calling C<flush>.

 (Optional)

- def zero(h, count, offset, may_trim=False, fua=False):
+ def zero(h, count, offset, may_trim=False, fua=False, fast=False):
    # no return value

 The body of your C<zero> function should ensure that C<count> bytes
@@ -331,13 +343,19 @@ return success until the write has landed in persistent storage.  If
 it is absent but C<can_fua> returned True, then nbdkit emulates a
 client request for FUA by calling C<flush>.

-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)>.
+Your function may support an optional C<fast> parameter; if it is
+present and the caller sets it to True, then your callback must return
+a failure if it is not any faster than a corresponding C<pwrite>. If
+it is absent but C<can_fast_zero> returned True, then nbdkit treats
+all fast zero requests as failures.
+
+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) or declare that fast zero is not
+possible, use C<nbdkit.set_error(errno.EOPNOTSUPP)>.

 =back

@@ -362,7 +380,6 @@ C<description>,
 C<config_help>,
 C<magic_config_key>,
 C<can_cache>,
-C<can_fast_zero>,
 C<can_extents>,
 C<can_multi_conn>,
 C<cache>,
diff --git a/plugins/python/python.c b/plugins/python/python.c
index 677420bd..fee339f4 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -699,11 +699,14 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
   PyObject *r;
   int may_trim = (flags & NBDKIT_FLAG_MAY_TRIM) != 0;
   int fua = (flags & NBDKIT_FLAG_FUA) != 0;
+  int fast = (flags & NBDKIT_FLAG_FAST_ZERO) != 0;
   int need_flush = fua && !zero_has_fua;

-  assert (!(flags & ~(NBDKIT_FLAG_MAY_TRIM | NBDKIT_FLAG_FUA)));
+  assert (!(flags & ~(NBDKIT_FLAG_MAY_TRIM | NBDKIT_FLAG_FUA |
+                      NBDKIT_FLAG_FAST_ZERO)));
   if (callback_defined ("zero", &fn)) {
     static int zero_may_trim = -1;
+    static int zero_fast = -1;

     if (zero_may_trim < 0)
       zero_may_trim = callback_has_parameter (fn, "may_trim");
@@ -711,6 +714,18 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
       check_python_failure ("zero");
       return -1;
     }
+    if (zero_fast < 0)
+      zero_fast = callback_has_parameter (fn, "fast");
+    if (zero_fast < 0) {
+      check_python_failure ("zero");
+      return -1;
+    }
+
+    if (fast && zero_fast != 1) {
+      nbdkit_debug ("zero lacks fast support, failing fast request");
+      nbdkit_set_error (EOPNOTSUPP);
+      return -1;
+    }

     PyErr_Clear ();

@@ -746,6 +761,15 @@ py_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
       Py_DECREF (fn);
       return -1;
     }
+    if (zero_fast &&
+        PyDict_SetItemString (kwargs, "fast",
+                              fast ? Py_True : Py_False) == -1) {
+      check_python_failure ("zero");
+      Py_DECREF (kwargs);
+      Py_DECREF (args);
+      Py_DECREF (fn);
+      return -1;
+    }
     r = PyObject_Call (fn, args, kwargs);
     Py_DECREF (fn);
     Py_DECREF (args);
@@ -831,6 +855,24 @@ py_can_zero (void *handle)
   return boolean_callback (handle, "can_zero", "zero", 0);
 }

+static int
+py_can_fast_zero (void *handle)
+{
+  int r = boolean_callback (handle, "can_fast_zero", NULL, 2);
+  PyObject *fn;
+
+  if (r == 2) {
+    /* can_fast_zero was missing, but we still want to default to true
+     * if the zero callback is missing or supports optional fast argument.
+     */
+    if (callback_defined ("zero", &fn))
+      r = callback_has_parameter (fn, "fast");
+    else
+      r = 1;
+  }
+  return r;
+}
+
 static int
 py_can_fua (void *handle)
 {
@@ -876,6 +918,7 @@ static struct nbdkit_plugin plugin = {
   .can_flush         = py_can_flush,
   .can_trim          = py_can_trim,
   .can_zero          = py_can_zero,
+  .can_fast_zero     = py_can_fast_zero,
   .can_fua           = py_can_fua,

   .pread             = py_pread,
diff --git a/plugins/python/example.py b/plugins/python/example.py
index ca7c2661..b1a9fed1 100644
--- a/plugins/python/example.py
+++ b/plugins/python/example.py
@@ -67,7 +67,7 @@ def pwrite(h, buf, offset, fua=False):
     disk[offset:end] = buf


-def zero(h, count, offset, may_trim=False, fua=False):
+def zero(h, count, offset, may_trim=False, fua=False, fast=False):
     global disk
     if may_trim:
         disk[offset:offset+count] = bytearray(count)
-- 
2.21.0




More information about the Libguestfs mailing list