[Libguestfs] [nbdkit PATCH 2/2] ruby: Support zero callback

Eric Blake eblake at redhat.com
Thu Feb 2 19:45:38 UTC 2017


Add a ruby 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().

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 plugins/ruby/example.rb             | 11 +++++++++++
 plugins/ruby/nbdkit-ruby-plugin.pod | 20 ++++++++++++++++++++
 plugins/ruby/ruby.c                 | 28 ++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/plugins/ruby/example.rb b/plugins/ruby/example.rb
index 3238031..9a88f60 100644
--- a/plugins/ruby/example.rb
+++ b/plugins/ruby/example.rb
@@ -24,6 +24,8 @@
 #   ><fs> mount /dev/sda1 /
 #   ><fs> [etc]

+include Nbdkit
+
 $disk = "\0" * (1024 * 1024)

 def config(key, value)
@@ -50,3 +52,12 @@ def pwrite(h, buf, offset)
   # Hmm, is this using bytes or chars? XXX
   $disk[offset, buf.length] = buf
 end
+
+def zero(h, count, offset, may_trim)
+  if may_trim
+    $disk[offset, count] = "\0" * count
+  else
+    set_error(Errno::EOPNOTSUPP)
+    raise "falling back to pwrite"
+  end
+end
diff --git a/plugins/ruby/nbdkit-ruby-plugin.pod b/plugins/ruby/nbdkit-ruby-plugin.pod
index 0131574..6cf8e97 100644
--- a/plugins/ruby/nbdkit-ruby-plugin.pod
+++ b/plugins/ruby/nbdkit-ruby-plugin.pod
@@ -220,6 +220,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/ruby/ruby.c b/plugins/ruby/ruby.c
index fc2e8ad..33d7968 100644
--- a/plugins/ruby/ruby.c
+++ b/plugins/ruby/ruby.c
@@ -36,12 +36,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <errno.h>

 #include <nbdkit-plugin.h>

 #include <ruby.h>

 static VALUE nbdkit_module = Qnil;
+static int last_error;

 static VALUE
 set_error(VALUE self, VALUE arg)
@@ -58,6 +60,7 @@ set_error(VALUE self, VALUE arg)
   } else {
     err = NUM2INT(arg);
   }
+  last_error = err;
   nbdkit_set_error(err);
   return Qnil;
 }
@@ -367,6 +370,30 @@ plugin_rb_trim (void *handle, uint32_t count, uint64_t offset)
 }

 static int
+plugin_rb_zero (void *handle, uint32_t count, uint64_t offset, int may_trim)
+{
+  volatile VALUE argv[4];
+
+  argv[0] = (VALUE) handle;
+  argv[1] = ULL2NUM (count);
+  argv[2] = ULL2NUM (offset);
+  argv[3] = may_trim ? Qtrue : Qfalse;
+  exception_happened = 0;
+  last_error = 0;
+  (void) funcall2 (Qnil, rb_intern ("zero"), 4, argv);
+  if (last_error == EOPNOTSUPP ||
+      exception_happened == EXCEPTION_NO_METHOD_ERROR) {
+    nbdkit_debug ("zero falling back to pwrite");
+    nbdkit_set_error (EOPNOTSUPP);
+    return -1;
+  }
+  else if (exception_happened == EXCEPTION_OTHER)
+    return -1;
+
+  return 0;
+}
+
+static int
 plugin_rb_can_write (void *handle)
 {
   volatile VALUE argv[1];
@@ -483,6 +510,7 @@ static struct nbdkit_plugin plugin = {
   .pwrite            = plugin_rb_pwrite,
   .flush             = plugin_rb_flush,
   .trim              = plugin_rb_trim,
+  .zero              = plugin_rb_zero,

   .errno_is_reliable = plugin_rb_errno_is_reliable,
 };
-- 
2.9.3




More information about the Libguestfs mailing list