Requires patch 1 and 2 of the python series: https://www.redhat.com/archives/libguestfs/2017-January/msg00126.html This is the perl implementation along the same lines. We still haven't decided if patch 1 of the python series should change OCaml to report errno as reliable or not, but perhaps we can commit that patch as-is now and then touch things up further when we actually get set_error and zero working in OCaml. Eric Blake (3): perl: Allow use of modules perl: Expose nbdkit_set_error to perl script perl: Support zero callback plugins/perl/example.pl | 16 +++++++++ plugins/perl/nbdkit-perl-plugin.pod | 56 +++++++++++++++++++++++++++++--- plugins/perl/perl.c | 65 ++++++++++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 6 deletions(-) -- 2.9.3
Eric Blake
2017-Jan-31 04:24 UTC
[Libguestfs] [nbdkit PATCH v3 1/3] perl: Allow use of modules
Trying something as simple as 'use POSIX ();' causes the perl plugin to croak: $ ./src/nbdkit -e foo -fv plugins/perl/.libs/nbdkit-perl-plugin.so \ script=plugins/perl/example.pl nbdkit: debug: registering plugins/perl/.libs/nbdkit-perl-plugin.so nbdkit: debug: registered plugins/perl/.libs/nbdkit-perl-plugin.so (name perl) nbdkit: debug: plugins/perl/.libs/nbdkit-perl-plugin.so: load nbdkit: debug: plugins/perl/.libs/nbdkit-perl-plugin.so: config key=script, value=plugins/perl/example.pl Can't load module Fcntl, dynamic loading not available in this perl. (You may need to build a new perl executable which either supports dynamic loading or has the Fcntl module statically linked into it.) at /usr/lib64/perl5/POSIX.pm line 17. Compilation failed in require at /usr/lib64/perl5/POSIX.pm line 17. BEGIN failed--compilation aborted at /usr/lib64/perl5/POSIX.pm line 17. Compilation failed in require at plugins/perl/example.pl line 2. BEGIN failed--compilation aborted at plugins/perl/example.pl line 2. nbdkit: error: plugins/perl/example.pl: one of the required callbacks 'open', 'get_size' or 'pread' is not defined by this Perl script. nbdkit requires these callbacks. Per 'perldoc perlembed', the solution is simple: statically incorporate the DynaLoader module into the embedded interpreter. Signed-off-by: Eric Blake <eblake@redhat.com> --- plugins/perl/perl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/perl/perl.c b/plugins/perl/perl.c index ec82ecb..64d9d17 100644 --- a/plugins/perl/perl.c +++ b/plugins/perl/perl.c @@ -140,6 +140,16 @@ check_perl_failure (void) return 0; } + +EXTERN_C void boot_DynaLoader (pTHX_ CV *cv); + +static void +xs_init(pTHX) +{ + char *file = __FILE__; + newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); +} + static int perl_config (const char *key, const char *value) { @@ -158,7 +168,7 @@ perl_config (const char *key, const char *value) /* Load the Perl script. */ argv[1] = (char *) script; - if (perl_parse (my_perl, NULL, argc, argv, NULL) == -1) { + if (perl_parse (my_perl, xs_init, argc, argv, NULL) == -1) { nbdkit_error ("%s: error parsing this script", script); return -1; } -- 2.9.3
Eric Blake
2017-Jan-31 04:24 UTC
[Libguestfs] [nbdkit PATCH v3 2/3] perl: Expose nbdkit_set_error to perl script
In addition to calling Perl functions from C, we want to make script writing easier by exposing C functions to perl. For now, just wrap nbdkit_set_error(), as that will be needed for an optimal implementation of a zero() callback. I intentionally decided to go with a hand-rolled XSUB statically compiled in, rather than a full-blown dynamically loadable Nbdkit module compatible with CPAN, in part because the machinery to hook in a call to xsubcc would be lots of code, and in part because we already document that the user's script is not executable as a stand-alone perl script anyways. Perl is very picky: I found that including <XSUB.h> after <EXTERN.h>, even with no other changes in the file, caused the embedded interpreter to segfault during perl_open(); but listing <XSUB.h> first worked for me (tested on Fedora 25). Signed-off-by: Eric Blake <eblake@redhat.com> --- plugins/perl/nbdkit-perl-plugin.pod | 30 +++++++++++++++++++++++++----- plugins/perl/perl.c | 10 ++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/plugins/perl/nbdkit-perl-plugin.pod b/plugins/perl/nbdkit-perl-plugin.pod index c02f8ef..82766d0 100644 --- a/plugins/perl/nbdkit-perl-plugin.pod +++ b/plugins/perl/nbdkit-perl-plugin.pod @@ -60,11 +60,26 @@ and does I<not> need to be executable. In fact it's a good idea I<not> to do that, because running the plugin directly as a Perl script won't work. +=head2 METHODS + +Your script has access to the following methods in the C<Nbdkit> +package (in fact, attempting to C<use Nbdkit;> will fail, the methods +are already available): + + Nbdkit::set_error(I<$err>) + +Record C<$err> as the reason you are about to throw an exception. C<$err> +should correspond to usual errno values, where it may help to +C<use POSIX();>. + =head2 EXCEPTIONS Instead of returning error codes as in C, Perl callbacks should indicate problems by throwing Perl exceptions (ie. C<die>, C<croak> etc). The Perl error message is captured and printed by nbdkit. +Remember to use C<Nbdkit::set_error> if you need to control which +error is sent back to the client; if omitted, the client will see an +error of C<EIO>. =head2 32 vs 64 BIT @@ -216,7 +231,8 @@ disk starting at C<$offset>. NBD only supports whole reads, so your function should try to read the whole region (perhaps requiring a loop). If the read fails or is -partial, your function should C<die>. +partial, your function should C<die>, optionally using +C<Nbdkit::set_error> first. =item C<pwrite> @@ -237,7 +253,8 @@ C<$offset>. 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 C<die>. +partial, your function should C<die>, optionally using +C<Nbdkit::set_error> first. =item C<flush> @@ -252,7 +269,8 @@ partial, your function should C<die>. The body of your C<flush> function should do a L<sync(2)> or L<fdatasync(2)> or equivalent on the backing store. -If there is an error, the function should call C<die>. +If there is an error, the function should call C<die>, optionally using +C<Nbdkit::set_error> first. =item C<trim> @@ -269,7 +287,8 @@ If there is an error, the function should call C<die>. The body of your C<trim> function should "punch a hole" in the backing store. -If there is an error, the function should call C<die>. +If there is an error, the function should call C<die>, optionally using +C<Nbdkit::set_error> first. =back @@ -285,7 +304,8 @@ and C<END> constructs. =item Missing: C<errno_is_reliable> This is not needed because the process of gluing Perl code into C cannot -reliably use C<errno>. +reliably use C<errno>. Instead, call C<Nbdkit::set_error> when reporting +a failure. =item Missing: C<name>, C<version>, C<longname>, C<description>, C<config_help> diff --git a/plugins/perl/perl.c b/plugins/perl/perl.c index 64d9d17..9fb189c 100644 --- a/plugins/perl/perl.c +++ b/plugins/perl/perl.c @@ -43,6 +43,7 @@ #include <assert.h> #include <errno.h> +#include <XSUB.h> #include <EXTERN.h> #include <perl.h> @@ -140,6 +141,14 @@ check_perl_failure (void) return 0; } +XS(set_error) +{ + dXSARGS; + /* Is it worth adding error checking for bad arguments? */ + if (items >= 1) + nbdkit_set_error (SvIV (ST (0))); + XSRETURN_EMPTY; +} EXTERN_C void boot_DynaLoader (pTHX_ CV *cv); @@ -148,6 +157,7 @@ xs_init(pTHX) { char *file = __FILE__; newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); + newXS("Nbdkit::set_error", set_error, file); } static int -- 2.9.3
Eric Blake
2017-Jan-31 04:24 UTC
[Libguestfs] [nbdkit PATCH v3 3/3] perl: Support zero callback
Add a perl 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(POSIX::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@redhat.com> --- plugins/perl/example.pl | 16 +++++++++++++ plugins/perl/nbdkit-perl-plugin.pod | 26 +++++++++++++++++++++ plugins/perl/perl.c | 45 ++++++++++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/plugins/perl/example.pl b/plugins/perl/example.pl index fcdac33..7ea24ff 100644 --- a/plugins/perl/example.pl +++ b/plugins/perl/example.pl @@ -1,4 +1,5 @@ use strict; +use POSIX (); # Example Perl plugin. # @@ -83,3 +84,18 @@ sub pwrite substr ($disk, $offset, $count) = $buf; } + +sub zero +{ + my $h = shift; + my $count = shift; + my $offset = shift; + my $may_trim = shift; + + if ($may_trim) { + substr ($disk, $offset, $count) = "\0" x $count; + } else { + Nbdkit::set_error(POSIX::EOPNOTSUPP); + die "fall back to pwrite"; + } +} diff --git a/plugins/perl/nbdkit-perl-plugin.pod b/plugins/perl/nbdkit-perl-plugin.pod index 82766d0..920bdef 100644 --- a/plugins/perl/nbdkit-perl-plugin.pod +++ b/plugins/perl/nbdkit-perl-plugin.pod @@ -290,6 +290,32 @@ store. If there is an error, the function should call C<die>, optionally using C<Nbdkit::set_error> first. +=item C<zero> + +(Optional) + + sub zero + { + my $handle = shift; + my $count = shift; + my $offset = shift; + my $may_trim = shift; + # 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 C<die>, 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(POSIX::EOPNOTSUPP)>. + =back =head2 MISSING CALLBACKS diff --git a/plugins/perl/perl.c b/plugins/perl/perl.c index 9fb189c..52694d9 100644 --- a/plugins/perl/perl.c +++ b/plugins/perl/perl.c @@ -141,12 +141,15 @@ check_perl_failure (void) return 0; } +static int last_error; + XS(set_error) { dXSARGS; /* Is it worth adding error checking for bad arguments? */ if (items >= 1) - nbdkit_set_error (SvIV (ST (0))); + last_error = SvIV (ST (0)); + nbdkit_set_error (last_error); XSRETURN_EMPTY; } @@ -514,6 +517,45 @@ perl_can_trim (void *handle) } static int +perl_zero (void *handle, uint32_t count, uint64_t offset, int may_trim) +{ + dSP; + + if (callback_defined ("zero")) { + last_error = 0; + ENTER; + SAVETMPS; + PUSHMARK (SP); + XPUSHs (handle); + XPUSHs (sv_2mortal (newSViv (count))); + XPUSHs (sv_2mortal (newSViv (offset))); + XPUSHs (sv_2mortal (newSViv (may_trim))); + PUTBACK; + call_pv ("zero", G_EVAL|G_SCALAR); + SPAGAIN; + PUTBACK; + FREETMPS; + LEAVE; + + 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"); + return -1; + } + if (check_perl_failure () == -1) + return -1; + + return 0; + } + + nbdkit_debug ("zero falling back to pwrite"); + nbdkit_set_error (EOPNOTSUPP); + return -1; +} + +static int perl_is_rotational (void *handle) { dSP; @@ -644,6 +686,7 @@ static struct nbdkit_plugin plugin = { .pwrite = perl_pwrite, .flush = perl_flush, .trim = perl_trim, + .zero = perl_zero, .errno_is_reliable = perl_errno_is_reliable, }; -- 2.9.3
Richard W.M. Jones
2017-Jan-31 17:35 UTC
Re: [Libguestfs] [nbdkit PATCH v3 0/3] bind .zero to Perl
On Mon, Jan 30, 2017 at 10:24:44PM -0600, Eric Blake wrote:> Requires patch 1 and 2 of the python series: > https://www.redhat.com/archives/libguestfs/2017-January/msg00126.html > > This is the perl implementation along the same lines. > > We still haven't decided if patch 1 of the python series should > change OCaml to report errno as reliable or not, but perhaps we > can commit that patch as-is now and then touch things up further > when we actually get set_error and zero working in OCaml.Agreed - I pushed the previous series and this one. Thanks, Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://people.redhat.com/~rjones/virt-df/
Maybe Matching Threads
- [nbdkit PATCH 0/2] bind .zero to more languages
- [nbdkit PATCH 0/2] Ruby bindings for .zero
- [nbdkit PATCH v2 0/6] bind .zero to Python
- [PATCH nbdkit 0/8] Implement nbdkit API v2 for Python plugins.
- [PATCH nbdkit v2 00/10] Implement nbdkit API v2 for Python plugins.